/* modechange.c -- file mode manipulation
- Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001, 2003, 2004, 2005,
- 2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-1990, 1997-1999, 2001, 2003-2006, 2009-2013 Free Software
+ Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/* Written by David MacKenzie <djm@ai.mit.edu> */
-/* The ASCII mode string is compiled into an array of `struct
+/* The ASCII mode string is compiled into an array of 'struct
modechange', which can then be applied to each file to be changed.
We do this instead of re-parsing the ASCII string for each file
because the compiled form requires less computation to use; when
/* Help the compiler optimize the usual case where mode_t uses
the traditional octal representation. */
return ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
- && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
- && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
- && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
- ? octal
- : (mode_t) ((octal & SUID ? S_ISUID : 0)
- | (octal & SGID ? S_ISGID : 0)
- | (octal & SVTX ? S_ISVTX : 0)
- | (octal & RUSR ? S_IRUSR : 0)
- | (octal & WUSR ? S_IWUSR : 0)
- | (octal & XUSR ? S_IXUSR : 0)
- | (octal & RGRP ? S_IRGRP : 0)
- | (octal & WGRP ? S_IWGRP : 0)
- | (octal & XGRP ? S_IXGRP : 0)
- | (octal & ROTH ? S_IROTH : 0)
- | (octal & WOTH ? S_IWOTH : 0)
- | (octal & XOTH ? S_IXOTH : 0)));
+ && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
+ && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
+ && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
+ ? octal
+ : (mode_t) ((octal & SUID ? S_ISUID : 0)
+ | (octal & SGID ? S_ISGID : 0)
+ | (octal & SVTX ? S_ISVTX : 0)
+ | (octal & RUSR ? S_IRUSR : 0)
+ | (octal & WUSR ? S_IWUSR : 0)
+ | (octal & XUSR ? S_IXUSR : 0)
+ | (octal & RGRP ? S_IRGRP : 0)
+ | (octal & WGRP ? S_IWGRP : 0)
+ | (octal & XGRP ? S_IXGRP : 0)
+ | (octal & ROTH ? S_IROTH : 0)
+ | (octal & WOTH ? S_IWOTH : 0)
+ | (octal & XOTH ? S_IXOTH : 0)));
}
/* Special operations flags. */
/* Instead of the typical case, copy some existing permissions for
u, g, or o onto the other two. Which of u, g, or o is copied
- is determined by which bits are set in the `value' field. */
+ is determined by which bits are set in the 'value' field. */
MODE_COPY_EXISTING
};
/* Description of a mode change. */
struct mode_change
{
- char op; /* One of "=+-". */
- char flag; /* Special operations flag. */
- mode_t affected; /* Set for u, g, o, or a. */
- mode_t value; /* Bits to add/remove. */
- mode_t mentioned; /* Bits explicitly mentioned. */
+ char op; /* One of "=+-". */
+ char flag; /* Special operations flag. */
+ mode_t affected; /* Set for u, g, o, or a. */
+ mode_t value; /* Bits to add/remove. */
+ mode_t mentioned; /* Bits explicitly mentioned. */
};
-/* Return a mode_change array with the specified `=ddd'-style
- mode change operation, where NEW_MODE is `ddd' and MENTIONED
+/* Return a mode_change array with the specified "=ddd"-style
+ mode change operation, where NEW_MODE is "ddd" and MENTIONED
contains the bits explicitly mentioned in the mode are MENTIONED. */
static struct mode_change *
the form:
[ugoa...][[+-=][rwxXstugo...]...][,...]
- Return NULL if `mode_string' does not contain a valid
+ Return NULL if 'mode_string' does not contain a valid
representation of file mode change operations. */
struct mode_change *
/* The array of mode-change directives to be returned. */
struct mode_change *mc;
size_t used = 0;
+ char const *p;
if ('0' <= *mode_string && *mode_string < '8')
{
mode_t mode;
mode_t mentioned;
+ p = mode_string;
do
- {
- octal_mode = 8 * octal_mode + *mode_string++ - '0';
- if (ALLM < octal_mode)
- return NULL;
- }
- while ('0' <= *mode_string && *mode_string < '8');
+ {
+ octal_mode = 8 * octal_mode + *p++ - '0';
+ if (ALLM < octal_mode)
+ return NULL;
+ }
+ while ('0' <= *p && *p < '8');
- if (*mode_string)
- return NULL;
+ if (*p)
+ return NULL;
mode = octal_to_mode (octal_mode);
- mentioned = (mode & (S_ISUID | S_ISGID)) | S_ISVTX | S_IRWXUGO;
+ mentioned = (p - mode_string < 5
+ ? (mode & (S_ISUID | S_ISGID)) | S_ISVTX | S_IRWXUGO
+ : CHMOD_MODE_BITS);
return make_node_op_equals (mode, mentioned);
}
/* Allocate enough space to hold the result. */
{
size_t needed = 1;
- char const *p;
for (p = mode_string; *p; p++)
needed += (*p == '=' || *p == '+' || *p == '-');
mc = xnmalloc (needed, sizeof *mc);
}
- /* One loop iteration for each `[ugoa]*([-+=]([rwxXst]*|[ugo]))+'. */
- for (;; mode_string++)
+ /* One loop iteration for each
+ '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+'. */
+ for (p = mode_string; ; p++)
{
/* Which bits in the mode are operated on. */
mode_t affected = 0;
- /* Turn on all the bits in `affected' for each group given. */
- for (;; mode_string++)
- switch (*mode_string)
- {
- default:
- goto invalid;
- case 'u':
- affected |= S_ISUID | S_IRWXU;
- break;
- case 'g':
- affected |= S_ISGID | S_IRWXG;
- break;
- case 'o':
- affected |= S_ISVTX | S_IRWXO;
- break;
- case 'a':
- affected |= CHMOD_MODE_BITS;
- break;
- case '=': case '+': case '-':
- goto no_more_affected;
- }
+ /* Turn on all the bits in 'affected' for each group given. */
+ for (;; p++)
+ switch (*p)
+ {
+ default:
+ goto invalid;
+ case 'u':
+ affected |= S_ISUID | S_IRWXU;
+ break;
+ case 'g':
+ affected |= S_ISGID | S_IRWXG;
+ break;
+ case 'o':
+ affected |= S_ISVTX | S_IRWXO;
+ break;
+ case 'a':
+ affected |= CHMOD_MODE_BITS;
+ break;
+ case '=': case '+': case '-':
+ goto no_more_affected;
+ }
no_more_affected:;
do
- {
- char op = *mode_string++;
- mode_t value;
- char flag = MODE_COPY_EXISTING;
- struct mode_change *change;
-
- switch (*mode_string++)
- {
- case 'u':
- /* Set the affected bits to the value of the `u' bits
- on the same file. */
- value = S_IRWXU;
- break;
- case 'g':
- /* Set the affected bits to the value of the `g' bits
- on the same file. */
- value = S_IRWXG;
- break;
- case 'o':
- /* Set the affected bits to the value of the `o' bits
- on the same file. */
- value = S_IRWXO;
- break;
-
- default:
- value = 0;
- flag = MODE_ORDINARY_CHANGE;
-
- for (mode_string--;; mode_string++)
- switch (*mode_string)
- {
- case 'r':
- value |= S_IRUSR | S_IRGRP | S_IROTH;
- break;
- case 'w':
- value |= S_IWUSR | S_IWGRP | S_IWOTH;
- break;
- case 'x':
- value |= S_IXUSR | S_IXGRP | S_IXOTH;
- break;
- case 'X':
- flag = MODE_X_IF_ANY_X;
- break;
- case 's':
- /* Set the setuid/gid bits if `u' or `g' is selected. */
- value |= S_ISUID | S_ISGID;
- break;
- case 't':
- /* Set the "save text image" bit if `o' is selected. */
- value |= S_ISVTX;
- break;
- default:
- goto no_more_values;
- }
- no_more_values:;
- }
-
- change = &mc[used++];
- change->op = op;
- change->flag = flag;
- change->affected = affected;
- change->value = value;
- change->mentioned = (affected ? affected & value : value);
- }
- while (*mode_string == '=' || *mode_string == '+'
- || *mode_string == '-');
-
- if (*mode_string != ',')
- break;
+ {
+ char op = *p++;
+ mode_t value;
+ mode_t mentioned = 0;
+ char flag = MODE_COPY_EXISTING;
+ struct mode_change *change;
+
+ switch (*p)
+ {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ {
+ unsigned int octal_mode = 0;
+
+ do
+ {
+ octal_mode = 8 * octal_mode + *p++ - '0';
+ if (ALLM < octal_mode)
+ return NULL;
+ }
+ while ('0' <= *p && *p < '8');
+
+ if (affected || (*p && *p != ','))
+ return NULL;
+ affected = mentioned = CHMOD_MODE_BITS;
+ value = octal_to_mode (octal_mode);
+ flag = MODE_ORDINARY_CHANGE;
+ break;
+ }
+
+ case 'u':
+ /* Set the affected bits to the value of the "u" bits
+ on the same file. */
+ value = S_IRWXU;
+ p++;
+ break;
+ case 'g':
+ /* Set the affected bits to the value of the "g" bits
+ on the same file. */
+ value = S_IRWXG;
+ p++;
+ break;
+ case 'o':
+ /* Set the affected bits to the value of the "o" bits
+ on the same file. */
+ value = S_IRWXO;
+ p++;
+ break;
+
+ default:
+ value = 0;
+ flag = MODE_ORDINARY_CHANGE;
+
+ for (;; p++)
+ switch (*p)
+ {
+ case 'r':
+ value |= S_IRUSR | S_IRGRP | S_IROTH;
+ break;
+ case 'w':
+ value |= S_IWUSR | S_IWGRP | S_IWOTH;
+ break;
+ case 'x':
+ value |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case 'X':
+ flag = MODE_X_IF_ANY_X;
+ break;
+ case 's':
+ /* Set the setuid/gid bits if 'u' or 'g' is selected. */
+ value |= S_ISUID | S_ISGID;
+ break;
+ case 't':
+ /* Set the "save text image" bit if 'o' is selected. */
+ value |= S_ISVTX;
+ break;
+ default:
+ goto no_more_values;
+ }
+ no_more_values:;
+ }
+
+ change = &mc[used++];
+ change->op = op;
+ change->flag = flag;
+ change->affected = affected;
+ change->value = value;
+ change->mentioned =
+ (mentioned ? mentioned : affected ? affected & value : value);
+ }
+ while (*p == '=' || *p == '+' || *p == '-');
+
+ if (*p != ',')
+ break;
}
- if (*mode_string == 0)
+ if (*p == 0)
{
mc[used].flag = MODE_DONE;
return mc;
mode_t
mode_adjust (mode_t oldmode, bool dir, mode_t umask_value,
- struct mode_change const *changes, mode_t *pmode_bits)
+ struct mode_change const *changes, mode_t *pmode_bits)
{
/* The adjusted mode. */
mode_t newmode = oldmode & CHMOD_MODE_BITS;
{
mode_t affected = changes->affected;
mode_t omit_change =
- (dir ? S_ISUID | S_ISGID : 0) & ~ changes->mentioned;
+ (dir ? S_ISUID | S_ISGID : 0) & ~ changes->mentioned;
mode_t value = changes->value;
switch (changes->flag)
- {
- case MODE_ORDINARY_CHANGE:
- break;
-
- case MODE_COPY_EXISTING:
- /* Isolate in `value' the bits in `newmode' to copy. */
- value &= newmode;
-
- /* Copy the isolated bits to the other two parts. */
- value |= ((value & (S_IRUSR | S_IRGRP | S_IROTH)
- ? S_IRUSR | S_IRGRP | S_IROTH : 0)
- | (value & (S_IWUSR | S_IWGRP | S_IWOTH)
- ? S_IWUSR | S_IWGRP | S_IWOTH : 0)
- | (value & (S_IXUSR | S_IXGRP | S_IXOTH)
- ? S_IXUSR | S_IXGRP | S_IXOTH : 0));
- break;
-
- case MODE_X_IF_ANY_X:
- /* Affect the execute bits if execute bits are already set
- or if the file is a directory. */
- if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) | dir)
- value |= S_IXUSR | S_IXGRP | S_IXOTH;
- break;
- }
+ {
+ case MODE_ORDINARY_CHANGE:
+ break;
+
+ case MODE_COPY_EXISTING:
+ /* Isolate in 'value' the bits in 'newmode' to copy. */
+ value &= newmode;
+
+ /* Copy the isolated bits to the other two parts. */
+ value |= ((value & (S_IRUSR | S_IRGRP | S_IROTH)
+ ? S_IRUSR | S_IRGRP | S_IROTH : 0)
+ | (value & (S_IWUSR | S_IWGRP | S_IWOTH)
+ ? S_IWUSR | S_IWGRP | S_IWOTH : 0)
+ | (value & (S_IXUSR | S_IXGRP | S_IXOTH)
+ ? S_IXUSR | S_IXGRP | S_IXOTH : 0));
+ break;
+
+ case MODE_X_IF_ANY_X:
+ /* Affect the execute bits if execute bits are already set
+ or if the file is a directory. */
+ if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) | dir)
+ value |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ }
/* If WHO was specified, limit the change to the affected bits.
- Otherwise, apply the umask. Either way, omit changes as
- requested. */
+ Otherwise, apply the umask. Either way, omit changes as
+ requested. */
value &= (affected ? affected : ~umask_value) & ~ omit_change;
switch (changes->op)
- {
- case '=':
- /* If WHO was specified, preserve the previous values of
- bits that are not affected by this change operation.
- Otherwise, clear all the bits. */
- {
- mode_t preserved = (affected ? ~affected : 0) | omit_change;
- mode_bits |= CHMOD_MODE_BITS & ~preserved;
- newmode = (newmode & preserved) | value;
- break;
- }
-
- case '+':
- mode_bits |= value;
- newmode |= value;
- break;
-
- case '-':
- mode_bits |= value;
- newmode &= ~value;
- break;
- }
+ {
+ case '=':
+ /* If WHO was specified, preserve the previous values of
+ bits that are not affected by this change operation.
+ Otherwise, clear all the bits. */
+ {
+ mode_t preserved = (affected ? ~affected : 0) | omit_change;
+ mode_bits |= CHMOD_MODE_BITS & ~preserved;
+ newmode = (newmode & preserved) | value;
+ break;
+ }
+
+ case '+':
+ mode_bits |= value;
+ newmode |= value;
+ break;
+
+ case '-':
+ mode_bits |= value;
+ newmode &= ~value;
+ break;
+ }
}
if (pmode_bits)