From: Zen Dodd Date: Sat, 6 Jun 2026 05:00:57 +0000 (+1000) Subject: chmod: clear special bits on copy assignment X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=30751ca8643c51d4fadac8e8115d6e8d4b62e1fd;p=thirdparty%2Frsync.git chmod: clear special bits on copy assignment --- diff --git a/chmod.c b/chmod.c index 8538d182..8368e5b3 100644 --- a/chmod.c +++ b/chmod.c @@ -43,6 +43,20 @@ struct chmod_mode_struct { #define STATE_2ND_HALF 2 #define STATE_OCTAL_NUM 3 +static int mode_dest_special_bits(int where) +{ + int bits = 0; + + if (where & 0100) + bits |= S_ISUID; + if (where & 0010) + bits |= S_ISGID; + if (where & 0001) + bits |= S_ISVTX; + + return bits; +} + /* 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 success * (appending the items to the specified list), or NULL on error. */ @@ -96,7 +110,8 @@ struct chmod_mode_struct *parse_chmod(const char *modestr, curr_mode->ModeOP = op; break; case CHMOD_EQ: - curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0); + curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0) + - (copybits ? mode_dest_special_bits(where) : 0); curr_mode->ModeOR = bits + topoct; curr_mode->ModeCOPY_SRC = copybits; curr_mode->ModeCOPY_DST = where; diff --git a/testsuite/chmod-option_test.py b/testsuite/chmod-option_test.py index b1cf714e..ad0fc77b 100644 --- a/testsuite/chmod-option_test.py +++ b/testsuite/chmod-option_test.py @@ -62,12 +62,16 @@ for d in (checkdir, checkdir / 'dir1', checkdir / 'dir2'): checkit(['-avv', '--chmod', 'ug-s,a+rX,D+w', f'{FROMDIR}/', f'{TODIR}/'], checkdir, TODIR) -def check_permcopy(chmod_arg, start_mode, expected): +def check_permcopy(chmod_arg, start_mode, expected, is_dir=False): rmtree(FROMDIR) rmtree(TODIR) makepath(FROMDIR) - (FROMDIR / 'permcopy').write_text('permcopy\n') - os.chmod(FROMDIR / 'permcopy', start_mode) + permcopy = FROMDIR / 'permcopy' + if is_dir: + permcopy.mkdir() + else: + permcopy.write_text('permcopy\n') + os.chmod(permcopy, start_mode) run_rsync('-avv', f'--chmod={chmod_arg}', f'{FROMDIR}/', f'{TODIR}/') check_perms(TODIR / 'permcopy', expected) @@ -76,6 +80,9 @@ def check_permcopy(chmod_arg, start_mode, expected): check_permcopy('g=o,o=', 0o647, 'rw-rwx---') check_permcopy('g=u', 0o741, 'rwxrwx--x') check_permcopy('g-o', 0o775, 'rwx-w-r-x') +check_permcopy('u=g', 0o4755, 'r-xr-xr-x') +check_permcopy('g=u', 0o2755, 'rwxrwxr-x') +check_permcopy('o=u', 0o1750, 'rwxr-xrwx', is_dir=True) rmtree(FROMDIR) rmtree(TODIR)