chown, chmod

This commit is contained in:
Murphy 2023-05-12 17:38:41 -04:00
parent d8f2c10b71
commit c6446e178b
7 changed files with 457 additions and 20 deletions

232
command/chmod.c Normal file
View file

@ -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;
}

187
command/chown.c Normal file
View file

@ -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;
}

View file

@ -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[1] = (s.st_mode & S_IRUSR) ? 'r' : '-';
info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-'; info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-';
if (s.st_mode & S_IXUSR) { if (s.st_mode & S_IXUSR && s.st_mode & S_ISUID) {
if (s.st_mode & S_ISUID) {
info->mode[3] = 's'; info->mode[3] = 's';
info->set_uid = true; info->set_uid = true;
} else { 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'; info->mode[3] = 'x';
} if (!info->exec) info->exec = info->usr->pw_uid == uid;
if (!info->exec) info->exec = s.st_uid == uid;
} else { } else {
info->mode[3] = '-'; info->mode[3] = '-';
} }
info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-'; info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-';
info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-'; info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-';
if (s.st_mode & S_IXGRP) { if (s.st_mode & S_IXGRP && s.st_mode & S_ISGID) {
if (s.st_mode & S_ISGID) {
info->mode[6] = 's'; info->mode[6] = 's';
info->set_gid = true; info->set_gid = true;
} else { 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'; info->mode[6] = 'x';
} if (!info->exec) info->exec = info->grp->gr_gid == gid;
if (!info->exec) info->exec = s.st_gid == gid;
} else { } else {
info->mode[6] = '-'; info->mode[6] = '-';
} }
info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-'; info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-';
info->mode[8] = (s.st_mode & S_IWOTH) ? 'w' : '-'; 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->mode[9] = 'x';
info->exec = true; info->exec = true;
} else { } else {

View file

@ -46,7 +46,7 @@ mode_t get_mode(const char* next) {
mode_t mode = (mode_t)strtol(next, &end, 8); mode_t mode = (mode_t)strtol(next, &end, 8);
if (!end) return 0; if (!end) return 0;
while(isspace(*end)) end++; while(isspace(*end)) end++;
if (*end != '\0' || (unsigned) mode < 010000) { if (*end != '\0' || (unsigned) mode >= 010000) {
error("invalid file mode: `%s`", next); error("invalid file mode: `%s`", next);
} }
return mode; return mode;

View file

@ -4,7 +4,7 @@
A terrible busybox/gnu coreutils clone. A terrible busybox/gnu coreutils clone.
Currently the only supported commands are: 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 ## How to

View file

@ -26,5 +26,7 @@ COMMAND(cp);
COMMAND(makedir); COMMAND(makedir);
COMMAND(mv); COMMAND(mv);
COMMAND(grep); COMMAND(grep);
COMMAND(chown_cmd);
COMMAND(chmod_cmd);
#endif #endif

View file

@ -1,4 +1,5 @@
#include "command.h" #include "command.h"
#include "convert.h"
#include "lslib.h" #include "lslib.h"
#include <stdlib.h> #include <stdlib.h>
@ -22,7 +23,7 @@ int main (ARGUMENTS) {
if (argc < 2) { if (argc < 2) {
printf("usage: lazysphere [function [arguments]...]\n\n"); printf("usage: lazysphere [function [arguments]...]\n\n");
printf("currently defined functions:\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; return EXIT_SUCCESS;
} }
argc--; argc--;
@ -84,6 +85,10 @@ int main (ARGUMENTS) {
return mv(NEXT_ARGS); return mv(NEXT_ARGS);
} else if (streql(cmd, "grep")) { } else if (streql(cmd, "grep")) {
return grep(NEXT_ARGS); 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 { } else {
fprintf(stderr, "lazysphere: invalid command %s\n", cmd); fprintf(stderr, "lazysphere: invalid command %s\n", cmd);
return EXIT_FAILURE; return EXIT_FAILURE;