mirror of
http://galexander.org/git/simplesshd.git
synced 2024-11-30 19:28:10 +00:00
250 lines
5.5 KiB
C
250 lines
5.5 KiB
C
|
/*
|
||
|
* Implement the core of the --chmod option.
|
||
|
*
|
||
|
* Copyright (C) 2002 Scott Howard
|
||
|
* Copyright (C) 2005-2014 Wayne Davison
|
||
|
*
|
||
|
* 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
|
||
|
* the Free Software Foundation; either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* 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, visit the http://fsf.org website.
|
||
|
*/
|
||
|
|
||
|
#include "rsync.h"
|
||
|
#include "itypes.h"
|
||
|
|
||
|
extern mode_t orig_umask;
|
||
|
|
||
|
#define FLAG_X_KEEP (1<<0)
|
||
|
#define FLAG_DIRS_ONLY (1<<1)
|
||
|
#define FLAG_FILES_ONLY (1<<2)
|
||
|
|
||
|
struct chmod_mode_struct {
|
||
|
struct chmod_mode_struct *next;
|
||
|
int ModeAND, ModeOR;
|
||
|
char flags;
|
||
|
};
|
||
|
|
||
|
#define CHMOD_ADD 1
|
||
|
#define CHMOD_SUB 2
|
||
|
#define CHMOD_EQ 3
|
||
|
#define CHMOD_SET 4
|
||
|
|
||
|
#define STATE_ERROR 0
|
||
|
#define STATE_1ST_HALF 1
|
||
|
#define STATE_2ND_HALF 2
|
||
|
#define STATE_OCTAL_NUM 3
|
||
|
|
||
|
/* Parse a chmod-style argument, and break it down into one or more AND/OR
|
||
|
* pairs in a linked list. We return a pointer to new items on succcess
|
||
|
* (appending the items to the specified list), or NULL on error. */
|
||
|
struct chmod_mode_struct *parse_chmod(const char *modestr,
|
||
|
struct chmod_mode_struct **root_mode_ptr)
|
||
|
{
|
||
|
int state = STATE_1ST_HALF;
|
||
|
int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
|
||
|
struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
|
||
|
*prev_mode = NULL;
|
||
|
|
||
|
while (state != STATE_ERROR) {
|
||
|
if (!*modestr || *modestr == ',') {
|
||
|
int bits;
|
||
|
|
||
|
if (!op) {
|
||
|
state = STATE_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
prev_mode = curr_mode;
|
||
|
curr_mode = new_array(struct chmod_mode_struct, 1);
|
||
|
if (prev_mode)
|
||
|
prev_mode->next = curr_mode;
|
||
|
else
|
||
|
first_mode = curr_mode;
|
||
|
curr_mode->next = NULL;
|
||
|
|
||
|
if (where)
|
||
|
bits = where * what;
|
||
|
else {
|
||
|
where = 0111;
|
||
|
bits = (where * what) & ~orig_umask;
|
||
|
}
|
||
|
|
||
|
switch (op) {
|
||
|
case CHMOD_ADD:
|
||
|
curr_mode->ModeAND = CHMOD_BITS;
|
||
|
curr_mode->ModeOR = bits + topoct;
|
||
|
break;
|
||
|
case CHMOD_SUB:
|
||
|
curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
|
||
|
curr_mode->ModeOR = 0;
|
||
|
break;
|
||
|
case CHMOD_EQ:
|
||
|
curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
|
||
|
curr_mode->ModeOR = bits + topoct;
|
||
|
break;
|
||
|
case CHMOD_SET:
|
||
|
curr_mode->ModeAND = 0;
|
||
|
curr_mode->ModeOR = bits;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
curr_mode->flags = flags;
|
||
|
|
||
|
if (!*modestr)
|
||
|
break;
|
||
|
modestr++;
|
||
|
|
||
|
state = STATE_1ST_HALF;
|
||
|
where = what = op = topoct = topbits = flags = 0;
|
||
|
}
|
||
|
|
||
|
switch (state) {
|
||
|
case STATE_1ST_HALF:
|
||
|
switch (*modestr) {
|
||
|
case 'D':
|
||
|
if (flags & FLAG_FILES_ONLY)
|
||
|
state = STATE_ERROR;
|
||
|
flags |= FLAG_DIRS_ONLY;
|
||
|
break;
|
||
|
case 'F':
|
||
|
if (flags & FLAG_DIRS_ONLY)
|
||
|
state = STATE_ERROR;
|
||
|
flags |= FLAG_FILES_ONLY;
|
||
|
break;
|
||
|
case 'u':
|
||
|
where |= 0100;
|
||
|
topbits |= 04000;
|
||
|
break;
|
||
|
case 'g':
|
||
|
where |= 0010;
|
||
|
topbits |= 02000;
|
||
|
break;
|
||
|
case 'o':
|
||
|
where |= 0001;
|
||
|
break;
|
||
|
case 'a':
|
||
|
where |= 0111;
|
||
|
break;
|
||
|
case '+':
|
||
|
op = CHMOD_ADD;
|
||
|
state = STATE_2ND_HALF;
|
||
|
break;
|
||
|
case '-':
|
||
|
op = CHMOD_SUB;
|
||
|
state = STATE_2ND_HALF;
|
||
|
break;
|
||
|
case '=':
|
||
|
op = CHMOD_EQ;
|
||
|
state = STATE_2ND_HALF;
|
||
|
break;
|
||
|
default:
|
||
|
if (isDigit(modestr) && *modestr < '8' && !where) {
|
||
|
op = CHMOD_SET;
|
||
|
state = STATE_OCTAL_NUM;
|
||
|
where = 1;
|
||
|
what = *modestr - '0';
|
||
|
} else
|
||
|
state = STATE_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case STATE_2ND_HALF:
|
||
|
switch (*modestr) {
|
||
|
case 'r':
|
||
|
what |= 4;
|
||
|
break;
|
||
|
case 'w':
|
||
|
what |= 2;
|
||
|
break;
|
||
|
case 'X':
|
||
|
flags |= FLAG_X_KEEP;
|
||
|
/* FALL THROUGH */
|
||
|
case 'x':
|
||
|
what |= 1;
|
||
|
break;
|
||
|
case 's':
|
||
|
if (topbits)
|
||
|
topoct |= topbits;
|
||
|
else
|
||
|
topoct = 04000;
|
||
|
break;
|
||
|
case 't':
|
||
|
topoct |= 01000;
|
||
|
break;
|
||
|
default:
|
||
|
state = STATE_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case STATE_OCTAL_NUM:
|
||
|
if (isDigit(modestr) && *modestr < '8') {
|
||
|
what = what*8 + *modestr - '0';
|
||
|
if (what > CHMOD_BITS)
|
||
|
state = STATE_ERROR;
|
||
|
} else
|
||
|
state = STATE_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
modestr++;
|
||
|
}
|
||
|
|
||
|
if (state == STATE_ERROR) {
|
||
|
free_chmod_mode(first_mode);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (!(curr_mode = *root_mode_ptr))
|
||
|
*root_mode_ptr = first_mode;
|
||
|
else {
|
||
|
while (curr_mode->next)
|
||
|
curr_mode = curr_mode->next;
|
||
|
curr_mode->next = first_mode;
|
||
|
}
|
||
|
|
||
|
return first_mode;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Takes an existing file permission and a list of AND/OR changes, and
|
||
|
* create a new permissions. */
|
||
|
int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
|
||
|
{
|
||
|
int IsX = mode & 0111;
|
||
|
int NonPerm = mode & ~CHMOD_BITS;
|
||
|
|
||
|
for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
|
||
|
if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
|
||
|
continue;
|
||
|
if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
|
||
|
continue;
|
||
|
mode &= chmod_modes->ModeAND;
|
||
|
if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
|
||
|
mode |= chmod_modes->ModeOR & ~0111;
|
||
|
else
|
||
|
mode |= chmod_modes->ModeOR;
|
||
|
}
|
||
|
|
||
|
return mode | NonPerm;
|
||
|
}
|
||
|
|
||
|
/* Free the linked list created by parse_chmod. */
|
||
|
int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
|
||
|
{
|
||
|
struct chmod_mode_struct *next;
|
||
|
|
||
|
while (chmod_modes) {
|
||
|
next = chmod_modes->next;
|
||
|
free(chmod_modes);
|
||
|
chmod_modes = next;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|