From 7437a242743cf29cfd7ed00ab6e5b660bcb09c44 Mon Sep 17 00:00:00 2001 From: Alastair Stuart Date: Sat, 13 Apr 2013 00:36:26 -0500 Subject: [PATCH] chmod: Added chmod --- chmod.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 chmod.c diff --git a/chmod.c b/chmod.c new file mode 100644 index 0000000..65abd19 --- /dev/null +++ b/chmod.c @@ -0,0 +1,377 @@ +/* + Copyright © 2013 Alastair Stuart + + This program is open source software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define VERSION "0.01" + +bool errors = false; + +// option flags +struct { + bool recursive; +} flags; + + +struct symbolic_mode +{ + mode_t who; + char operation; + mode_t perm; +}; + +mode_t user_flags[] = { + S_IRUSR, + S_IWUSR, + S_IXUSR +}; + +mode_t group_flags[] = { + S_IRGRP, + S_IWGRP, + S_IXGRP +}; + +mode_t other_flags[] = { + S_IROTH, + S_IWOTH, + S_IXOTH +}; + +bool isoctal(char ch) +{ + return (ch >= '0' && ch <= '7'); +} + +void usage(char* program) +{ + printf("Usage: %s [options] [mode] [file ...]\n", program); + printf("Changes file permissions.\n" + "\n" + " -R Run recursively.\n" + "\n" + " --help Print this message.\n" + " --version Show version info.\n" + "\n" + "[mode] is in the form of [ugoa][+-=][rwx] or an octal number.\n"); +} + +struct symbolic_mode* parse_mode_str(char* mode_str) +{ + struct symbolic_mode *mode = calloc(1, sizeof(struct symbolic_mode)); + + // parse "who" + size_t i = 0; + for (i = 0; i < strlen(mode_str); i++) + { + switch (mode_str[i]) + { + case 'u': + mode->who |= S_ISUID; + break; + case 'g': + mode->who |= S_ISGID; + break; + case 'o': + mode->who |= S_ISVTX; + break; + case 'a': + mode->who = S_ISUID | S_ISGID | S_ISVTX; + break; + case '+': + case '-': + case '=': + goto exit_loop; + break; + default: + free(mode); + return NULL; + } + } + +exit_loop: + mode->operation = mode_str[i]; + i++; + // parse permissions + for ( ; i < strlen(mode_str); i++) + { + switch (mode_str[i]) + { + case 'r': + mode->perm |= S_IROTH; + break; + case 'w': + mode->perm |= S_IWOTH; + break; + case 'x': + mode->perm |= S_IXOTH; + break; + default: + free(mode); + return NULL; + } + } + + return mode; +} + +mode_t equals_op(mode_t original, mode_t who, mode_t perm) +{ + mode_t new = original; + mode_t *flag_array = NULL; + + while (who != 0) + { + if (who & S_ISUID) { + flag_array = user_flags; + } else if (who & S_ISGID) { + flag_array = group_flags; + } else if (who & S_ISVTX) { + flag_array = other_flags; + } else { + return original; + } + + // turn off all the flags for that type + for (int i = 0; i < 3; i++) + { + new &= ~(flag_array[i]); + } + + if (perm & S_IROTH) { + new |= flag_array[0]; + } + if (perm & S_IWOTH) { + new |= flag_array[1]; + } + if (perm & S_IXOTH) { + new |= flag_array[2]; + } + + if (flag_array == user_flags) { + who &= ~S_ISUID; + } else if (flag_array == group_flags) { + who &= ~S_ISGID; + } else if (flag_array == other_flags) { + who &= ~S_ISVTX; + } else { + who = 0; + } + } + + return new; +} + +void do_chmod(char* file, struct symbolic_mode *mode, mode_t change, char* argv0) +{ + struct stat st; + if (stat(file, &st) < 0) { + fprintf(stderr, "%s: %s: %s\n", + argv0, strerror(errno), realpath(file, NULL)); + errors = true; + } + + mode_t final_mode = 0; + + if (mode) { + final_mode = st.st_mode; + + switch (mode->operation) + { + case '+': + final_mode |= change; + break; + case '-': + final_mode &= ~change; + break; + case '=': + final_mode = equals_op(final_mode, mode->who, mode->perm); + break; + default: + fprintf(stderr, "%s: No operation specified.\n", argv0); + errors = true; + } + } else { + final_mode = change; + } + + if (chmod(file, final_mode) < 0) { + fprintf(stderr, "%s: %s: %s\n", + argv0, strerror(errno), realpath(file, NULL)); + errors = true; + } + + if (S_ISDIR(st.st_mode) && flags.recursive) { + if (chdir(file) == 0) { + DIR *dp = NULL; + struct dirent *ep = NULL; + dp = opendir("."); + if (dp) { + while ((ep = readdir(dp))) + { + if (!(strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)) { + do_chmod(ep->d_name, mode, change, argv0); + } + } + closedir(dp); + } else { + fprintf(stderr, "%s: %s: %s\n", + argv0, strerror(errno), realpath(file, NULL)); + errors = true; + } + chdir(".."); + } else { + fprintf(stderr, "%s: %s: %s\n", + argv0, strerror(errno), realpath(file, NULL)); + } + } +} + +int main(int argc, char* argv[]) +{ + flags.recursive = false; + static struct option long_options[] = { + {"recursive", no_argument, NULL, 'R'}, + {"help", no_argument, NULL, 1}, + {"version", no_argument, NULL, 2}, + {NULL, 0, NULL, 0} + }; + + int c = 0; + while ((c = getopt_long(argc, argv, "R", + long_options, NULL)) != -1) + { + switch (c) + { + case 'R': + flags.recursive = true; + break; + case 1: + usage(argv[0]); + return 0; + case 2: + printf("chmod (mutos) v"VERSION"\n"); + return 0; + } + } + + if ((argc - optind) < 2) { + fprintf(stderr, "%s: missing operands\n" + "Run '%s --help' for usage.\n", + argv[0], argv[0]); + return 1; + } + + char* mode_str = argv[optind]; + optind++; + + // check if mode is in octal form + bool octal_mode = true; + for (size_t i = 0; i < strlen(mode_str); i++) + { + if (!isoctal(mode_str[i])) { + octal_mode = false; + } + } + + if (octal_mode) { + // convert string to octal number + mode_t final_mode = strtoul(mode_str, NULL, 8); + for (int i = 0; i < (argc - optind); i++) + { + do_chmod(argv[optind], NULL, final_mode, argv[0]); + } + + if (errors) { + return 1; + } else { + return 0; + } + + } + + struct symbolic_mode *mode = parse_mode_str(mode_str); + + if (!mode) { + fprintf(stderr, "%s: Invalid mode: %s\n", + argv[0], mode_str); + return 1; + } + + // if who is not specified, it defaults to user + if (mode->who == 0) { + mode->who = S_ISUID; + } + + mode_t change = 0; + + if (mode->who & S_ISUID) { + if (mode->perm & S_IROTH) { + change |= S_IRUSR; + } + if (mode->perm & S_IWOTH) { + change |= S_IWUSR; + } + if (mode->perm & S_IXOTH) { + change |= S_IXUSR; + } + } + + if (mode->who & S_ISGID) { + if (mode->perm & S_IROTH) { + change |= S_IRGRP; + } + if (mode->perm & S_IWOTH) { + change |= S_IWGRP; + } + if (mode->perm & S_IXOTH) { + change |= S_IXGRP; + } + } + + if (mode->who & S_ISVTX) { + if (mode->perm & S_IROTH) { + change |= S_IROTH; + } + if (mode->perm & S_IWOTH) { + change |= S_IWOTH; + } + if (mode->perm & S_IXOTH) { + change |= S_IXOTH; + } + } + + for (int i = 0; i < (argc - optind); i++) + { + do_chmod(argv[optind], mode, change, argv[0]); + } + + free(mode); + + if (errors) { + return 1; + } else { + return 0; + } +} -- 2.11.4.GIT