mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-06-07 21:58:06 -04:00
testsuite: regression for the #829 daemon --chown/--groupmap wildcard
Maps every source group to a second group the test user belongs to via a daemon upload (--groupmap='*:GID') and checks the wildcard took effect. Runs both arg modes: the default path (the '*' is safe_arg-escaped and the daemon must un-backslash it -- the regression) and --secluded-args (the '*' is sent raw over the protected channel, a guard that the fix left that path alone). Needs no root -- a non-root receiver can chgrp to a member group -- and was verified RED on a pre-fix binary (the escaped '\*' is ignored, gid unchanged) and GREEN after the fix.
This commit is contained in:
87
testsuite/daemon-groupmap-wild_test.py
Normal file
87
testsuite/daemon-groupmap-wild_test.py
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
# Regression test for issue #829.
|
||||
#
|
||||
# Without --secluded-args the client's safe_arg() backslash-escapes wildcard
|
||||
# chars in option values, so --chown / --groupmap=*:GROUP is sent to a daemon
|
||||
# as --groupmap=\*:GROUP. A daemon has no shell to strip the backslash, and
|
||||
# read_args() used to store option args verbatim, so the receiver saw the
|
||||
# literal "\*", the wildcard never matched, and the map was ignored (the
|
||||
# module's configured gid won instead). The fix un-backslashes daemon option
|
||||
# args.
|
||||
#
|
||||
# We run it both ways:
|
||||
# * default args -- the '*' is safe_arg-escaped and the daemon must
|
||||
# un-backslash it (the path the fix repairs);
|
||||
# * --secluded-args -- the '*' is sent raw over the protected channel and
|
||||
# read with unescape=0, so it must keep working too
|
||||
# (a guard that the fix didn't disturb that path).
|
||||
#
|
||||
# No root needed: a non-root receiver can chgrp(2) to a group the test user
|
||||
# belongs to, so we map every source group to a second such group and check
|
||||
# the wildcard took effect.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from rsyncfns import (
|
||||
SCRATCHDIR, makepath, rmtree, rsync_argv, start_test_daemon,
|
||||
test_fail, test_skipped, write_daemon_conf,
|
||||
)
|
||||
|
||||
DAEMON_PORT = 12923
|
||||
|
||||
# Two distinct groups to map between. As root (the usual CI case) we can
|
||||
# chgrp(2) to any gid, so take two distinct named groups from the group
|
||||
# database; a non-root user can only chgrp to groups it belongs to, so use those
|
||||
# (skip if it is in fewer than two).
|
||||
if os.geteuid() == 0:
|
||||
import grp
|
||||
usable = []
|
||||
for gr in grp.getgrall():
|
||||
if gr.gr_gid not in usable:
|
||||
usable.append(gr.gr_gid)
|
||||
if len(usable) < 2:
|
||||
test_skipped("need >=2 groups defined on the system")
|
||||
else:
|
||||
usable = []
|
||||
for g in [os.getgid()] + list(os.getgroups()):
|
||||
if g not in usable:
|
||||
usable.append(g)
|
||||
if len(usable) < 2:
|
||||
test_skipped("need >=2 groups the test user belongs to")
|
||||
src_gid, dst_gid = usable[0], usable[1]
|
||||
|
||||
moddir = SCRATCHDIR / 'gmod'
|
||||
srcdir = SCRATCHDIR / 'gsrc'
|
||||
makepath(moddir)
|
||||
|
||||
conf = write_daemon_conf([('gmod', {'path': str(moddir), 'read only': 'no'})])
|
||||
url = start_test_daemon(conf, DAEMON_PORT) + 'gmod/'
|
||||
|
||||
|
||||
def check(label, *extra_opts):
|
||||
rmtree(moddir)
|
||||
rmtree(srcdir)
|
||||
makepath(moddir)
|
||||
makepath(srcdir)
|
||||
f = srcdir / 'f.dat'
|
||||
f.write_text("hi\n")
|
||||
os.chown(f, -1, src_gid) # source group differs from the map target
|
||||
|
||||
# A --chown-style wildcard map sent to a daemon: the '*' must survive as a
|
||||
# wildcard so every source group is remapped to dst_gid.
|
||||
proc = subprocess.run(
|
||||
rsync_argv('-rg', *extra_opts, f'--groupmap=*:{dst_gid}', str(f), url),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
||||
if proc.returncode != 0:
|
||||
print(proc.stdout)
|
||||
test_fail(f"[{label}] groupmap upload failed (rc={proc.returncode})")
|
||||
|
||||
got = os.stat(moddir / 'f.dat').st_gid
|
||||
if got != dst_gid:
|
||||
test_fail(f"[{label}] --groupmap='*:{dst_gid}' wildcard ignored over "
|
||||
f"daemon: got gid {got}, expected {dst_gid} (regression of #829)")
|
||||
|
||||
|
||||
check('default-args')
|
||||
check('secluded-args', '--secluded-args')
|
||||
Reference in New Issue
Block a user