]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
rrsync: add -absolute argument to support calling rsync with absolute path master
authorSebMtn <102696928+SebMtn@users.noreply.github.com>
Sat, 2 May 2026 16:47:28 +0000 (17:47 +0100)
committerAndrew Tridgell <andrew@tridgell.net>
Fri, 5 Jun 2026 06:01:44 +0000 (16:01 +1000)
Signed-off-by: SebMtn <102696928+SebMtn@users.noreply.github.com>
support/rrsync
support/rrsync.1.md

index 56389c52c99f1a0d9c417bc3770060036e6dcda7..d69c17cfc779416b1659028cadbf1d2e69d02049 100755 (executable)
@@ -302,12 +302,12 @@ def validated_arg(opt, arg, typ=3, wild=False):
     if arg.startswith('./'):
         arg = arg[1:]
     arg = arg.replace('//', '/')
-    arg = arg.lstrip('/')
+    is_absolute_arg = args.absolute and opt == 'arg' and args.dir != '/' and (arg == args.dir or arg.startswith(args.dir_slash))
+    if not is_absolute_arg:
+        arg = arg.lstrip('/')
     if args.dir != '/':
         if HAS_DOT_DOT_RE.search(arg):
             die("do not use .. in", opt, "(anchor the path at the root of your restricted dir)")
-        if arg.startswith('/'):
-            arg = args.dir + arg
 
     if wild:
         got = glob.glob(arg)
@@ -328,12 +328,15 @@ def validated_arg(opt, arg, typ=3, wild=False):
                     arg = arg[:-2]
             real_arg = os.path.realpath(arg)
             if arg != real_arg and not real_arg.startswith(args.dir_slash):
-                die('unsafe arg:', orig_arg, [arg, real_arg])
+                if not (is_absolute_arg and real_arg == args.dir):
+                    die('unsafe arg:', orig_arg, [arg, real_arg])
             if arg_has_trailing_slash:
                 arg += '/'
             elif arg_has_trailing_slash_dot:
                 arg += '/.'
-            if opt == 'arg' and arg.startswith(args.dir_slash):
+            if is_absolute_arg and arg == args.dir:
+                arg = '.'
+            elif opt == 'arg' and arg.startswith(args.dir_slash):
                 arg = arg[args.dir_slash_len:]
                 if arg == '':
                     arg = '.'
@@ -372,6 +375,7 @@ if __name__ == '__main__':
     only_group.add_argument('-ro', action='store_true', help="Allow only reading from the DIR. Implies -no-del and -no-lock.")
     only_group.add_argument('-wo', action='store_true', help="Allow only writing to the DIR.")
     arg_parser.add_argument('-munge', action='store_true', help="Enable rsync's --munge-links on the server side.")
+    arg_parser.add_argument('-absolute', action='store_true', help="Allow transfer args to use absolute server paths under DIR.")
     arg_parser.add_argument('-no-del', action='store_true', help="Disable rsync's --delete* and --remove* options.")
     arg_parser.add_argument('-no-lock', action='store_true', help="Avoid the single-run (per-user) lock check.")
     arg_parser.add_argument('-no-overwrite', action='store_true', help="Prevent overwriting existing files by enforcing --ignore-existing")
index 09b2f28226a8ff22b5fa62c680c68a817706c365..a3ee858dd89708eb21c5492668f19bf44f916842 100644 (file)
@@ -5,7 +5,7 @@ rrsync - a script to setup restricted rsync users via ssh logins
 ## SYNOPSIS
 
 ```
-rrsync [-ro|-wo] [-munge] [-no-del] [-no-lock] [-no-overwrite]  DIR
+rrsync [-ro|-wo] [-munge] [-absolute] [-no-del] [-no-lock] [-no-overwrite] DIR
 ```
 
 The single non-option argument specifies the restricted _DIR_ to use. It can be
@@ -77,6 +77,12 @@ The remainder of this manpage is dedicated to using the rrsync script.
 
     Enable rsync's [`--munge-links`](rsync.1#opt) on the server side.
 
+0. `-absolute`
+
+    Allow file-transfer arguments to name the restricted directory using its
+    absolute server path. For example, with `rrsync -absolute /path/to/root`,
+    the transfer arg `/path/to/root/dir1` is accepted as an alias for `dir1`.
+
 0.  `-no-del`
 
     Disable rsync's `--delete*` and `--remove*` options.