From 2f4cc13cdde6bd79356513085fc85d97ebd7c76a Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Sun, 9 Aug 2020 19:29:02 +0200 Subject: Added chown [WIP] --- chown.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 chown.c (limited to 'chown.c') diff --git a/chown.c b/chown.c new file mode 100644 index 0000000..d273afd --- /dev/null +++ b/chown.c @@ -0,0 +1,193 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum +{ + RMODE_FOLLOW_ROOT, + RMODE_FOLLOW_ALL, + RMODE_NO_FOLLOW, +} t_recursive_mode; + +typedef enum +{ + VLEVEL_CHANGE, + VLEVEL_QUIET, + VLEVEL_VERBOSE, +} t_verbose_level; + +static t_verbose_level g_verbosity = VLEVEL_QUIET; +static bool g_follow_link = true; +static bool g_recursive = false; +static t_recursive_mode g_recursive_mode = RMODE_NO_FOLLOW; +static char *g_name = "chown"; +static uid_t g_user_id = -1; +static gid_t g_group_id = -1; + +void verror_log(const char *format, va_list ap) +{ + fprintf(stderr, "%s: ", g_name); + vfprintf(stderr, format, ap); + fputc('\n', stderr); +} + +void error_log(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + verror_log(format, ap); + va_end(ap); +} + +void fatal(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + verror_log(format, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +bool parse_id(char *id_str, id_t *id_ptr, bool group) +{ + char *end; + + if (isdigit(*id_str)) + { + errno = 0; + long tmp = strtol(id_str, &end, 10); + if (errno != 0 || tmp > UINT_MAX || *end != '\0') + return false; + *id_ptr = (id_t)tmp; + } + else + { + struct passwd *pw = getpwnam(id_str); + if (pw == NULL) + return false; + if (group) + *id_ptr = pw->pw_gid; + else + *id_ptr = pw->pw_uid; + } + return true; +} + +bool file_chown(char *path, bool root) +{ + char recursive_path[PATH_MAX + 1] = {'\0'}; + struct stat statbuf; + + + if (stat(path, &statbuf) == -1) + { + error_log("cannot access '%s': %s", path, strerror(errno)); + return false; + } + + errno = 0; + if ((!g_recursive && g_follow_link) + || (g_recursive && (g_recursive_mode == RMODE_FOLLOW_ALL + || (root && g_recursive_mode == RMODE_FOLLOW_ROOT)))) + chown(path, g_user_id, g_group_id); + else + lchown(path, g_user_id, g_group_id); + if (errno != 0) + { + error_log("changing ownership of '%s': %s", path, strerror(errno)); + return false; + } + + if (g_recursive) + { + errno = 0; + DIR *directory = opendir(path); + + if (directory == NULL && errno == ENOTDIR) + return true; + if (directory == NULL) + { + error_log("cannot access '%s': %s", path, strerror(errno)); + return false; + } + + strcpy(recursive_path, path); + if (path[strlen(path) - 1] != '/') + strcat(recursive_path, "/"); + + struct dirent *entry; + while ((entry = readdir(directory)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + strcat(recursive_path, entry->d_name); + file_chown(recursive_path, false); + strrchr(recursive_path, '/')[1] = '\0'; + } + closedir(directory); + } + return true; +} + +/* +** +** TODO +** don't change group/user if not asked +** change group to login group is trailling : after user in cmd +** +*/ + +int main(int argc, char **argv) +{ + int option; + + while ((option = getopt(argc, argv, "cfvhRHLP")) != -1) + { + switch (option) + { + case 'c': g_verbosity = VLEVEL_CHANGE; break; + case 'f': g_verbosity = VLEVEL_QUIET; break; + case 'v': g_verbosity = VLEVEL_VERBOSE; break; + case 'h': g_follow_link = false; break; + case 'R': g_recursive = true; break; + case 'H': g_recursive_mode = RMODE_FOLLOW_ROOT; break; + case 'L': g_recursive_mode = RMODE_FOLLOW_ALL; break; + case 'P': g_recursive_mode = RMODE_NO_FOLLOW; break; + } + } + + if (argc - optind == 0) + fatal("missing operand"); + if (argc - optind == 1) + fatal("missing operand after '%s'", argv[optind]); + + char *user_str = argv[optind]; + char *group_str = strchr(argv[optind], ':'); + if (group_str != NULL) + { + *group_str = '\0'; + group_str++; + } + + if (!parse_id(user_str, &g_user_id, false)) + fatal("invalid user '%s'", user_str); + if (group_str != NULL && !parse_id(group_str, &g_group_id, true)) + fatal("invalid group '%s:%s'", user_str, group_str); + + for (optind++; optind < argc; optind++) + file_chown(argv[optind], true); + + return EXIT_SUCCESS; +} -- cgit