2023-05-16 23:15:01 +00:00
|
|
|
/**
|
|
|
|
* file: chown.c
|
|
|
|
* author: Tyler Murphy
|
|
|
|
*/
|
|
|
|
|
2023-05-12 21:38:41 +00:00
|
|
|
#include "command.h"
|
|
|
|
#include "lslib.h"
|
|
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/**
|
|
|
|
* File flags to be used with chown
|
|
|
|
*/
|
2023-05-12 21:38:41 +00:00
|
|
|
static struct {
|
2023-05-16 23:15:01 +00:00
|
|
|
bool recurse; /* -R if to recurse directorys */
|
|
|
|
bool list_changed; /* -c if to list what was changed */
|
|
|
|
bool verbose; /* -v if to list all output */
|
|
|
|
bool quiet; /* -f if to silence errors */
|
|
|
|
uid_t uid; /* the uid to set the file to */
|
|
|
|
gid_t gid; /* the gid to set the file to */
|
2023-05-12 21:38:41 +00:00
|
|
|
} flags;
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/**
|
|
|
|
* Help function for chown
|
|
|
|
*/
|
2023-05-12 21:38:41 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/**
|
|
|
|
* Takes in each argument that has a single - and parses it
|
|
|
|
* @param c the character after the -
|
|
|
|
* @param next the next argument in argv that hasnt been parsed
|
|
|
|
* @reutrn if the next arg was used or if the arg was invalid
|
|
|
|
*/
|
2023-05-12 21:38:41 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/**
|
|
|
|
* Given a file path change the filw ownership with the given flags
|
|
|
|
* @param path the file to change the ownership
|
|
|
|
*/
|
2023-05-12 21:38:41 +00:00
|
|
|
static void chown_file(char* path) {
|
2023-05-16 23:15:01 +00:00
|
|
|
|
|
|
|
/* allocate arguments on the stack */
|
|
|
|
int save; /* path buffer save */
|
|
|
|
struct stat s; /* file stats */
|
|
|
|
DIR* d; /* recursing dir if recursing */
|
|
|
|
struct dirent* file; /*if recursing current file being read */
|
2023-05-12 21:38:41 +00:00
|
|
|
|
|
|
|
save = push_path_buffer(path);
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* attempt to change file ownership, if failed error and return */
|
2023-05-12 21:38:41 +00:00
|
|
|
if (chown(get_path_buffer(), flags.uid, flags.gid) < 0) {
|
|
|
|
if (!flags.quiet) {
|
2023-05-16 23:15:01 +00:00
|
|
|
error_s("cannot chown '%s'", get_path_buffer()); /* error if failed */
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|
2023-05-16 23:15:01 +00:00
|
|
|
pop_path_buffer(save); /* cleanup */
|
2023-05-12 21:38:41 +00:00
|
|
|
return;
|
2023-05-16 23:15:01 +00:00
|
|
|
} else if (flags.list_changed) { /* list changed if verbose */
|
|
|
|
output("changed '%s' to %u:%u", get_path_buffer(), flags.uid, flags.gid);
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* if not recurse dont do future checks and return */
|
2023-05-12 21:38:41 +00:00
|
|
|
if (!flags.recurse) {
|
|
|
|
pop_path_buffer(save);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* stat file info and return if failed */
|
2023-05-12 21:38:41 +00:00
|
|
|
if (lstat(get_path_buffer(), &s) < 0) {
|
|
|
|
if (!flags.quiet) {
|
2023-05-16 23:15:01 +00:00
|
|
|
error_s("cannot stat '%s'", get_path_buffer()); /* error if failed */
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|
2023-05-16 23:15:01 +00:00
|
|
|
pop_path_buffer(save); /* clean up */
|
2023-05-12 21:38:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* if not dir, cannot recurse so return */
|
2023-05-12 21:38:41 +00:00
|
|
|
if (!S_ISDIR(s.st_mode)) {
|
2023-05-16 23:15:01 +00:00
|
|
|
pop_path_buffer(save); /* clean up */
|
2023-05-12 21:38:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* open dir, if failed return and error */
|
2023-05-12 21:38:41 +00:00
|
|
|
d = opendir(get_path_buffer());
|
|
|
|
if (d == NULL) {
|
|
|
|
if (!flags.quiet) {
|
2023-05-16 23:15:01 +00:00
|
|
|
error_s("cannot open dir '%s'", get_path_buffer()); /* error if failed */
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|
2023-05-16 23:15:01 +00:00
|
|
|
pop_path_buffer(save); /* clean up */
|
2023-05-12 21:38:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* read each file in directory, and modify its ownership recursivly */
|
2023-05-12 21:38:41 +00:00
|
|
|
while ((file = readdir(d)) != NULL) {
|
2023-05-16 23:15:01 +00:00
|
|
|
if (is_dot_dir(file->d_name)) continue; /* if dot dir skip */
|
2023-05-12 21:38:41 +00:00
|
|
|
chown_file(file->d_name);
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* clean up */
|
2023-05-12 21:38:41 +00:00
|
|
|
closedir(d);
|
|
|
|
pop_path_buffer(save);
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/**
|
|
|
|
* Parse a given user and group from a string
|
|
|
|
* @param str the stirng to parse
|
|
|
|
*/
|
2023-05-12 21:38:41 +00:00
|
|
|
static void parse_ownership(char* str) {
|
2023-05-16 23:15:01 +00:00
|
|
|
char* user = str; /* a copy of the input string */
|
|
|
|
char* group = NULL; /* the group name found in str */
|
|
|
|
char* end = NULL; /* the end of a string to parse a number if passed */
|
|
|
|
unsigned long i; /* a number i */
|
|
|
|
struct passwd* p = NULL; /* a user if found */
|
|
|
|
struct group* g = NULL; /* a group if found */
|
|
|
|
|
|
|
|
/* attempy to find a colon seperating the user and group in the string, if not parse it as a user only */
|
2023-05-12 21:38:41 +00:00
|
|
|
for (i = 0; i < strlen(str); i++) {
|
|
|
|
if (str[i] == ':') {
|
|
|
|
str[i] = '\0';
|
|
|
|
group = &str[i] + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* attempt to parse input as a number */
|
2023-05-12 21:38:41 +00:00
|
|
|
flags.uid = strtol(user, &end, 10);
|
2023-05-16 23:15:01 +00:00
|
|
|
if (end != user) goto group; /* if successfull they ave a direct uid */
|
2023-05-12 21:38:41 +00:00
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* if not try to get the user by the string */
|
2023-05-12 21:38:41 +00:00
|
|
|
if ((p = getpwnam(user)) == NULL) {
|
2023-05-16 23:15:01 +00:00
|
|
|
error("invalid user '%s'", user); /* error and abort if failed */
|
2023-05-12 21:38:41 +00:00
|
|
|
} else {
|
2023-05-16 23:15:01 +00:00
|
|
|
flags.uid = p->pw_uid; /* update flags */
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
group:
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* if the group string was not found set the gorup to the user */
|
2023-05-12 21:38:41 +00:00
|
|
|
if (group == NULL) {
|
|
|
|
if (p == NULL) {
|
2023-05-16 23:15:01 +00:00
|
|
|
flags.gid = flags.uid; /* if a int was passed set that int to the group as well */
|
2023-05-12 21:38:41 +00:00
|
|
|
} else {
|
2023-05-16 23:15:01 +00:00
|
|
|
flags.gid = p->pw_gid; /* if a user was passed set the group to the users gid */
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* if a group was passed, attempt to parse it as a gid first */
|
2023-05-12 21:38:41 +00:00
|
|
|
flags.gid = strtol(group, &end, 10);
|
2023-05-16 23:15:01 +00:00
|
|
|
if (end != group) return; /* if succssfull return were done */
|
2023-05-12 21:38:41 +00:00
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* otherwise try to get the group from the name */
|
2023-05-12 21:38:41 +00:00
|
|
|
if ((g = getgrnam(group)) == NULL) {
|
2023-05-16 23:15:01 +00:00
|
|
|
error("invalid group '%s'", group); /* error and abort if failed */
|
2023-05-12 21:38:41 +00:00
|
|
|
} else {
|
2023-05-16 23:15:01 +00:00
|
|
|
flags.gid = g->gr_gid; /* update gid */
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-15 14:57:33 +00:00
|
|
|
COMMAND(chown_main) {
|
2023-05-12 21:38:41 +00:00
|
|
|
|
|
|
|
int start, i;
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* set default flags for chown */
|
2023-05-12 21:38:41 +00:00
|
|
|
flags.recurse = false;
|
|
|
|
flags.list_changed = false;
|
|
|
|
flags.verbose = false;
|
|
|
|
flags.quiet = false;
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* parse argument */
|
2023-05-12 21:38:41 +00:00
|
|
|
start = parse_args(argc, argv, help, short_arg, NULL);
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* if no more arguments error and abort since no ownership passed */
|
2023-05-12 21:38:41 +00:00
|
|
|
if (argc - start < 1) {
|
2023-05-16 23:15:01 +00:00
|
|
|
if (!flags.quiet) { /* if not set to hide errors */
|
2023-05-12 21:38:41 +00:00
|
|
|
error("no onwership passed");
|
|
|
|
} else {
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parse_ownership(argv[start]);
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* if not more arugumnets error and abort since no files passed */
|
2023-05-12 21:38:41 +00:00
|
|
|
if (argc - start < 1) {
|
2023-05-16 23:15:01 +00:00
|
|
|
if (!flags.quiet) { /* if not set to hide errors */
|
|
|
|
error("no files passed");
|
2023-05-12 21:38:41 +00:00
|
|
|
} else {
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
/* change ownership for each file passed */
|
2023-05-12 21:38:41 +00:00
|
|
|
for (i = start + 1; i < argc; i++) {
|
|
|
|
chown_file(argv[i]);
|
|
|
|
}
|
|
|
|
|
2023-05-16 23:15:01 +00:00
|
|
|
return EXIT_SUCCESS; /* done */
|
2023-05-12 21:38:41 +00:00
|
|
|
}
|