summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-05-12 17:38:41 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-05-12 17:38:41 -0400
commitc6446e178bf8f4a53511e5ae1e0f3feeb921197c (patch)
treef13384c1d4f6f085d0856c253996c7b9000bcb7b
parentrefactor (diff)
downloadlazysphere-c6446e178bf8f4a53511e5ae1e0f3feeb921197c.tar.gz
lazysphere-c6446e178bf8f4a53511e5ae1e0f3feeb921197c.tar.bz2
lazysphere-c6446e178bf8f4a53511e5ae1e0f3feeb921197c.zip
chown, chmod
-rw-r--r--command/chmod.c232
-rw-r--r--command/chown.c187
-rw-r--r--command/ls.c45
-rw-r--r--lib/convert.c2
-rw-r--r--readme.md2
-rw-r--r--src/command.h2
-rw-r--r--src/main.c7
7 files changed, 457 insertions, 20 deletions
diff --git a/command/chmod.c b/command/chmod.c
new file mode 100644
index 0000000..9e2972e
--- /dev/null
+++ b/command/chmod.c
@@ -0,0 +1,232 @@
+#include "args.h"
+#include "command.h"
+#include "lslib.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+
+enum method {
+ ADD,
+ SUB,
+ SET
+};
+
+static struct {
+ bool recurse;
+ bool list_changed;
+ bool verbose;
+ bool quiet;
+ enum method method;
+ mode_t mode;
+} flags;
+
+static void help (void) {
+ printf("Usage: chmod [-Rcvf] MODE[,MODE]... FILE...\n\n");
+ printf("MODE is octal number (bit pattern sstrwxrwxrwx) or [ugoa]{+|-|=}[rwxXst]\n\n");
+ printf("\t-R\tRecurse\n");
+ printf("\t-c\tList changed files\n");
+ printf("\t-v\tVerbose\n");
+ printf("\t-f\tHide errors\n");
+}
+
+static int short_arg(char c, char* next) {
+ UNUSED(next);
+ switch (c) {
+ case 'R':
+ flags.recurse = true;
+ break;
+ case 'c':
+ flags.list_changed = true;
+ break;
+ case 'v':
+ flags.verbose = true;
+ break;
+ case 'f':
+ flags.quiet = true;
+ break;
+ case 'r':
+ case 'w':
+ case 'x':
+ case 'X':
+ case 't':
+ case 's':
+ return ARG_IGNORE;
+ default:
+ return ARG_INVALID;
+ }
+ return ARG_UNUSED;
+}
+
+static void chmod_file(char* path) {
+ int save;
+ struct stat s;
+ DIR* d;
+ struct dirent* file;
+ mode_t mode = 0;
+
+ save = push_path_buffer(path);
+
+ if (lstat(get_path_buffer(), &s) < 0) {
+ if (!flags.quiet) {
+ error_s("cannot stat '%s': %s", get_path_buffer(), strerror(errno));
+ }
+ pop_path_buffer(save);
+ return;
+ }
+
+ if (flags.method == SET) {
+ mode = flags.mode;;
+ } else if (flags.method == ADD) {
+ mode = s.st_mode | flags.mode;
+ } else if (flags.method == SUB) {
+ mode = s.st_mode & ~flags.mode;
+ }
+
+ if (chmod(get_path_buffer(), mode) < 0) {
+ if (!flags.quiet) {
+ error_s("cannot chmod '%s': %s", get_path_buffer(), strerror(errno));
+ }
+ pop_path_buffer(save);
+ return;
+ } else if (flags.list_changed) {
+ printf("chmod: changed '%s' to %o\n", get_path_buffer(), mode);
+ }
+
+ if (!flags.recurse) {
+ pop_path_buffer(save);
+ return;
+ }
+
+ if (!S_ISDIR(s.st_mode)) {
+ pop_path_buffer(save);
+ return;
+ }
+
+ d = opendir(get_path_buffer());
+ if (d == NULL) {
+ if (!flags.quiet) {
+ error_s("cannot open dir '%s': %s", get_path_buffer(), strerror(errno));
+ }
+ pop_path_buffer(save);
+ return;
+ }
+
+ while ((file = readdir(d)) != NULL) {
+ if (is_dot_dir(file->d_name)) continue;
+ chmod_file(file->d_name);
+ }
+
+ closedir(d);
+ pop_path_buffer(save);
+}
+
+static mode_t parse_mode(char** input) {
+ mode_t mode = 00000;
+ char* str = *input;
+
+ switch (*str) {
+ case '=':
+ flags.method = SET;
+ break;
+ case '+':
+ flags.method = ADD;
+ break;
+ case '-':
+ flags.method = SUB;
+ break;
+ default:
+ flags.method = SET;
+ mode = get_mode(str);
+ goto end;
+ }
+
+next:
+ str++;
+ switch (*str) {
+ case 'r':
+ mode |= 00444;
+ goto next;
+ case 'w':
+ mode |= 00200;
+ goto next;
+ case 'x':
+ mode |= 00111;
+ goto next;
+ case 'X':
+ mode |= 00000;
+ goto next;
+ case 's':
+ mode |= 06000;
+ goto next;
+ case 't':
+ mode |= 01000;
+ goto next;
+ case '\0':
+ case ',':
+ goto end;
+ default:
+ error("invalid option: %c", *str);
+ }
+
+end:
+ while (true) {
+
+ if (*str == '\0') {
+ *input = NULL;
+ break;
+ }
+
+ if (*str == ',') {
+ *input = ++str;
+ break;
+ }
+
+ str++;
+ }
+
+ return mode;
+}
+
+COMMAND(chmod_cmd) {
+
+ int start, i;
+
+ flags.recurse = false;
+ flags.list_changed = false;
+ flags.verbose = false;
+ flags.quiet = false;
+ flags.mode = 0;
+
+ start = parse_args(argc, argv, help, short_arg, NULL);
+
+ if (argc - start < 1) {
+ if (!flags.quiet) {
+ error("no mode provided");
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+
+ do {
+ flags.mode |= parse_mode(&argv[start]);
+ } while (argv[start] != NULL);
+
+ if (argc - start < 2) {
+ if (!flags.quiet) {
+ error("no files passed");
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (i = start + 1; i < argc; i++) {
+ chmod_file(argv[i]);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/command/chown.c b/command/chown.c
new file mode 100644
index 0000000..164e3b4
--- /dev/null
+++ b/command/chown.c
@@ -0,0 +1,187 @@
+#include "command.h"
+#include "lslib.h"
+
+#include <dirent.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+static struct {
+ bool recurse;
+ bool list_changed;
+ bool verbose;
+ bool quiet;
+ uid_t uid;
+ gid_t gid;
+} flags;
+
+static void help (void) {
+ printf("Usage: chown [-Rcvf]... USER[:[GRP]] FILE...\n\n");
+ printf("Change the owner and/or group of FILEs to USER and/or GRP\n\n");
+ printf("\t-R\tRecurse\n");
+ printf("\t-c\tList changed files\n");
+ printf("\t-v\tVerbose\n");
+ printf("\t-f\tHide errors\n");
+}
+
+static int short_arg(char c, char* next) {
+ UNUSED(next);
+ switch (c) {
+ case 'R':
+ flags.recurse = true;
+ break;
+ case 'c':
+ flags.list_changed = true;
+ break;
+ case 'v':
+ flags.verbose = true;
+ break;
+ case 'f':
+ flags.quiet = true;
+ break;
+ default:
+ return ARG_INVALID;
+ }
+ return ARG_UNUSED;
+}
+
+static void chown_file(char* path) {
+ int save;
+ struct stat s;
+ DIR* d;
+ struct dirent* file;
+
+ save = push_path_buffer(path);
+
+ if (chown(get_path_buffer(), flags.uid, flags.gid) < 0) {
+ if (!flags.quiet) {
+ error_s("cannot chown '%s': %s", get_path_buffer(), strerror(errno));
+ }
+ pop_path_buffer(save);
+ return;
+ } else if (flags.list_changed) {
+ printf("chown: changed '%s' to %u:%u\n", get_path_buffer(), flags.uid, flags.gid);
+ }
+
+ if (!flags.recurse) {
+ pop_path_buffer(save);
+ return;
+ }
+
+ if (lstat(get_path_buffer(), &s) < 0) {
+ if (!flags.quiet) {
+ error_s("cannot stat '%s': %s", get_path_buffer(), strerror(errno));
+ }
+ pop_path_buffer(save);
+ return;
+ }
+
+ if (!S_ISDIR(s.st_mode)) {
+ pop_path_buffer(save);
+ return;
+ }
+
+ d = opendir(get_path_buffer());
+ if (d == NULL) {
+ if (!flags.quiet) {
+ error_s("cannot open dir '%s': %s", get_path_buffer(), strerror(errno));
+ }
+ pop_path_buffer(save);
+ return;
+ }
+
+ while ((file = readdir(d)) != NULL) {
+ if (is_dot_dir(file->d_name)) continue;
+ chown_file(file->d_name);
+ }
+
+ closedir(d);
+ pop_path_buffer(save);
+}
+
+static void parse_ownership(char* str) {
+ char* user = str;
+ char* group = NULL;
+ char* end = NULL;
+ unsigned long i;
+ struct passwd* p = NULL;
+ struct group* g = NULL;
+
+ for (i = 0; i < strlen(str); i++) {
+ if (str[i] == ':') {
+ str[i] = '\0';
+ group = &str[i] + 1;
+ break;
+ }
+ }
+
+ flags.uid = strtol(user, &end, 10);
+ if (end != user) goto group;
+
+ if ((p = getpwnam(user)) == NULL) {
+ error("invalid user '%s'", user);
+ } else {
+ flags.uid = p->pw_uid;
+ }
+
+group:
+
+ if (group == NULL) {
+ if (p == NULL) {
+ flags.gid = flags.uid;
+ } else {
+ flags.gid = p->pw_gid;
+ }
+ return;
+ }
+
+ flags.gid = strtol(group, &end, 10);
+ if (end != group) return;
+
+ if ((g = getgrnam(group)) == NULL) {
+ error("invalid group '%s'", group);
+ } else {
+ flags.gid = g->gr_gid;
+ }
+}
+
+COMMAND(chown_cmd) {
+
+ int start, i;
+
+ flags.recurse = false;
+ flags.list_changed = false;
+ flags.verbose = false;
+ flags.quiet = false;
+
+ start = parse_args(argc, argv, help, short_arg, NULL);
+
+ if (argc - start < 1) {
+ if (!flags.quiet) {
+ error("no onwership passed");
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+
+ parse_ownership(argv[start]);
+
+ if (argc - start < 1) {
+ if (!flags.quiet) {
+ error("no files passed");
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (i = start + 1; i < argc; i++) {
+ chown_file(argv[i]);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/command/ls.c b/command/ls.c
index 8ed796f..bbfedfb 100644
--- a/command/ls.c
+++ b/command/ls.c
@@ -133,35 +133,46 @@ static bool get_file_info(const char* file_name, struct FileInfo* info) {
info->mode[1] = (s.st_mode & S_IRUSR) ? 'r' : '-';
info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-';
- if (s.st_mode & S_IXUSR) {
- if (s.st_mode & S_ISUID) {
- info->mode[3] = 's';
- info->set_uid = true;
- } else {
- info->mode[3] = 'x';
- }
- if (!info->exec) info->exec = s.st_uid == uid;
+ if (s.st_mode & S_IXUSR && s.st_mode & S_ISUID) {
+ info->mode[3] = 's';
+ info->set_uid = true;
+ if (!info->exec) info->exec = info->usr->pw_uid == uid;
+ } else if (s.st_mode & S_ISUID) {
+ info->mode[3] = 'S';
+ info->set_uid = true;
+ } else if (s.st_mode & S_IXUSR) {
+ info->mode[3] = 'x';
+ if (!info->exec) info->exec = info->usr->pw_uid == uid;
} else {
info->mode[3] = '-';
}
info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-';
info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-';
- if (s.st_mode & S_IXGRP) {
- if (s.st_mode & S_ISGID) {
- info->mode[6] = 's';
- info->set_gid = true;
- } else {
- info->mode[6] = 'x';
- }
- if (!info->exec) info->exec = s.st_gid == gid;
+ if (s.st_mode & S_IXGRP && s.st_mode & S_ISGID) {
+ info->mode[6] = 's';
+ info->set_gid = true;
+ if (!info->exec) info->exec = info->grp->gr_gid == gid;
+ } else if (s.st_mode & S_ISGID) {
+ info->mode[6] = 'S';
+ info->set_gid = true;
+ } else if (s.st_mode & S_IXGRP) {
+ info->mode[6] = 'x';
+ if (!info->exec) info->exec = info->grp->gr_gid == gid;
} else {
info->mode[6] = '-';
}
info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-';
info->mode[8] = (s.st_mode & S_IWOTH) ? 'w' : '-';
- if (s.st_mode & S_IXOTH) {
+ if (s.st_mode & S_IXOTH && s.st_mode & S_ISVTX) {
+ info->mode[9] = 't';
+ info->set_gid = true;
+ info->exec = true;
+ } else if (s.st_mode & S_ISVTX) {
+ info->mode[9] = 'T';
+ info->set_gid = true;
+ } else if (s.st_mode & S_IXOTH) {
info->mode[9] = 'x';
info->exec = true;
} else {
diff --git a/lib/convert.c b/lib/convert.c
index 071268e..a4639c4 100644
--- a/lib/convert.c
+++ b/lib/convert.c
@@ -46,7 +46,7 @@ mode_t get_mode(const char* next) {
mode_t mode = (mode_t)strtol(next, &end, 8);
if (!end) return 0;
while(isspace(*end)) end++;
- if (*end != '\0' || (unsigned) mode < 010000) {
+ if (*end != '\0' || (unsigned) mode >= 010000) {
error("invalid file mode: `%s`", next);
}
return mode;
diff --git a/readme.md b/readme.md
index f63aa5e..6eed372 100644
--- a/readme.md
+++ b/readme.md
@@ -4,7 +4,7 @@
A terrible busybox/gnu coreutils clone.
Currently the only supported commands are:
-`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc`, `xargs`, `tac`, `rm`, `cp`, `mkdir`, `mv`, `grep`
+`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc`, `xargs`, `tac`, `rm`, `cp`, `mkdir`, `mv`, `grep`, `chown`, `chmod`
## How to
diff --git a/src/command.h b/src/command.h
index 87cc749..fa8b00c 100644
--- a/src/command.h
+++ b/src/command.h
@@ -26,5 +26,7 @@ COMMAND(cp);
COMMAND(makedir);
COMMAND(mv);
COMMAND(grep);
+COMMAND(chown_cmd);
+COMMAND(chmod_cmd);
#endif
diff --git a/src/main.c b/src/main.c
index ad64b10..2718041 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,4 +1,5 @@
#include "command.h"
+#include "convert.h"
#include "lslib.h"
#include <stdlib.h>
@@ -22,7 +23,7 @@ int main (ARGUMENTS) {
if (argc < 2) {
printf("usage: lazysphere [function [arguments]...]\n\n");
printf("currently defined functions:\n");
- printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc, xargs, tac, rm, cp, mkdir, mv, grep\n");
+ printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc, xargs, tac, rm, cp, mkdir, mv, grep, chown, chmod\n");
return EXIT_SUCCESS;
}
argc--;
@@ -84,6 +85,10 @@ int main (ARGUMENTS) {
return mv(NEXT_ARGS);
} else if (streql(cmd, "grep")) {
return grep(NEXT_ARGS);
+ } else if (streql(cmd, "chown")) {
+ return chown_cmd(NEXT_ARGS);
+ } else if (streql(cmd, "chmod")) {
+ return chmod_cmd(NEXT_ARGS);
} else {
fprintf(stderr, "lazysphere: invalid command %s\n", cmd);
return EXIT_FAILURE;