X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fmodechange.c;h=b4df3388ceba2b38eec2fdf8b4ed5eb2b8000609;hb=ec354b13bec21d8510dd60e1e129cc194809248b;hp=8eb8dc55db11c099c4d1f80dd15562bb758c4ab8;hpb=300424a2d37b17d304be2783ed40f2efa4d950c0;p=gnulib.git diff --git a/lib/modechange.c b/lib/modechange.c index 8eb8dc55d..b4df3388c 100644 --- a/lib/modechange.c +++ b/lib/modechange.c @@ -1,5 +1,7 @@ /* modechange.c -- file mode manipulation - Copyright (C) 1989, 1990 Free Software Foundation, Inc. + + Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001, 2003, 2004 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 @@ -12,8 +14,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by David MacKenzie */ @@ -24,38 +26,128 @@ changing the mode of many files, this probably results in a performance gain. */ -#ifdef HAVE_CONFIG_H -#include +#if HAVE_CONFIG_H +# include #endif -#include -#include #include "modechange.h" - -#ifdef STDC_HEADERS +#include +#include "xstrtol.h" +#include +#include #include -#else -char *malloc (); -#endif -#ifndef NULL -#define NULL 0 +#if STAT_MACROS_BROKEN +# undef S_ISDIR #endif -#ifdef STAT_MACROS_BROKEN -#undef S_ISDIR -#endif /* STAT_MACROS_BROKEN. */ - #if !defined(S_ISDIR) && defined(S_IFDIR) -#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* The traditional octal values corresponding to each mode bit. */ +#define SUID 04000 +#define SGID 02000 +#define SVTX 01000 +#define RUSR 00400 +#define WUSR 00200 +#define XUSR 00100 +#define RGRP 00040 +#define WGRP 00020 +#define XGRP 00010 +#define ROTH 00004 +#define WOTH 00002 +#define XOTH 00001 +#define ALLM 07777 /* all octal mode bits */ + +#ifndef S_ISUID +# define S_ISUID SUID +#endif +#ifndef S_ISGID +# define S_ISGID SGID +#endif +#ifndef S_ISVTX +# define S_ISVTX SVTX +#endif +#ifndef S_IRUSR +# define S_IRUSR RUSR +#endif +#ifndef S_IWUSR +# define S_IWUSR WUSR +#endif +#ifndef S_IXUSR +# define S_IXUSR XUSR +#endif +#ifndef S_IRGRP +# define S_IRGRP RGRP +#endif +#ifndef S_IWGRP +# define S_IWGRP WGRP +#endif +#ifndef S_IXGRP +# define S_IXGRP XGRP +#endif +#ifndef S_IROTH +# define S_IROTH ROTH +#endif +#ifndef S_IWOTH +# define S_IWOTH WOTH #endif +#ifndef S_IXOTH +# define S_IXOTH XOTH +#endif +#ifndef S_IRWXU +# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +#endif +#ifndef S_IRWXG +# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) +#endif +#ifndef S_IRWXO +# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) +#endif + +/* All the mode bits that can be affected by chmod. */ +#define CHMOD_MODE_BITS \ + (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) /* Return newly allocated memory to hold one element of type TYPE. */ #define talloc(type) ((type *) malloc (sizeof (type))) -#define isodigit(c) ((c) >= '0' && (c) <= '7') +/* Create a mode_change entry with the specified `=ddd'-style + mode change operation, where NEW_MODE is `ddd'. Return the + new entry, or NULL upon failure. */ + +static struct mode_change * +make_node_op_equals (mode_t new_mode) +{ + struct mode_change *p; + p = talloc (struct mode_change); + if (p == NULL) + return p; + p->next = NULL; + p->op = '='; + p->flags = 0; + p->value = new_mode; + p->affected = CHMOD_MODE_BITS; /* Affect all permissions. */ + return p; +} + +/* Append entry E to the end of the link list with the specified + HEAD and TAIL. */ -static int oatoi (); +static void +mode_append_entry (struct mode_change **head, + struct mode_change **tail, + struct mode_change *e) +{ + if (*head == NULL) + *head = *tail = e; + else + { + (*tail)->next = e; + *tail = e; + } +} /* Return a linked list of file mode change operations created from MODE_STRING, an ASCII string that contains either an octal number @@ -71,63 +163,83 @@ static int oatoi (); return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */ struct mode_change * -mode_compile (mode_string, masked_ops) - register char *mode_string; - unsigned masked_ops; +mode_compile (const char *mode_string, unsigned int masked_ops) { struct mode_change *head; /* First element of the linked list. */ - struct mode_change *change; /* An element of the linked list. */ - int i; /* General purpose temporary. */ - int umask_value; /* The umask value (surprise). */ - unsigned short affected_bits; /* Which bits in the mode are operated on. */ - unsigned short affected_masked; /* `affected_bits' modified by umask. */ - unsigned ops_to_mask; /* Operators to actually use umask on. */ - - i = oatoi (mode_string); - if (i >= 0) + struct mode_change *tail; /* An element of the linked list. */ + unsigned long octal_value; /* The mode value, if octal. */ + mode_t umask_value; /* The umask value (surprise). */ + + head = NULL; +#ifdef lint + tail = NULL; +#endif + + if (xstrtoul (mode_string, NULL, 8, &octal_value, "") == LONGINT_OK) { - if (i > 07777) + struct mode_change *p; + mode_t mode; + if (octal_value != (octal_value & ALLM)) return MODE_INVALID; - head = talloc (struct mode_change); - if (head == NULL) + + /* Help the compiler optimize the usual case where mode_t uses + the traditional octal representation. */ + mode = ((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_value + : (mode_t) ((octal_value & SUID ? S_ISUID : 0) + | (octal_value & SGID ? S_ISGID : 0) + | (octal_value & SVTX ? S_ISVTX : 0) + | (octal_value & RUSR ? S_IRUSR : 0) + | (octal_value & WUSR ? S_IWUSR : 0) + | (octal_value & XUSR ? S_IXUSR : 0) + | (octal_value & RGRP ? S_IRGRP : 0) + | (octal_value & WGRP ? S_IWGRP : 0) + | (octal_value & XGRP ? S_IXGRP : 0) + | (octal_value & ROTH ? S_IROTH : 0) + | (octal_value & WOTH ? S_IWOTH : 0) + | (octal_value & XOTH ? S_IXOTH : 0))); + + p = make_node_op_equals (mode); + if (p == NULL) return MODE_MEMORY_EXHAUSTED; - head->next = NULL; - head->op = '='; - head->flags = 0; - head->value = i; - head->affected = 07777; /* Affect all permissions. */ + mode_append_entry (&head, &tail, p); return head; } umask_value = umask (0); umask (umask_value); /* Restore the old value. */ - - head = NULL; -#ifdef lint - change = NULL; -#endif --mode_string; /* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */ do { - affected_bits = 0; - ops_to_mask = 0; + /* Which bits in the mode are operated on. */ + mode_t affected_bits = 0; + /* `affected_bits' modified by umask. */ + mode_t affected_masked; + /* Operators to actually use umask on. */ + unsigned int ops_to_mask = 0; + + bool who_specified_p; + /* Turn on all the bits in `affected_bits' for each group given. */ for (++mode_string;; ++mode_string) switch (*mode_string) { case 'u': - affected_bits |= 04700; + affected_bits |= S_ISUID | S_IRWXU; break; case 'g': - affected_bits |= 02070; + affected_bits |= S_ISGID | S_IRWXG; break; case 'o': - affected_bits |= 01007; + affected_bits |= S_ISVTX | S_IRWXO; break; case 'a': - affected_bits |= 07777; + affected_bits |= CHMOD_MODE_BITS; break; default: goto no_more_affected; @@ -136,37 +248,39 @@ mode_compile (mode_string, masked_ops) no_more_affected: /* If none specified, affect all bits, except perhaps those set in the umask. */ - if (affected_bits == 0) + if (affected_bits) + who_specified_p = true; + else { - affected_bits = 07777; + who_specified_p = false; + affected_bits = CHMOD_MODE_BITS; ops_to_mask = masked_ops; } while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-') { - /* Add the element to the tail of the list, so the operations - are performed in the correct order. */ - if (head == NULL) - { - head = talloc (struct mode_change); - if (head == NULL) - return MODE_MEMORY_EXHAUSTED; - change = head; - } - else + struct mode_change *change = talloc (struct mode_change); + if (change == NULL) { - change->next = talloc (struct mode_change); - if (change->next == NULL) - { - mode_free (change); - return MODE_MEMORY_EXHAUSTED; - } - change = change->next; + mode_free (head); + return MODE_MEMORY_EXHAUSTED; } change->next = NULL; change->op = *mode_string; /* One of "=+-". */ affected_masked = affected_bits; + + /* Per the Single Unix Spec, if `who' is not specified and the + `=' operator is used, then clear all the bits first. */ + if (!who_specified_p && + ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS : 0)) + { + struct mode_change *p = make_node_op_equals (0); + if (p == NULL) + return MODE_MEMORY_EXHAUSTED; + mode_append_entry (&head, &tail, p); + } + if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS : *mode_string == '+' ? MODE_MASK_PLUS : MODE_MASK_MINUS)) @@ -175,36 +289,43 @@ mode_compile (mode_string, masked_ops) change->value = 0; change->flags = 0; + /* Add the element to the tail of the list, so the operations + are performed in the correct order. */ + mode_append_entry (&head, &tail, change); + /* Set `value' according to the bits set in `affected_masked'. */ for (++mode_string;; ++mode_string) switch (*mode_string) { case 'r': - change->value |= 00444 & affected_masked; + change->value |= ((S_IRUSR | S_IRGRP | S_IROTH) + & affected_masked); break; case 'w': - change->value |= 00222 & affected_masked; + change->value |= ((S_IWUSR | S_IWGRP | S_IWOTH) + & affected_masked); break; case 'X': change->flags |= MODE_X_IF_ANY_X; /* Fall through. */ case 'x': - change->value |= 00111 & affected_masked; + change->value |= ((S_IXUSR | S_IXGRP | S_IXOTH) + & affected_masked); break; case 's': /* Set the setuid/gid bits if `u' or `g' is selected. */ - change->value |= 06000 & affected_masked; + change->value |= (S_ISUID | S_ISGID) & affected_masked; break; case 't': /* Set the "save text image" bit if `o' is selected. */ - change->value |= 01000 & affected_masked; + change->value |= S_ISVTX & affected_masked; break; case 'u': /* Set the affected bits to the value of the `u' bits on the same file. */ if (change->value) goto invalid; - change->value = 00700; + change->value = S_IRWXU; change->flags |= MODE_COPY_EXISTING; break; case 'g': @@ -212,7 +333,7 @@ mode_compile (mode_string, masked_ops) on the same file. */ if (change->value) goto invalid; - change->value = 00070; + change->value = S_IRWXG; change->flags |= MODE_COPY_EXISTING; break; case 'o': @@ -220,7 +341,7 @@ mode_compile (mode_string, masked_ops) on the same file. */ if (change->value) goto invalid; - change->value = 00007; + change->value = S_IRWXO; change->flags |= MODE_COPY_EXISTING; break; default: @@ -236,20 +357,44 @@ invalid: return MODE_INVALID; } +/* Return a file mode change operation that sets permissions to match those + of REF_FILE. Return MODE_BAD_REFERENCE if REF_FILE can't be accessed. */ + +struct mode_change * +mode_create_from_ref (const char *ref_file) +{ + struct mode_change *change; /* the only change element */ + struct stat ref_stats; + + if (stat (ref_file, &ref_stats)) + return MODE_BAD_REFERENCE; + + change = talloc (struct mode_change); + + if (change == NULL) + return MODE_MEMORY_EXHAUSTED; + + change->op = '='; + change->flags = 0; + change->affected = CHMOD_MODE_BITS; + change->value = ref_stats.st_mode; + change->next = NULL; + + return change; +} + /* Return file mode OLDMODE, adjusted as indicated by the list of change operations CHANGES. If OLDMODE is a directory, the type `X' change affects it even if no execute bits were set in OLDMODE. The returned value has the S_IFMT bits cleared. */ -unsigned short -mode_adjust (oldmode, changes) - unsigned oldmode; - register struct mode_change *changes; +mode_t +mode_adjust (mode_t oldmode, const struct mode_change *changes) { - unsigned short newmode; /* The adjusted mode and one operand. */ - unsigned short value; /* The other operand. */ + mode_t newmode; /* The adjusted mode and one operand. */ + mode_t value; /* The other operand. */ - newmode = oldmode & 07777; + newmode = oldmode & CHMOD_MODE_BITS; for (; changes; changes = changes->next) { @@ -259,19 +404,25 @@ mode_adjust (oldmode, changes) the mask `changes->value'. */ value = newmode & changes->value; - if (changes->value & 00700) + if (changes->value & S_IRWXU) /* Copy `u' permissions onto `g' and `o'. */ - value |= (value >> 3) | (value >> 6); - else if (changes->value & 00070) + value |= ( (value & S_IRUSR ? S_IRGRP | S_IROTH : 0) + | (value & S_IWUSR ? S_IWGRP | S_IWOTH : 0) + | (value & S_IXUSR ? S_IXGRP | S_IXOTH : 0)); + else if (changes->value & S_IRWXG) /* Copy `g' permissions onto `u' and `o'. */ - value |= (value << 3) | (value >> 3); + value |= ( (value & S_IRGRP ? S_IRUSR | S_IROTH : 0) + | (value & S_IWGRP ? S_IWUSR | S_IWOTH : 0) + | (value & S_IXGRP ? S_IXUSR | S_IXOTH : 0)); else /* Copy `o' permissions onto `u' and `g'. */ - value |= (value << 3) | (value << 6); + value |= ( (value & S_IROTH ? S_IRUSR | S_IRGRP : 0) + | (value & S_IWOTH ? S_IWUSR | S_IWGRP : 0) + | (value & S_IXOTH ? S_IXUSR | S_IXGRP : 0)); /* In order to change only `u', `g', or `o' permissions, or some combination thereof, clear unselected bits. - This can not be done in mode_compile because the value + This cannot be done in mode_compile because the value to which the `changes->affected' mask is applied depends on the old mode of each file. */ value &= changes->affected; @@ -283,8 +434,9 @@ mode_adjust (oldmode, changes) directory and no execute bits are already set. */ if ((changes->flags & MODE_X_IF_ANY_X) && !S_ISDIR (oldmode) - && (newmode & 00111) == 0) - value &= ~00111; /* Clear the execute bits. */ + && (newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) + /* Clear the execute bits. */ + value &= ~ (S_IXUSR | S_IXGRP | S_IXOTH); } switch (changes->op) @@ -309,8 +461,7 @@ mode_adjust (oldmode, changes) CHANGES. */ void -mode_free (changes) - register struct mode_change *changes; +mode_free (register struct mode_change *changes) { register struct mode_change *next; @@ -321,21 +472,3 @@ mode_free (changes) changes = next; } } - -/* Return a positive integer containing the value of the ASCII - octal number S. If S is not an octal number, return -1. */ - -static int -oatoi (s) - char *s; -{ - register int i; - - if (*s == 0) - return -1; - for (i = 0; isodigit (*s); ++s) - i = i * 8 + *s - '0'; - if (*s) - return -1; - return i; -}