mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-07-30 23:36:21 -04:00
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.
88 lines
3.2 KiB
Python
88 lines
3.2 KiB
Python
#!/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')
|