From 50710fbf7171b9aad39880165da4e6bdda18c1dc Mon Sep 17 00:00:00 2001 From: Alan Potteiger Date: Wed, 23 Nov 2022 21:05:07 +0100 Subject: [PATCH] Starting `chmod` and symbolic mode parser --- Makefile | 11 +-- README | 4 ++ chmod.c | 107 ++++++++++++++++++++++++++++++ lab.h | 7 ++ liblab/liblab.mk | 4 ++ liblab/mode.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 328 insertions(+), 4 deletions(-) create mode 100644 chmod.c create mode 100644 lab.h create mode 100644 liblab/liblab.mk create mode 100644 liblab/mode.c diff --git a/Makefile b/Makefile index 778cf5f..84b7aa0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ -BIN=cat logname pwd sleep tty uname -CFLAGS=-std=c99 -all: $(BIN) +BIN=cat chmod logname pwd sleep tty uname +LIB=liblab.a +CFLAGS=-std=c99 -L./ -llab +all: $(LIB) $(BIN) clean: - rm -f $(BIN) + rm -f $(LIB) $(BIN) + +include liblab/liblab.mk diff --git a/README b/README index ff6d855..49aa13c 100644 --- a/README +++ b/README @@ -5,12 +5,15 @@ This repository is just me writing Unix utilities and tinkering, trying to learn all I can. No real specific scope, just going wild. We'll see what happens. +Some of this may have been written while drinking. + Welcome to my lab. Utilities --------- - cat +- chmod - logname - pwd - sleep @@ -21,3 +24,4 @@ License ------- BSD 3-Clause License, see `LICENSE`. + diff --git a/chmod.c b/chmod.c new file mode 100644 index 0000000..edd2748 --- /dev/null +++ b/chmod.c @@ -0,0 +1,107 @@ +/* `chmod.c` - change the file modes + Copyright (c) 2022, Alan Potteiger + See `LICENSE` for copyright and license details */ + +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include + +#include "lab.h" + +static const char *usage = { + "usage: chmod [-R] mode file...\n" +}; + +static char *modestr; /* mode string */ + +/* callback for ftw(3) to traverse directory tree */ +static int +recurse(const char *path, const struct stat *sb, int flag) +{ + mode_t m; + switch (flag) { + case FTW_D: + m = sb->st_mode; + modeset(modestr, &m); + if (chmod(path, m) != 0) + fprintf(stderr, "chmod: %s: %s\n", path, + strerror(errno)); + return 0; + case FTW_F: case FTW_SL: case FTW_SLN: + m = sb->st_mode; + modeset(modestr, &m); + if (chmod(path, m) != 0) + fprintf(stderr, "chmod: %s: %s\n", path, + strerror(errno)); + return 0; + default: + return 0; + } +} + +int +main(int argc, char *argv[]) +{ + struct stat st; + int status, Rflag; + char ch; + mode_t mode; + + status = 0; + Rflag = 0; + + argc--; + argv++; + + if (argc < 2) { + fputs(usage, stderr); + return 1; + } + + if (argv[0][0] == '-' && argv[0][1] == 'R') { + Rflag = 1; + argc--; + argv++; + } + + if (argc < 2) { + fputs(usage, stderr); + return 1; + } + + modestr = argv[0]; + argc--; argv++; + + for (; argc > 0; argc-- && argv++) { + if (stat(argv[0], &st) != 0) { + fprintf(stderr, "chmod: %s: %s\n", argv[0], + strerror(errno)); + status = 1; + continue; + } + + mode = st.st_mode; + if (modeset(modestr, &mode) != 0) { + fprintf(stderr, "chmod: invalid mode"); + return 1; + } + + if (Rflag) + ftw(argv[0], recurse, 1); + + if (chmod(argv[0], mode) != 0) { + fprintf(stderr, "chmod: %s: %s\n", argv[0], + strerror(errno)); + status = 1; + continue; + } + } + + return status; +} + diff --git a/lab.h b/lab.h new file mode 100644 index 0000000..ea68aba --- /dev/null +++ b/lab.h @@ -0,0 +1,7 @@ +/* `lab.h` - liblab definitions + Copyright (c) 2022, Alan Potteiger + See `LICENSE` for copyright and license details */ + +/* mode.c */ +int modeset(char *modestr, mode_t *modeset); + diff --git a/liblab/liblab.mk b/liblab/liblab.mk new file mode 100644 index 0000000..f42e1bf --- /dev/null +++ b/liblab/liblab.mk @@ -0,0 +1,4 @@ +OBJ=liblab/mode.o +liblab.a: $(OBJ) + $(AR) -rs liblab.a liblab/mode.o + diff --git a/liblab/mode.c b/liblab/mode.c new file mode 100644 index 0000000..dc96db6 --- /dev/null +++ b/liblab/mode.c @@ -0,0 +1,199 @@ +/* mode.c - parse and apply symbolic modes (mode_t) + Copyright (c) 2022, Alan Potteiger + See `LICENSE` for copyright and license details */ + +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 700 /* Single UNIX Specification, Version 4 + required for `t` perm, S_ISVTX bits */ + +#include +#include +#include +#include +#include +#include + +/* Following is an excerpt from the POSIX definition of `chmod` containing a + grammar for symbolic modes. + + symbolic_mode : clause + | symbolic_mode ',' clause + ; + + clause : actionlist + | wholist actionlist + ; + + wholist : who + | wholist who + ; + + who : 'u' | 'g' | 'o' | 'a' + ; + + actionlist : action + | actionlist action + ; + + action : op + | op permlist + | op permcopy + ; + + permcopy : 'u' | 'g' | 'o' + ; + + op : '+' | '-' | '=' + ; + + permlist : perm + | perm permlist + ; + perm : 'r' | 'w' | 'x' | 'X' | 's' | 't' + ; */ + +/* Accepts symbolic mode string `modestr` and applies its changes to `modeset` + Returns > 0 for any syntax errors */ +int +modeset(char *modestr, mode_t *modeset) +{ + char *ptr; + char *end; + char op, perm; + mode_t copy, work, who, mode; + + ptr = modestr; + + /* if octal number we set the mode absolutely and return */ + end = ptr; + mode = (mode_t) strtol(ptr, &end, 8); + if (*end == '\0') { + *modeset = mode; + return 0; + } + +clause: + mode = *modeset; + op = perm = 0; + copy = work = who = 0; + + /* Find who's permissions we're editing */ + for (; *ptr != '\0'; ptr++) { + switch (*ptr) { + case 'u': + who |= S_IRWXU; + continue; + case 'g': + who |= S_IRWXG; + continue; + case 'o': + who |= S_IRWXO; + continue; + case 'a': + who |= S_IRWXU | S_IRWXG | S_IRWXO; + continue; + default: + break; + } + + break; + } + + /* if no who specified, default to all */ + if (who == 0) + who |= S_IRWXU | S_IRWXG | S_IRWXO; + + /* find operator */ + switch (*ptr) { + case '+': + case '-': + case '=': + op = *ptr; + break; + default: + return 1; + } + + ptr++; + + /* find permissions/permcopy and setup mask to be used */ + for (; *ptr != '\0'; ptr++) { + switch (*ptr) { + case 'r': + work = who & (S_IRUSR | S_IRGRP | S_IROTH); + continue; + case 'w': + work = who & (S_IWUSR | S_IWGRP | S_IWOTH); + continue; + case 'x': + work = who & (S_IXUSR | S_IXGRP | S_IXOTH); + continue; + /* TODO + case 'X': + continue; + case 's': + continue; + */ + case 't': + work |= S_ISVTX; + continue; + /* permcopy */ + case 'u': + copy = *modeset; + copy |= copy >> 3; + copy |= copy >> 3; + work = who & copy; + break; + case 'g': + copy = *modeset; + copy |= copy << 3; + copy |= copy >> 3; + work = who & copy; + break; + case 'o': + copy = *modeset; + copy |= copy << 3; + copy |= copy << 3; + work = who & copy; + break; + default: + break; + } + + if (work == 0) { + /* invalid or missing permission character */ + return 1; + } + break; + } + + /* do our work on the given mode depending on our operator */ + switch (op) { + case '+': + /* + operator: turn selected bits on */ + mode |= work; + break; + case '-': + /* - operator: turn selected bits off */ + work = ~work; + mode &= work; + break; + case '=': + /* = operator: clear user permissions mentioned, then set */ + who = ~who; + mode &= who; + + mode |= work; + break; + } + + *modeset = mode; + + if (*ptr == ',') { + ptr++; + goto clause; + } + + return 0; +} + -- 2.11.4.GIT