LCOV - code coverage report
Current view: top level - lib - modechange.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 150 150 100.0 %
Date: 2018-01-30 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /* modechange.c -- file mode manipulation
       2             : 
       3             :    Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001, 2003, 2004, 2005,
       4             :    2006 Free Software Foundation, Inc.
       5             : 
       6             :    This program is free software: you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      18             : 
      19             : /* Written by David MacKenzie <djm@ai.mit.edu> */
      20             : 
      21             : /* The ASCII mode string is compiled into an array of `struct
      22             :    modechange', which can then be applied to each file to be changed.
      23             :    We do this instead of re-parsing the ASCII string for each file
      24             :    because the compiled form requires less computation to use; when
      25             :    changing the mode of many files, this probably results in a
      26             :    performance gain.  */
      27             : 
      28             : #include <config.h>
      29             : 
      30             : #include "modechange.h"
      31             : #include <sys/stat.h>
      32             : #include "stat-macros.h"
      33             : #include "xalloc.h"
      34             : #include <stdlib.h>
      35             : 
      36             : /* The traditional octal values corresponding to each mode bit.  */
      37             : #define SUID 04000
      38             : #define SGID 02000
      39             : #define SVTX 01000
      40             : #define RUSR 00400
      41             : #define WUSR 00200
      42             : #define XUSR 00100
      43             : #define RGRP 00040
      44             : #define WGRP 00020
      45             : #define XGRP 00010
      46             : #define ROTH 00004
      47             : #define WOTH 00002
      48             : #define XOTH 00001
      49             : #define ALLM 07777 /* all octal mode bits */
      50             : 
      51             : /* Convert OCTAL, which uses one of the traditional octal values, to
      52             :    an internal mode_t value.  */
      53             : static mode_t
      54          20 : octal_to_mode (unsigned int octal)
      55             : {
      56             :   /* Help the compiler optimize the usual case where mode_t uses
      57             :      the traditional octal representation.  */
      58             :   return ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
      59             :            && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
      60             :            && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
      61             :            && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
      62             :           ? octal
      63          20 :           : (mode_t) ((octal & SUID ? S_ISUID : 0)
      64             :                       | (octal & SGID ? S_ISGID : 0)
      65             :                       | (octal & SVTX ? S_ISVTX : 0)
      66             :                       | (octal & RUSR ? S_IRUSR : 0)
      67             :                       | (octal & WUSR ? S_IWUSR : 0)
      68             :                       | (octal & XUSR ? S_IXUSR : 0)
      69             :                       | (octal & RGRP ? S_IRGRP : 0)
      70             :                       | (octal & WGRP ? S_IWGRP : 0)
      71             :                       | (octal & XGRP ? S_IXGRP : 0)
      72             :                       | (octal & ROTH ? S_IROTH : 0)
      73             :                       | (octal & WOTH ? S_IWOTH : 0)
      74             :                       | (octal & XOTH ? S_IXOTH : 0)));
      75             : }
      76             : 
      77             : /* Special operations flags.  */
      78             : enum
      79             :   {
      80             :     /* For the sentinel at the end of the mode changes array.  */
      81             :     MODE_DONE,
      82             : 
      83             :     /* The typical case.  */
      84             :     MODE_ORDINARY_CHANGE,
      85             : 
      86             :     /* In addition to the typical case, affect the execute bits if at
      87             :        least one execute bit is set already, or if the file is a
      88             :        directory.  */
      89             :     MODE_X_IF_ANY_X,
      90             : 
      91             :     /* Instead of the typical case, copy some existing permissions for
      92             :        u, g, or o onto the other two.  Which of u, g, or o is copied
      93             :        is determined by which bits are set in the `value' field.  */
      94             :     MODE_COPY_EXISTING
      95             :   };
      96             : 
      97             : /* Description of a mode change.  */
      98             : struct mode_change
      99             : {
     100             :   char op;                      /* One of "=+-".  */
     101             :   char flag;                    /* Special operations flag.  */
     102             :   mode_t affected;              /* Set for u, g, o, or a.  */
     103             :   mode_t value;                 /* Bits to add/remove.  */
     104             :   mode_t mentioned;             /* Bits explicitly mentioned.  */
     105             : };
     106             : 
     107             : /* Return a mode_change array with the specified `=ddd'-style
     108             :    mode change operation, where NEW_MODE is `ddd' and MENTIONED
     109             :    contains the bits explicitly mentioned in the mode are MENTIONED.  */
     110             : 
     111             : static struct mode_change *
     112          21 : make_node_op_equals (mode_t new_mode, mode_t mentioned)
     113             : {
     114          21 :   struct mode_change *p = xmalloc (2 * sizeof *p);
     115          21 :   p->op = '=';
     116          21 :   p->flag = MODE_ORDINARY_CHANGE;
     117          21 :   p->affected = CHMOD_MODE_BITS;
     118          21 :   p->value = new_mode;
     119          21 :   p->mentioned = mentioned;
     120          21 :   p[1].flag = MODE_DONE;
     121          21 :   return p;
     122             : }
     123             : 
     124             : /* Return a pointer to an array of file mode change operations created from
     125             :    MODE_STRING, an ASCII string that contains either an octal number
     126             :    specifying an absolute mode, or symbolic mode change operations with
     127             :    the form:
     128             :    [ugoa...][[+-=][rwxXstugo...]...][,...]
     129             : 
     130             :    Return NULL if `mode_string' does not contain a valid
     131             :    representation of file mode change operations.  */
     132             : 
     133             : struct mode_change *
     134         195 : mode_compile (char const *mode_string)
     135             : {
     136             :   /* The array of mode-change directives to be returned.  */
     137             :   struct mode_change *mc;
     138         195 :   size_t used = 0;
     139             : 
     140         195 :   if ('0' <= *mode_string && *mode_string < '8')
     141             :     {
     142          41 :       unsigned int octal_mode = 0;
     143             :       mode_t mode;
     144             :       mode_t mentioned;
     145             : 
     146             :       do
     147             :         {
     148         100 :           octal_mode = 8 * octal_mode + *mode_string++ - '0';
     149         100 :           if (ALLM < octal_mode)
     150           7 :             return NULL;
     151             :         }
     152          93 :       while ('0' <= *mode_string && *mode_string < '8');
     153             : 
     154          34 :       if (*mode_string)
     155          14 :         return NULL;
     156             : 
     157          20 :       mode = octal_to_mode (octal_mode);
     158          20 :       mentioned = (mode & (S_ISUID | S_ISGID)) | S_ISVTX | S_IRWXUGO;
     159          20 :       return make_node_op_equals (mode, mentioned);
     160             :     }
     161             : 
     162             :   /* Allocate enough space to hold the result.  */
     163             :   {
     164         154 :     size_t needed = 1;
     165             :     char const *p;
     166         392 :     for (p = mode_string; *p; p++)
     167         238 :       needed += (*p == '=' || *p == '+' || *p == '-');
     168         154 :     mc = xnmalloc (needed, sizeof *mc);
     169             :   }
     170             : 
     171             :   /* One loop iteration for each `[ugoa]*([-+=]([rwxXst]*|[ugo]))+'.  */
     172           5 :   for (;; mode_string++)
     173           5 :     {
     174             :       /* Which bits in the mode are operated on.  */
     175         159 :       mode_t affected = 0;
     176             : 
     177             :       /* Turn on all the bits in `affected' for each group given.  */
     178          22 :       for (;; mode_string++)
     179         203 :         switch (*mode_string)
     180             :           {
     181          28 :           default:
     182          28 :             goto invalid;
     183           7 :           case 'u':
     184           7 :             affected |= S_ISUID | S_IRWXU;
     185           7 :             break;
     186           5 :           case 'g':
     187           5 :             affected |= S_ISGID | S_IRWXG;
     188           5 :             break;
     189           5 :           case 'o':
     190           5 :             affected |= S_ISVTX | S_IRWXO;
     191           5 :             break;
     192           5 :           case 'a':
     193           5 :             affected |= CHMOD_MODE_BITS;
     194           5 :             break;
     195         131 :           case '=': case '+': case '-':
     196         131 :             goto no_more_affected;
     197             :           }
     198          22 :     no_more_affected:;
     199             : 
     200             :       do
     201             :         {
     202         153 :           char op = *mode_string++;
     203             :           mode_t value;
     204         153 :           char flag = MODE_COPY_EXISTING;
     205             :           struct mode_change *change;
     206             : 
     207         153 :           switch (*mode_string++)
     208             :             {
     209           4 :             case 'u':
     210             :               /* Set the affected bits to the value of the `u' bits
     211             :                  on the same file.  */
     212           4 :               value = S_IRWXU;
     213           4 :               break;
     214           6 :             case 'g':
     215             :               /* Set the affected bits to the value of the `g' bits
     216             :                  on the same file.  */
     217           6 :               value = S_IRWXG;
     218           6 :               break;
     219           5 :             case 'o':
     220             :               /* Set the affected bits to the value of the `o' bits
     221             :                  on the same file.  */
     222           5 :               value = S_IRWXO;
     223           5 :               break;
     224             : 
     225         138 :             default:
     226         138 :               value = 0;
     227         138 :               flag = MODE_ORDINARY_CHANGE;
     228             : 
     229         168 :               for (mode_string--;; mode_string++)
     230         198 :                 switch (*mode_string)
     231             :                   {
     232           5 :                   case 'r':
     233           5 :                     value |= S_IRUSR | S_IRGRP | S_IROTH;
     234           5 :                     break;
     235           5 :                   case 'w':
     236           5 :                     value |= S_IWUSR | S_IWGRP | S_IWOTH;
     237           5 :                     break;
     238           5 :                   case 'x':
     239           5 :                     value |= S_IXUSR | S_IXGRP | S_IXOTH;
     240           5 :                     break;
     241           5 :                   case 'X':
     242           5 :                     flag = MODE_X_IF_ANY_X;
     243           5 :                     break;
     244           5 :                   case 's':
     245             :                     /* Set the setuid/gid bits if `u' or `g' is selected.  */
     246           5 :                     value |= S_ISUID | S_ISGID;
     247           5 :                     break;
     248           5 :                   case 't':
     249             :                     /* Set the "save text image" bit if `o' is selected.  */
     250           5 :                     value |= S_ISVTX;
     251           5 :                     break;
     252         138 :                   default:
     253         138 :                     goto no_more_values;
     254             :                   }
     255         153 :             no_more_values:;
     256             :             }
     257             : 
     258         153 :           change = &mc[used++];
     259         153 :           change->op = op;
     260         153 :           change->flag = flag;
     261         153 :           change->affected = affected;
     262         153 :           change->value = value;
     263         153 :           change->mentioned = (affected ? affected & value : value);
     264             :         }
     265         295 :       while (*mode_string == '=' || *mode_string == '+'
     266         290 :              || *mode_string == '-');
     267             : 
     268         131 :       if (*mode_string != ',')
     269         126 :         break;
     270             :     }
     271             : 
     272         126 :   if (*mode_string == 0)
     273             :     {
     274         117 :       mc[used].flag = MODE_DONE;
     275         117 :       return mc;
     276             :     }
     277             : 
     278           9 : invalid:
     279          37 :   free (mc);
     280          37 :   return NULL;
     281             : }
     282             : 
     283             : /* Return a file mode change operation that sets permissions to match those
     284             :    of REF_FILE.  Return NULL (setting errno) if REF_FILE can't be accessed.  */
     285             : 
     286             : struct mode_change *
     287           2 : mode_create_from_ref (const char *ref_file)
     288             : {
     289             :   struct stat ref_stats;
     290             : 
     291           2 :   if (stat (ref_file, &ref_stats) != 0)
     292           1 :     return NULL;
     293           1 :   return make_node_op_equals (ref_stats.st_mode, CHMOD_MODE_BITS);
     294             : }
     295             : 
     296             : /* Return the file mode bits of OLDMODE (which is the mode of a
     297             :    directory if DIR), assuming the umask is UMASK_VALUE, adjusted as
     298             :    indicated by the list of change operations CHANGES.  If DIR, the
     299             :    type 'X' change affects the returned value even if no execute bits
     300             :    were set in OLDMODE, and set user and group ID bits are preserved
     301             :    unless CHANGES mentioned them.  If PMODE_BITS is not null, store into
     302             :    *PMODE_BITS a mask denoting file mode bits that are affected by
     303             :    CHANGES.
     304             : 
     305             :    The returned value and *PMODE_BITS contain only file mode bits.
     306             :    For example, they have the S_IFMT bits cleared on a standard
     307             :    Unix-like host.  */
     308             : 
     309             : mode_t
     310      266904 : mode_adjust (mode_t oldmode, bool dir, mode_t umask_value,
     311             :              struct mode_change const *changes, mode_t *pmode_bits)
     312             : {
     313             :   /* The adjusted mode.  */
     314      266904 :   mode_t newmode = oldmode & CHMOD_MODE_BITS;
     315             : 
     316             :   /* File mode bits that CHANGES cares about.  */
     317      266904 :   mode_t mode_bits = 0;
     318             : 
     319      533831 :   for (; changes->flag != MODE_DONE; changes++)
     320             :     {
     321      266927 :       mode_t affected = changes->affected;
     322      266927 :       mode_t omit_change =
     323      266927 :         (dir ? S_ISUID | S_ISGID : 0) & ~ changes->mentioned;
     324      266927 :       mode_t value = changes->value;
     325             : 
     326      266927 :       switch (changes->flag)
     327             :         {
     328      266904 :         case MODE_ORDINARY_CHANGE:
     329      266904 :           break;
     330             : 
     331          16 :         case MODE_COPY_EXISTING:
     332             :           /* Isolate in `value' the bits in `newmode' to copy.  */
     333          16 :           value &= newmode;
     334             : 
     335             :           /* Copy the isolated bits to the other two parts.  */
     336          32 :           value |= ((value & (S_IRUSR | S_IRGRP | S_IROTH)
     337          16 :                      ? S_IRUSR | S_IRGRP | S_IROTH : 0)
     338          32 :                     | (value & (S_IWUSR | S_IWGRP | S_IWOTH)
     339          16 :                        ? S_IWUSR | S_IWGRP | S_IWOTH : 0)
     340          32 :                     | (value & (S_IXUSR | S_IXGRP | S_IXOTH)
     341          16 :                        ? S_IXUSR | S_IXGRP | S_IXOTH : 0));
     342          16 :           break;
     343             : 
     344           7 :         case MODE_X_IF_ANY_X:
     345             :           /* Affect the execute bits if execute bits are already set
     346             :              or if the file is a directory.  */
     347           7 :           if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) | dir)
     348           4 :             value |= S_IXUSR | S_IXGRP | S_IXOTH;
     349           7 :           break;
     350             :         }
     351             : 
     352             :       /* If WHO was specified, limit the change to the affected bits.
     353             :          Otherwise, apply the umask.  Either way, omit changes as
     354             :          requested.  */
     355      266927 :       value &= (affected ? affected : ~umask_value) & ~ omit_change;
     356             : 
     357      266927 :       switch (changes->op)
     358             :         {
     359          58 :         case '=':
     360             :           /* If WHO was specified, preserve the previous values of
     361             :              bits that are not affected by this change operation.
     362             :              Otherwise, clear all the bits.  */
     363             :           {
     364          58 :             mode_t preserved = (affected ? ~affected : 0) | omit_change;
     365          58 :             mode_bits |= CHMOD_MODE_BITS & ~preserved;
     366          58 :             newmode = (newmode & preserved) | value;
     367          58 :             break;
     368             :           }
     369             : 
     370          61 :         case '+':
     371          61 :           mode_bits |= value;
     372          61 :           newmode |= value;
     373          61 :           break;
     374             : 
     375      266808 :         case '-':
     376      266808 :           mode_bits |= value;
     377      266808 :           newmode &= ~value;
     378      266808 :           break;
     379             :         }
     380             :     }
     381             : 
     382      266904 :   if (pmode_bits)
     383          39 :     *pmode_bits = mode_bits;
     384      266904 :   return newmode;
     385             : }

Generated by: LCOV version 1.10