busybox/libbb/parse_mode.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * parse_mode implementation for busybox
   4 *
   5 * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9#include "libbb.h"
  10
  11/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
  12
  13/* This function is used from NOFORK applets. It must not allocate anything */
  14
  15#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
  16
  17int FAST_FUNC bb_parse_mode(const char *s, unsigned current_mode)
  18{
  19        static const mode_t who_mask[] = {
  20                S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
  21                S_ISUID | S_IRWXU,           /* u */
  22                S_ISGID | S_IRWXG,           /* g */
  23                S_IRWXO                      /* o */
  24        };
  25        static const mode_t perm_mask[] = {
  26                S_IRUSR | S_IRGRP | S_IROTH, /* r */
  27                S_IWUSR | S_IWGRP | S_IWOTH, /* w */
  28                S_IXUSR | S_IXGRP | S_IXOTH, /* x */
  29                S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
  30                S_ISUID | S_ISGID,           /* s */
  31                S_ISVTX                      /* t */
  32        };
  33        static const char who_chars[] ALIGN1 = "augo";
  34        static const char perm_chars[] ALIGN1 = "rwxXst";
  35
  36        const char *p;
  37        mode_t wholist;
  38        mode_t permlist;
  39        mode_t new_mode;
  40        char op;
  41
  42        if ((unsigned char)(*s - '0') < 8) {
  43                unsigned long tmp;
  44                char *e;
  45
  46                tmp = strtoul(s, &e, 8);
  47                if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
  48                        return -1;
  49                }
  50                return tmp;
  51        }
  52
  53        new_mode = current_mode;
  54
  55        /* Note: we allow empty clauses, and hence empty modes.
  56         * We treat an empty mode as no change to perms. */
  57
  58        while (*s) {  /* Process clauses. */
  59                if (*s == ',') {  /* We allow empty clauses. */
  60                        ++s;
  61                        continue;
  62                }
  63
  64                /* Get a wholist. */
  65                wholist = 0;
  66 WHO_LIST:
  67                p = who_chars;
  68                do {
  69                        if (*p == *s) {
  70                                wholist |= who_mask[(int)(p-who_chars)];
  71                                if (!*++s) {
  72                                        return -1;
  73                                }
  74                                goto WHO_LIST;
  75                        }
  76                } while (*++p);
  77
  78                do {    /* Process action list. */
  79                        if ((*s != '+') && (*s != '-')) {
  80                                if (*s != '=') {
  81                                        return -1;
  82                                }
  83                                /* Since op is '=', clear all bits corresponding to the
  84                                 * wholist, or all file bits if wholist is empty. */
  85                                permlist = ~FILEMODEBITS;
  86                                if (wholist) {
  87                                        permlist = ~wholist;
  88                                }
  89                                new_mode &= permlist;
  90                        }
  91                        op = *s++;
  92
  93                        /* Check for permcopy. */
  94                        p = who_chars + 1;  /* Skip 'a' entry. */
  95                        do {
  96                                if (*p == *s) {
  97                                        int i = 0;
  98                                        permlist = who_mask[(int)(p-who_chars)]
  99                                                         & (S_IRWXU | S_IRWXG | S_IRWXO)
 100                                                         & new_mode;
 101                                        do {
 102                                                if (permlist & perm_mask[i]) {
 103                                                        permlist |= perm_mask[i];
 104                                                }
 105                                        } while (++i < 3);
 106                                        ++s;
 107                                        goto GOT_ACTION;
 108                                }
 109                        } while (*++p);
 110
 111                        /* It was not a permcopy, so get a permlist. */
 112                        permlist = 0;
 113 PERM_LIST:
 114                        p = perm_chars;
 115                        do {
 116                                if (*p == *s) {
 117                                        if ((*p != 'X')
 118                                         || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
 119                                        ) {
 120                                                permlist |= perm_mask[(int)(p-perm_chars)];
 121                                        }
 122                                        if (!*++s) {
 123                                                break;
 124                                        }
 125                                        goto PERM_LIST;
 126                                }
 127                        } while (*++p);
 128 GOT_ACTION:
 129                        if (permlist) { /* The permlist was nonempty. */
 130                                mode_t tmp = wholist;
 131                                if (!wholist) {
 132                                        mode_t u_mask = umask(0);
 133                                        umask(u_mask);
 134                                        tmp = ~u_mask;
 135                                }
 136                                permlist &= tmp;
 137                                if (op == '-') {
 138                                        new_mode &= ~permlist;
 139                                } else {
 140                                        new_mode |= permlist;
 141                                }
 142                        }
 143                } while (*s && (*s != ','));
 144        }
 145
 146        return new_mode;
 147}
 148