X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fmodechange.c;h=71432fa427bbd782acc239d240a399f7e6f99cc5;hb=ade79def12b42c40afa2d4bd47989ee40df7fe98;hp=97f2e63ba7709a05ba051b7ce9b8e6be2d18d504;hpb=68fa88b900c9096c51a309411fbc3b2089b42621;p=gnulib.git diff --git a/lib/modechange.c b/lib/modechange.c index 97f2e63ba..71432fa42 100644 --- a/lib/modechange.c +++ b/lib/modechange.c @@ -1,5 +1,5 @@ /* modechange.c -- file mode manipulation - Copyright (C) 1989, 1990, 1997 Free Software Foundation, Inc. + Copyright (C) 1989, 1990, 1997, 1998, 1999 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 @@ -28,9 +28,9 @@ # include #endif -#include -#include #include "modechange.h" +#include +#include "xstrtol.h" #if STDC_HEADERS # include @@ -50,12 +50,94 @@ char *malloc (); # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif +#ifndef S_ISUID +# define S_ISUID 04000 +#endif +#ifndef S_ISGID +# define S_ISGID 04000 +#endif +#ifndef S_ISVTX +# define S_ISVTX 01000 +#endif +#ifndef S_IRUSR +# define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 0200 +#endif +#ifndef S_IXUSR +# define S_IXUSR 0100 +#endif +#ifndef S_IRGRP +# define S_IRGRP 0040 +#endif +#ifndef S_IWGRP +# define S_IWGRP 0020 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0010 +#endif +#ifndef S_IROTH +# define S_IROTH 0004 +#endif +#ifndef S_IWOTH +# define S_IWOTH 0002 +#endif +#ifndef S_IXOTH +# define S_IXOTH 0001 +#endif +#ifndef S_IRWXU +# define S_IRWXU 0700 +#endif +#ifndef S_IRWXG +# define S_IRWXG 0070 +#endif +#ifndef S_IRWXO +# define S_IRWXO 0007 +#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 int oatoi (); +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 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,46 +153,47 @@ static int oatoi (); return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */ struct mode_change * -mode_compile (mode_string, masked_ops) - const 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 mode_value; /* The mode value, if octal. */ + char *string_end; /* Pointer to end of parsed value. */ + mode_t umask_value; /* The umask value (surprise). */ + + head = NULL; +#ifdef lint + tail = NULL; +#endif + + if (xstrtoul (mode_string, &string_end, 8, &mode_value, "") == LONGINT_OK) { - if (i > 07777) + struct mode_change *p; + if (mode_value > CHMOD_MODE_BITS) return MODE_INVALID; - head = talloc (struct mode_change); - if (head == NULL) + p = make_node_op_equals ((mode_t) mode_value); + 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 { + /* 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 ops_to_mask = 0; + + int who_specified_p; + affected_bits = 0; ops_to_mask = 0; /* Turn on all the bits in `affected_bits' for each group given. */ @@ -118,16 +201,16 @@ mode_compile (mode_string, masked_ops) 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 +219,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 = 1; + else { - affected_bits = 07777; + who_specified_p = 0; + 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 +260,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 +304,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 +312,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 +328,14 @@ invalid: return MODE_INVALID; } -/* Return a file mode change operation created from the reference REF_FILE - Don't affect special permissions, use umask, affect 'x' if any 'x', for - maximum security - - Return MODE_BAD_REFERENCE if REF_FILE can't be accessed */ +/* 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 (ref_file) - const char *ref_file; +mode_create_from_ref (const char *ref_file) { struct mode_change *change; /* the only change element */ struct stat ref_stats; - int i; - int umask_value; if (stat (ref_file, &ref_stats)) return MODE_BAD_REFERENCE; @@ -259,12 +345,9 @@ mode_create_from_ref (ref_file) if (change == NULL) return MODE_MEMORY_EXHAUSTED; - umask_value = umask (0); - umask (umask_value); - change->op = '='; - change->flags = MODE_X_IF_ANY_X; - change->affected = 0777 & ~umask_value; + change->flags = 0; + change->affected = CHMOD_MODE_BITS; change->value = ref_stats.st_mode; change->next = NULL; @@ -276,15 +359,13 @@ mode_create_from_ref (ref_file) 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; - const 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) { @@ -294,15 +375,21 @@ 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_IROTH : 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_IROTH : 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_IRGRP : 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. @@ -318,8 +405,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) @@ -344,8 +432,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; @@ -356,21 +443,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; -}