struct chmod_mode_struct {
struct chmod_mode_struct *next;
- int ModeAND, ModeOR;
+ int ModeAND, ModeOR, ModeCOPY_SRC, ModeCOPY_DST, ModeCOPY_AND, ModeOP;
char flags;
};
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;
+ int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0, copybits = 0;
struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
*prev_mode = NULL;
while (state != STATE_ERROR) {
if (!*modestr || *modestr == ',') {
- int bits;
+ int bits, where_specified;
if (!op) {
state = STATE_ERROR;
first_mode = curr_mode;
curr_mode->next = NULL;
- if (where)
+ where_specified = where;
+ if (where) {
bits = where * what;
- else {
+ } else {
where = 0111;
bits = (where * what) & ~orig_umask;
}
case CHMOD_ADD:
curr_mode->ModeAND = CHMOD_BITS;
curr_mode->ModeOR = bits + topoct;
+ curr_mode->ModeCOPY_SRC = copybits;
+ curr_mode->ModeCOPY_DST = where;
+ curr_mode->ModeCOPY_AND = where_specified ? CHMOD_BITS : ~orig_umask;
+ curr_mode->ModeOP = op;
break;
case CHMOD_SUB:
curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
curr_mode->ModeOR = 0;
+ curr_mode->ModeCOPY_SRC = copybits;
+ curr_mode->ModeCOPY_DST = where;
+ curr_mode->ModeCOPY_AND = where_specified ? CHMOD_BITS : ~orig_umask;
+ curr_mode->ModeOP = op;
break;
case CHMOD_EQ:
curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
curr_mode->ModeOR = bits + topoct;
+ curr_mode->ModeCOPY_SRC = copybits;
+ curr_mode->ModeCOPY_DST = where;
+ curr_mode->ModeCOPY_AND = where_specified ? CHMOD_BITS : ~orig_umask;
+ curr_mode->ModeOP = op;
break;
case CHMOD_SET:
curr_mode->ModeAND = 0;
curr_mode->ModeOR = bits;
+ curr_mode->ModeCOPY_SRC = 0;
+ curr_mode->ModeCOPY_DST = 0;
+ curr_mode->ModeCOPY_AND = CHMOD_BITS;
+ curr_mode->ModeOP = op;
break;
}
modestr++;
state = STATE_1ST_HALF;
- where = what = op = topoct = topbits = flags = 0;
+ where = what = op = topoct = topbits = flags = copybits = 0;
}
switch (state) {
case STATE_2ND_HALF:
switch (*modestr) {
case 'r':
+ if (copybits)
+ state = STATE_ERROR;
what |= 4;
break;
case 'w':
+ if (copybits)
+ state = STATE_ERROR;
what |= 2;
break;
case 'X':
+ if (copybits)
+ state = STATE_ERROR;
flags |= FLAG_X_KEEP;
/* FALL THROUGH */
case 'x':
+ if (copybits)
+ state = STATE_ERROR;
what |= 1;
break;
case 's':
+ if (copybits)
+ state = STATE_ERROR;
if (topbits)
topoct |= topbits;
else
topoct = 04000;
break;
case 't':
+ if (copybits)
+ state = STATE_ERROR;
topoct |= 01000;
break;
+ case 'u':
+ if (what || topoct || copybits)
+ state = STATE_ERROR;
+ copybits = 0100;
+ break;
+ case 'g':
+ if (what || topoct || copybits)
+ state = STATE_ERROR;
+ copybits = 0010;
+ break;
+ case 'o':
+ if (what || topoct || copybits)
+ state = STATE_ERROR;
+ copybits = 0001;
+ break;
default:
state = STATE_ERROR;
break;
return first_mode;
}
+static int mode_copy_bits(int mode, int copy_src, int copy_dst, int copy_and)
+{
+ int copy_bits = 0;
+
+ if (copy_src & 0100)
+ copy_bits |= (mode >> 6) & 7;
+ if (copy_src & 0010)
+ copy_bits |= (mode >> 3) & 7;
+ if (copy_src & 0001)
+ copy_bits |= mode & 7;
+
+ return (copy_dst * copy_bits) & copy_and;
+}
+
/* Takes an existing file permission and a list of AND/OR changes, and
* create a new permissions. */
{
int IsX = mode & 0111;
int NonPerm = mode & ~CHMOD_BITS;
+ int copy_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;
+ copy_bits = mode_copy_bits(mode, chmod_modes->ModeCOPY_SRC,
+ chmod_modes->ModeCOPY_DST,
+ chmod_modes->ModeCOPY_AND);
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;
+ if (chmod_modes->ModeOP == CHMOD_SUB)
+ mode &= CHMOD_BITS - copy_bits;
+ else
+ mode |= copy_bits;
}
return mode | NonPerm;
from rsyncfns import (
FROMDIR, SCRATCHDIR, TODIR,
- build_rsyncd_conf, checkit, makepath, rmtree,
- run_rsync, start_test_daemon,
+ build_rsyncd_conf, check_perms, checkit, makepath, rmtree,
+ run_rsync, start_test_daemon, test_fail,
)
checkit(['-avv', '--chmod', 'ug-s,a+rX,D+w', f'{FROMDIR}/', f'{TODIR}/'],
checkdir, TODIR)
+def check_permcopy(chmod_arg, start_mode, expected):
+ rmtree(FROMDIR)
+ rmtree(TODIR)
+ makepath(FROMDIR)
+ (FROMDIR / 'permcopy').write_text('permcopy\n')
+ os.chmod(FROMDIR / 'permcopy', start_mode)
+ run_rsync('-avv', f'--chmod={chmod_arg}', f'{FROMDIR}/', f'{TODIR}/')
+ check_perms(TODIR / 'permcopy', expected)
+
+
+# Exercise chmod(1)-style permission copies.
+check_permcopy('g=o,o=', 0o647, 'rw-rwx---')
+check_permcopy('g=u', 0o741, 'rwxrwx--x')
+check_permcopy('g-o', 0o775, 'rwx-w-r-x')
+
+rmtree(FROMDIR)
+rmtree(TODIR)
+makepath(FROMDIR)
+(FROMDIR / 'permcopy').write_text('permcopy\n')
+proc = run_rsync('-avv', '--chmod=g=ur', f'{FROMDIR}/', f'{TODIR}/',
+ check=False, capture_output=True)
+if proc.returncode == 0:
+ test_fail('--chmod=g=ur was not rejected')
+
# Now exercise the F-only chmod path.
rmtree(FROMDIR)
rmtree(checkdir)