More NEWS updates.
[rsync.git] / support / munge-symlinks
blobe7a5474167d231c1a89586a351c9ee8300e94cf3
1 #!/usr/bin/env python3
2 # This script will either prefix all symlink values with the string
3 # "/rsyncd-munged/" or remove that prefix.
5 import os, sys, argparse
7 SYMLINK_PREFIX = '/rsyncd-munged/'
8 PREFIX_LEN = len(SYMLINK_PREFIX)
10 def main():
11     for arg in args.names:
12         if os.path.islink(arg):
13             process_one_arg(arg)
14         elif os.path.isdir(arg):
15             for fn in find_symlinks(arg):
16                 process_one_arg(fn)
17         else:
18             print("Arg is not a symlink or a dir:", arg, file=sys.stderr)
21 def find_symlinks(path):
22     for entry in os.scandir(path):
23         if entry.is_symlink():
24             yield entry.path
25         elif entry.is_dir(follow_symlinks=False):
26             yield from find_symlinks(entry.path)
29 def process_one_arg(fn):
30     lnk = os.readlink(fn)
31     if args.unmunge:
32         if not lnk.startswith(SYMLINK_PREFIX):
33             return
34         lnk = lnk[PREFIX_LEN:]
35         while args.all and lnk.startswith(SYMLINK_PREFIX):
36             lnk = lnk[PREFIX_LEN:]
37     else:
38         if not args.all and lnk.startswith(SYMLINK_PREFIX):
39             return
40         lnk = SYMLINK_PREFIX + lnk
42     try:
43         os.unlink(fn)
44     except OSError as e:
45         print("Unable to unlink symlink:", str(e), file=sys.stderr)
46         return
47     try:
48         os.symlink(lnk, fn)
49     except OSError as e:
50         print("Unable to recreate symlink", fn, '->', lnk + ':', str(e), file=sys.stderr)
51         return
52     print(fn, '->', lnk)
55 if __name__ == '__main__':
56     our_desc = """\
57 Adds or removes the %s prefix to/from the start of each symlink's value.
58 When given the name of a directory, affects all the symlinks in that directory hierarchy.
59 """ % SYMLINK_PREFIX
60     epilog = 'See the "munge symlinks" option in the rsyncd.conf manpage for more details.'
61     parser = argparse.ArgumentParser(description=our_desc, epilog=epilog, add_help=False)
62     uniq_group = parser.add_mutually_exclusive_group()
63     uniq_group.add_argument('--munge', action='store_true', help="Add the prefix to symlinks (the default).")
64     uniq_group.add_argument('--unmunge', action='store_true', help="Remove the prefix from symlinks.")
65     parser.add_argument('--all', action='store_true', help="Always adds the prefix when munging (even if already munged) or removes multiple instances of the prefix when unmunging.")
66     parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
67     parser.add_argument('names', metavar='NAME', nargs='+', help="One or more directories and/or symlinks to process.")
68     args = parser.parse_args()
69     main()
71 # vim: sw=4 et