summaryrefslogtreecommitdiff
path: root/corebinutils/mkdir/mode.c
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:27:09 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:27:09 +0300
commiteabb006d617365cd92c3337d673af7fc6a210d78 (patch)
tree4afb8fd1d39a5d10b2e8cbb8579501de67cbf754 /corebinutils/mkdir/mode.c
parent4328365a80faebd5963112936b3d5daf0440d6e8 (diff)
parent4360cafc082b73ed9578911ed2dd8e022f4a5924 (diff)
downloadProject-Tick-eabb006d617365cd92c3337d673af7fc6a210d78.tar.gz
Project-Tick-eabb006d617365cd92c3337d673af7fc6a210d78.zip
Add 'corebinutils/mkdir/' from commit '4360cafc082b73ed9578911ed2dd8e022f4a5924'
git-subtree-dir: corebinutils/mkdir git-subtree-mainline: 4328365a80faebd5963112936b3d5daf0440d6e8 git-subtree-split: 4360cafc082b73ed9578911ed2dd8e022f4a5924
Diffstat (limited to 'corebinutils/mkdir/mode.c')
-rw-r--r--corebinutils/mkdir/mode.c357
1 files changed, 357 insertions, 0 deletions
diff --git a/corebinutils/mkdir/mode.c b/corebinutils/mkdir/mode.c
new file mode 100644
index 0000000000..1f3d2af9b8
--- /dev/null
+++ b/corebinutils/mkdir/mode.c
@@ -0,0 +1,357 @@
+#include "mode.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+typedef struct bitcmd {
+ char cmd;
+ char cmd2;
+ mode_t bits;
+} bitcmd_t;
+
+#define SET_LEN 6
+#define SET_LEN_INCR 4
+
+#define CMD2_CLR 0x01
+#define CMD2_SET 0x02
+#define CMD2_GBITS 0x04
+#define CMD2_OBITS 0x08
+#define CMD2_UBITS 0x10
+
+#define STANDARD_BITS (S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO)
+
+#ifndef S_ISVTX
+#define S_ISVTX 01000
+#endif
+
+static bitcmd_t *addcmd(bitcmd_t *set, mode_t op, mode_t who, mode_t oparg,
+ mode_t mask);
+static void compress_mode(bitcmd_t *set);
+
+mode_t
+mode_apply(const void *compiled, mode_t old_mode)
+{
+ const bitcmd_t *set;
+ mode_t clear_value, new_mode, value;
+
+ set = compiled;
+ new_mode = old_mode;
+ for (value = 0;; set++) {
+ switch (set->cmd) {
+ case 'u':
+ value = (new_mode & S_IRWXU) >> 6;
+ goto common;
+ case 'g':
+ value = (new_mode & S_IRWXG) >> 3;
+ goto common;
+ case 'o':
+ value = new_mode & S_IRWXO;
+common:
+ if (set->cmd2 & CMD2_CLR) {
+ clear_value = (set->cmd2 & CMD2_SET) ? S_IRWXO :
+ value;
+ if (set->cmd2 & CMD2_UBITS)
+ new_mode &= ~((clear_value << 6) &
+ set->bits);
+ if (set->cmd2 & CMD2_GBITS)
+ new_mode &= ~((clear_value << 3) &
+ set->bits);
+ if (set->cmd2 & CMD2_OBITS)
+ new_mode &= ~(clear_value & set->bits);
+ }
+ if (set->cmd2 & CMD2_SET) {
+ if (set->cmd2 & CMD2_UBITS)
+ new_mode |= (value << 6) & set->bits;
+ if (set->cmd2 & CMD2_GBITS)
+ new_mode |= (value << 3) & set->bits;
+ if (set->cmd2 & CMD2_OBITS)
+ new_mode |= value & set->bits;
+ }
+ break;
+ case '+':
+ new_mode |= set->bits;
+ break;
+ case '-':
+ new_mode &= ~set->bits;
+ break;
+ case 'X':
+ if (old_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+ new_mode |= set->bits;
+ break;
+ case '\0':
+ default:
+ return (new_mode);
+ }
+ }
+}
+
+void *
+mode_compile(const char *mode_string)
+{
+ char op, *endp;
+ bitcmd_t *endset, *saveset, *set;
+ mode_t mask, perm, perm_xbits, who;
+ long perm_value;
+ unsigned int setlen;
+ int saved_errno;
+ int equal_done;
+
+ if (*mode_string == '\0') {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ mask = S_IRWXU | S_IRWXG | S_IRWXO;
+ setlen = SET_LEN + 2;
+ set = malloc(setlen * sizeof(*set));
+ if (set == NULL)
+ return (NULL);
+ saveset = set;
+ endset = set + (setlen - 2);
+
+ if (isdigit((unsigned char)*mode_string)) {
+ errno = 0;
+ perm_value = strtol(mode_string, &endp, 8);
+ if (*endp != '\0') {
+ errno = EINVAL;
+ goto fail;
+ }
+ if (errno == ERANGE &&
+ (perm_value == LONG_MAX || perm_value == LONG_MIN)) {
+ goto fail;
+ }
+ if (perm_value & ~(STANDARD_BITS | S_ISVTX)) {
+ errno = EINVAL;
+ goto fail;
+ }
+ perm = (mode_t)perm_value;
+ set = addcmd(set, '=', STANDARD_BITS | S_ISVTX, perm, mask);
+ set->cmd = '\0';
+ return (saveset);
+ }
+
+ equal_done = 0;
+ for (;;) {
+ for (who = 0;; mode_string++) {
+ switch (*mode_string) {
+ case 'a':
+ who |= STANDARD_BITS;
+ break;
+ case 'u':
+ who |= S_ISUID | S_IRWXU;
+ break;
+ case 'g':
+ who |= S_ISGID | S_IRWXG;
+ break;
+ case 'o':
+ who |= S_IRWXO;
+ break;
+ default:
+ goto getop;
+ }
+ }
+getop:
+ op = *mode_string++;
+ if (op != '+' && op != '-' && op != '=') {
+ errno = EINVAL;
+ goto fail;
+ }
+ if (op == '=')
+ equal_done = 0;
+
+ who &= ~S_ISVTX;
+ for (perm = 0, perm_xbits = 0;; mode_string++) {
+ switch (*mode_string) {
+ case 'r':
+ perm |= S_IRUSR | S_IRGRP | S_IROTH;
+ break;
+ case 'w':
+ perm |= S_IWUSR | S_IWGRP | S_IWOTH;
+ break;
+ case 'x':
+ perm |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case 'X':
+ perm_xbits = S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case 's':
+ if (who == 0 || (who & ~S_IRWXO) != 0)
+ perm |= S_ISUID | S_ISGID;
+ break;
+ case 't':
+ if (who == 0 || (who & ~S_IRWXO) != 0) {
+ who |= S_ISVTX;
+ perm |= S_ISVTX;
+ }
+ break;
+ case 'u':
+ case 'g':
+ case 'o':
+ if (perm != 0) {
+ set = addcmd(set, op, who, perm, mask);
+ perm = 0;
+ }
+ if (op == '=')
+ equal_done = 1;
+ if (op == '+' && perm_xbits != 0) {
+ set = addcmd(set, 'X', who, perm_xbits,
+ mask);
+ perm_xbits = 0;
+ }
+ set = addcmd(set, *mode_string, who, op, mask);
+ break;
+ default:
+ if (perm != 0 || (op == '=' && !equal_done)) {
+ if (op == '=')
+ equal_done = 1;
+ set = addcmd(set, op, who, perm, mask);
+ perm = 0;
+ }
+ if (perm_xbits != 0) {
+ set = addcmd(set, 'X', who, perm_xbits,
+ mask);
+ perm_xbits = 0;
+ }
+ goto apply;
+ }
+
+ if (set >= endset) {
+ ptrdiff_t offset;
+ bitcmd_t *newset;
+
+ offset = set - saveset;
+ setlen += SET_LEN_INCR;
+ newset = realloc(saveset,
+ setlen * sizeof(*newset));
+ if (newset == NULL)
+ goto fail;
+ saveset = newset;
+ set = newset + offset;
+ endset = newset + (setlen - 2);
+ }
+ }
+apply:
+ if (*mode_string == '\0')
+ break;
+ if (*mode_string != ',') {
+ errno = EINVAL;
+ goto fail;
+ }
+ mode_string++;
+ }
+
+ set->cmd = '\0';
+ compress_mode(saveset);
+ return (saveset);
+
+fail:
+ saved_errno = errno;
+ free(saveset);
+ errno = saved_errno;
+ return (NULL);
+}
+
+void
+mode_free(void *compiled)
+{
+ free(compiled);
+}
+
+static bitcmd_t *
+addcmd(bitcmd_t *set, mode_t op, mode_t who, mode_t oparg, mode_t mask)
+{
+ switch (op) {
+ case '=':
+ set->cmd = '-';
+ set->bits = who ? who : STANDARD_BITS;
+ set++;
+ op = '+';
+ /* FALLTHROUGH */
+ case '+':
+ case '-':
+ case 'X':
+ set->cmd = op;
+ set->cmd2 = 0;
+ set->bits = (who ? who : mask) & oparg;
+ break;
+ case 'u':
+ case 'g':
+ case 'o':
+ set->cmd = op;
+ if (who != 0) {
+ set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
+ ((who & S_IRGRP) ? CMD2_GBITS : 0) |
+ ((who & S_IROTH) ? CMD2_OBITS : 0);
+ set->bits = (mode_t)~0;
+ } else {
+ set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
+ set->bits = mask;
+ }
+
+ if (oparg == '+')
+ set->cmd2 |= CMD2_SET;
+ else if (oparg == '-')
+ set->cmd2 |= CMD2_CLR;
+ else if (oparg == '=')
+ set->cmd2 |= CMD2_SET | CMD2_CLR;
+ break;
+ }
+
+ return (set + 1);
+}
+
+static void
+compress_mode(bitcmd_t *set)
+{
+ bitcmd_t *next_set;
+ int clear_bits, op, set_bits, x_bits;
+
+ for (next_set = set;;) {
+ while ((op = next_set->cmd) != '+' && op != '-' && op != 'X') {
+ *set++ = *next_set++;
+ if (op == '\0')
+ return;
+ }
+
+ for (set_bits = clear_bits = x_bits = 0;; next_set++) {
+ op = next_set->cmd;
+ if (op == '-') {
+ clear_bits |= next_set->bits;
+ set_bits &= ~next_set->bits;
+ x_bits &= ~next_set->bits;
+ } else if (op == '+') {
+ set_bits |= next_set->bits;
+ clear_bits &= ~next_set->bits;
+ x_bits &= ~next_set->bits;
+ } else if (op == 'X') {
+ x_bits |= next_set->bits & ~set_bits;
+ } else {
+ break;
+ }
+ }
+
+ if (clear_bits != 0) {
+ set->cmd = '-';
+ set->cmd2 = 0;
+ set->bits = clear_bits;
+ set++;
+ }
+ if (set_bits != 0) {
+ set->cmd = '+';
+ set->cmd2 = 0;
+ set->bits = set_bits;
+ set++;
+ }
+ if (x_bits != 0) {
+ set->cmd = 'X';
+ set->cmd2 = 0;
+ set->bits = x_bits;
+ set++;
+ }
+ }
+}