#if HAVE_CONFIG_H
# include <config.h>
#endif
#include "modechange.h"
#include <sys/stat.h>
#include "xstrtol.h"
#if STDC_HEADERS
# include <stdlib.h>
#else
char *malloc ();
#endif
#ifndef NULL
# define NULL 0
#endif
#if STAT_MACROS_BROKEN
# undef S_ISDIR
#endif
#if !defined(S_ISDIR) && defined(S_IFDIR)
# 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
#define CHMOD_MODE_BITS \
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
#define talloc(type) ((type *) malloc (sizeof (type)))
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;
return p;
}
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;
}
}
struct mode_change *
mode_compile (const char *mode_string, unsigned int masked_ops)
{
struct mode_change *head;
struct mode_change *tail;
unsigned long mode_value;
char *string_end;
mode_t umask_value;
head = NULL;
#ifdef lint
tail = NULL;
#endif
if (xstrtoul (mode_string, &string_end, 8, &mode_value, "") == LONGINT_OK)
{
struct mode_change *p;
if (mode_value > CHMOD_MODE_BITS)
return MODE_INVALID;
p = make_node_op_equals ((mode_t) mode_value);
if (p == NULL)
return MODE_MEMORY_EXHAUSTED;
mode_append_entry (&head, &tail, p);
return head;
}
umask_value = umask (0);
umask (umask_value);
--mode_string;
do
{
mode_t affected_bits = 0;
mode_t affected_masked;
unsigned ops_to_mask = 0;
int who_specified_p;
affected_bits = 0;
ops_to_mask = 0;
for (++mode_string;; ++mode_string)
switch (*mode_string)
{
case 'u':
affected_bits |= S_ISUID | S_IRWXU;
break;
case 'g':
affected_bits |= S_ISGID | S_IRWXG;
break;
case 'o':
affected_bits |= S_ISVTX | S_IRWXO;
break;
case 'a':
affected_bits |= CHMOD_MODE_BITS;
break;
default:
goto no_more_affected;
}
no_more_affected:
if (affected_bits)
who_specified_p = 1;
else
{
who_specified_p = 0;
affected_bits = CHMOD_MODE_BITS;
ops_to_mask = masked_ops;
}
while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-')
{
struct mode_change *change = talloc (struct mode_change);
if (change == NULL)
{
mode_free (head);
return MODE_MEMORY_EXHAUSTED;
}
change->next = NULL;
change->op = *mode_string;
affected_masked = affected_bits;
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))
affected_masked &= ~umask_value;
change->affected = affected_masked;
change->value = 0;
change->flags = 0;
mode_append_entry (&head, &tail, change);
for (++mode_string;; ++mode_string)
switch (*mode_string)
{
case 'r':
change->value |= ((S_IRUSR | S_IRGRP | S_IROTH)
& affected_masked);
break;
case 'w':
change->value |= ((S_IWUSR | S_IWGRP | S_IWOTH)
& affected_masked);
break;
case 'X':
change->flags |= MODE_X_IF_ANY_X;
case 'x':
change->value |= ((S_IXUSR | S_IXGRP | S_IXOTH)
& affected_masked);
break;
case 's':
change->value |= (S_ISUID | S_ISGID) & affected_masked;
break;
case 't':
change->value |= S_ISVTX & affected_masked;
break;
case 'u':
if (change->value)
goto invalid;
change->value = S_IRWXU;
change->flags |= MODE_COPY_EXISTING;
break;
case 'g':
if (change->value)
goto invalid;
change->value = S_IRWXG;
change->flags |= MODE_COPY_EXISTING;
break;
case 'o':
if (change->value)
goto invalid;
change->value = S_IRWXO;
change->flags |= MODE_COPY_EXISTING;
break;
default:
goto no_more_values;
}
no_more_values:;
}
} while (*mode_string == ',');
if (*mode_string == 0)
return head;
invalid:
mode_free (head);
return MODE_INVALID;
}
struct mode_change *
mode_create_from_ref (const char *ref_file)
{
struct mode_change *change;
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;
}
mode_t
mode_adjust (mode_t oldmode, const struct mode_change *changes)
{
mode_t newmode;
mode_t value;
newmode = oldmode & CHMOD_MODE_BITS;
for (; changes; changes = changes->next)
{
if (changes->flags & MODE_COPY_EXISTING)
{
value = newmode & changes->value;
if (changes->value & S_IRWXU)
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)
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
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));
value &= changes->affected;
}
else
{
value = changes->value;
if ((changes->flags & MODE_X_IF_ANY_X)
&& !S_ISDIR (oldmode)
&& (newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
value &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
}
switch (changes->op)
{
case '=':
newmode = (newmode & ~changes->affected) | value;
break;
case '+':
newmode |= value;
break;
case '-':
newmode &= ~value;
break;
}
}
return newmode;
}
void
mode_free (register struct mode_change *changes)
{
register struct mode_change *next;
while (changes)
{
next = changes->next;
free (changes);
changes = next;
}
}