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/* should be mode_t really, but in all Unixes these constants fit into uint16 */
  20        static const uint16_t who_mask[] ALIGN2 = {
  21                S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
  22                S_ISUID | S_IRWXU,           /* u */
  23                S_ISGID | S_IRWXG,           /* g */
  24                S_IRWXO                      /* o */
  25        };
  26        static const uint16_t perm_mask[] ALIGN2 = {
  27                S_IRUSR | S_IRGRP | S_IROTH, /* r */
  28                S_IWUSR | S_IWGRP | S_IWOTH, /* w */
  29                S_IXUSR | S_IXGRP | S_IXOTH, /* x */
  30                S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
  31                S_ISUID | S_ISGID,           /* s */
  32                S_ISVTX                      /* t */
  33        };
  34        static const char who_chars[] ALIGN1 = "augo";
  35        static const char perm_chars[] ALIGN1 = "rwxXst";
  36
  37        const char *p;
  38        mode_t wholist;
  39        mode_t permlist;
  40        mode_t new_mode;
  41        char op;
  42
  43        if ((unsigned char)(*s - '0') < 8) {
  44                unsigned long tmp;
  45                char *e;
  46
  47                tmp = strtoul(s, &e, 8);
  48                if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
  49                        return -1;
  50                }
  51                return tmp;
  52        }
  53
  54        new_mode = current_mode;
  55
  56        /* Note: we allow empty clauses, and hence empty modes.
  57         * We treat an empty mode as no change to perms. */
  58
  59        while (*s) {  /* Process clauses. */
  60                if (*s == ',') {  /* We allow empty clauses. */
  61                        ++s;
  62                        continue;
  63                }
  64
  65                /* Get a wholist. */
  66                wholist = 0;
  67 WHO_LIST:
  68                p = who_chars;
  69                do {
  70                        if (*p == *s) {
  71                                wholist |= who_mask[(int)(p-who_chars)];
  72                                if (!*++s) {
  73                                        return -1;
  74                                }
  75                                goto WHO_LIST;
  76                        }
  77                } while (*++p);
  78
  79                do {    /* Process action list. */
  80                        if ((*s != '+') && (*s != '-')) {
  81                                if (*s != '=') {
  82                                        return -1;
  83                                }
  84                                /* Since op is '=', clear all bits corresponding to the
  85                                 * wholist, or all file bits if wholist is empty. */
  86                                permlist = ~FILEMODEBITS;
  87                                if (wholist) {
  88                                        permlist = ~wholist;
  89                                }
  90                                new_mode &= permlist;
  91                        }
  92                        op = *s++;
  93
  94                        /* Check for permcopy. */
  95                        p = who_chars + 1;  /* Skip 'a' entry. */
  96                        do {
  97                                if (*p == *s) {
  98                                        int i = 0;
  99                                        permlist = who_mask[(int)(p-who_chars)]
 100                                                         & (S_IRWXU | S_IRWXG | S_IRWXO)
 101                                                         & new_mode;
 102                                        do {
 103                                                if (permlist & perm_mask[i]) {
 104                                                        permlist |= perm_mask[i];
 105                                                }
 106                                        } while (++i < 3);
 107                                        ++s;
 108                                        goto GOT_ACTION;
 109                                }
 110                        } while (*++p);
 111
 112                        /* It was not a permcopy, so get a permlist. */
 113                        permlist = 0;
 114 PERM_LIST:
 115                        p = perm_chars;
 116                        do {
 117                                if (*p == *s) {
 118                                        if ((*p != 'X')
 119                                         || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
 120                                        ) {
 121                                                permlist |= perm_mask[(int)(p-perm_chars)];
 122                                        }
 123                                        if (!*++s) {
 124                                                break;
 125                                        }
 126                                        goto PERM_LIST;
 127                                }
 128                        } while (*++p);
 129 GOT_ACTION:
 130                        if (permlist) { /* The permlist was nonempty. */
 131                                mode_t tmp = wholist;
 132                                if (!wholist) {
 133                                        mode_t u_mask = umask(0);
 134                                        umask(u_mask);
 135                                        tmp = ~u_mask;
 136                                }
 137                                permlist &= tmp;
 138                                if (op == '-') {
 139                                        new_mode &= ~permlist;
 140                                } else {
 141                                        new_mode |= permlist;
 142                                }
 143                        }
 144                } while (*s && (*s != ','));
 145        }
 146
 147        return new_mode;
 148}
 149