diff cutils/treesum.py @ 131:3a18d71d7c50

Implement --follow-directory-symlinks when walking a directory tree
author Franz Glasner <fzglas.hg@dom66.de>
date Thu, 02 Jan 2025 20:52:49 +0100
parents d5621028ce39
children 8b73dca5db97
line wrap: on
line diff
--- a/cutils/treesum.py	Thu Jan 02 19:50:24 2025 +0100
+++ b/cutils/treesum.py	Thu Jan 02 20:52:49 2025 +0100
@@ -46,6 +46,13 @@
         "--base64", action="store_true",
         help="Output checksums in base64 notation, not hexadecimal (OpenBSD).")
     aparser.add_argument(
+        "--follow-directory-symlinks", action="store_true",
+        dest="follow_directory_symlinks",
+        help="Follow symbolic links to directories when walking a directory"
+             " tree. Note that this is different from using \"--logical\" or"
+             " \"--physical\" for arguments given directly on the command"
+             " line")
+    aparser.add_argument(
         "--logical", "-L", dest="logical", action="store_true", default=None,
         help="Follow symbolic links given on command line arguments."
              " Note that this is a different setting as to follow symbolic"
@@ -83,17 +90,20 @@
              algorithm="BLAKE2b-256",
              append_output=False,
              base64=False,
+             follow_directory_symlinks=False,
              logical=None,
              mmap=None,
              output=None):
-    opts = argparse.Namespace(directories=directories,
-                              algorithm=(util.algotag2algotype(algorithm),
-                                         algorithm),
-                              append_output=append_output,
-                              base64=base64,
-                              logical=logical,
-                              mmap=mmap,
-                              output=output)
+    opts = argparse.Namespace(
+        directories=directories,
+        algorithm=(util.algotag2algotype(algorithm),
+                   algorithm),
+        append_output=append_output,
+        base64=base64,
+        follow_directory_symlinks=follow_directory_symlinks,
+        logical=logical,
+        mmap=mmap,
+        output=output)
     return opts
 
 
@@ -120,11 +130,13 @@
     with out_cm as outfp:
         for d in opts.directories:
             generate_treesum_for_directory(
-                outfp, d, opts.algorithm, opts.mmap, opts.base64, opts.logical)
+                outfp, d, opts.algorithm, opts.mmap, opts.base64, opts.logical,
+                opts.follow_directory_symlinks)
 
 
 def generate_treesum_for_directory(
-        outfp, root, algorithm, use_mmap, use_base64, handle_root_logical):
+        outfp, root, algorithm, use_mmap, use_base64, handle_root_logical,
+        follow_directory_symlinks):
     """
 
     :param outfp: a *binary* file with a "write()" and a "flush()" method
@@ -137,6 +149,8 @@
     flags = []
     if handle_root_logical:
         flags.append("logical")
+    if follow_directory_symlinks:
+        flags.append("follow-directory-symlinks")
     if flags:
         outfp.write(format_bsd_line("FLAGS", ",".join(flags), None, False))
 
@@ -163,10 +177,12 @@
         outfp.flush()
         return
 
-    for top, dirs, nondirs in walk.walk(root, follow_symlinks=False):
+    for top, dirs, nondirs in walk.walk(
+            root,
+            follow_symlinks=follow_directory_symlinks):
         dir_dgst = algorithm[0]()
         for dn in dirs:
-            if dn.is_symlink:
+            if dn.is_symlink and not follow_directory_symlinks:
                 linktgt = util.fsencode(os.readlink(dn.path))
                 linkdgst = algorithm[0]()
                 linkdgst.update(linktgt)
@@ -180,11 +196,11 @@
                         "%s/./@" % (opath,),
                         use_base64))
                 outfp.flush()
-            else:
-                # fetch from dir_digests
-                dgst = dir_digests[top + (dn.name,)]
-                dir_dgst.update(b"1:d,%d:%s," % (len(dn.fsname), dn.fsname))
-                dir_dgst.update(dgst)
+                continue
+            # fetch from dir_digests
+            dgst = dir_digests[top + (dn.name,)]
+            dir_dgst.update(b"1:d,%d:%s," % (len(dn.fsname), dn.fsname))
+            dir_dgst.update(dgst)
         for fn in nondirs:
             dir_dgst.update(b"1:f,%d:%s," % (len(fn.fsname), fn.fsname))
             dgst = digest.compute_digest_file(