diff cutils/treesum.py @ 158:d8cdd1985d43

Implement "--full-mode" for "treesum.py"
author Franz Glasner <fzglas.hg@dom66.de>
date Tue, 07 Jan 2025 12:48:37 +0100
parents 27d1aaf5fe39
children 5c23ad9a3f8f
line wrap: on
line diff
--- a/cutils/treesum.py	Mon Jan 06 21:03:10 2025 +0100
+++ b/cutils/treesum.py	Tue Jan 07 12:48:37 2025 +0100
@@ -65,6 +65,12 @@
                  "\"--logical\" or \"--physical\" for arguments given "
                  "directly on the command line")
         gp.add_argument(
+            "--full-mode", action="store_true", dest="metadata_full_mode",
+            help="Consider all mode bits as returned from stat(2) when "
+                 "computing directory digests. "
+                 "Note that mode bits on symbolic links itself are not "
+                 "considered.")
+        gp.add_argument(
             "--logical", "-L", dest="logical", action="store_true",
             default=None,
             help="Follow symbolic links given on command line arguments."
@@ -80,10 +86,10 @@
                  "automatically from the filesize.")
         gp.add_argument(
             "--mode", action="store_true", dest="metadata_mode",
-            help="Consider the permission bits of stat using S_IMODE (i.e. "
+            help="Consider the permission bits of stat(2) using S_IMODE (i.e. "
                  "all bits without the filetype bits) when "
-                 "computing directory digests. Note that symbolic links "
-                 "are not considered.")
+                 "computing directory digests. Note that mode bits on "
+                 "symbolic links itself are not considered.")
         gp.add_argument(
             "--mtime", action="store_true", dest="metadata_mtime",
             help="Consider the mtime of files (non-directories) when "
@@ -194,6 +200,7 @@
                       base64=False,
                       comment=[],
                       follow_directory_symlinks=False,
+                      full_mode=False,
                       logical=None,
                       minimal=None,
                       mode=False,
@@ -211,6 +218,7 @@
         logical=logical,
         minimal=minimal,
         mmap=mmap,
+        metadata_full_mode=full_mode,
         metadata_mode=mode,
         metadata_mtime=mtime,
         output=output)
@@ -250,6 +258,7 @@
                 outfp, d, opts.algorithm, opts.mmap, opts.base64, opts.logical,
                 opts.follow_directory_symlinks,
                 opts.metadata_mode,
+                opts.metadata_full_mode,
                 opts.metadata_mtime,
                 minimal=opts.minimal,
                 comment=opts.comment)
@@ -257,7 +266,8 @@
 
 def generate_treesum_for_directory(
         outfp, root, algorithm, use_mmap, use_base64, handle_root_logical,
-        follow_directory_symlinks, with_metadata_mode, with_metadata_mtime,
+        follow_directory_symlinks, with_metadata_mode, with_metadata_full_mode,
+        with_metadata_mtime,
         minimal=None, comment=None):
     """
 
@@ -322,8 +332,9 @@
             follow_symlinks=follow_directory_symlinks):
         dir_dgst = algorithm[0]()
         for fso in fsobjects:
-            # print("NNNNNNNN", fso.name, fso.stat,
-            # "%o (%o)" % (fso.stat.st_mode, stat.S_IMODE(fso.stat.st_mode)))
+            print("NNNNNNNN", fso.name, fso.stat,
+                  "%o (%o)"
+                  % (fso.stat.st_mode, stat.S_IMODE(fso.stat.st_mode)))
             if fso.is_dir:
                 if fso.is_symlink and not follow_directory_symlinks:
                     linktgt = util.fsencode(os.readlink(fso.path))
@@ -346,10 +357,16 @@
                 dgst = dir_digests[top + (fso.name,)]
                 dir_dgst.update(b"1:d,%d:%s," % (len(fso.fsname), fso.fsname))
                 dir_dgst.update(dgst)
-                if with_metadata_mode:
+                if with_metadata_full_mode:
                     modestr = normalized_mode_str(fso.stat.st_mode)
                     if not isinstance(modestr, bytes):
                         modestr = modestr.encode("ascii")
+                    dir_dgst.update(b"8:fullmode,%d:%s,"
+                                    % (len(modestr), modestr))
+                elif with_metadata_mode:
+                    modestr = normalized_compatible_mode_str(fso.stat.st_mode)
+                    if not isinstance(modestr, bytes):
+                        modestr = modestr.encode("ascii")
                     dir_dgst.update(b"4:mode,%d:%s," % (len(modestr), modestr))
             else:
                 dir_dgst.update(b"1:f,%d:%s," % (len(fso.fsname), fso.fsname))
@@ -360,10 +377,16 @@
                     if not isinstance(mtime, bytes):
                         mtime = mtime.encode("ascii")
                     dir_dgst.update(b"5:mtime,%d:%s," % (len(mtime), mtime))
-                if with_metadata_mode:
+                if with_metadata_full_mode:
                     modestr = normalized_mode_str(fso.stat.st_mode)
                     if not isinstance(modestr, bytes):
                         modestr = modestr.encode("ascii")
+                    dir_dgst.update(b"8:fullmode,%d:%s,"
+                                    % (len(modestr), modestr))
+                elif with_metadata_mode:
+                    modestr = normalized_compatible_mode_str(fso.stat.st_mode)
+                    if not isinstance(modestr, bytes):
+                        modestr = modestr.encode("ascii")
                     dir_dgst.update(b"4:mode,%d:%s," % (len(modestr), modestr))
                 dgst = digest.compute_digest_file(
                     algorithm[0], fso.path, use_mmap=use_mmap)
@@ -380,7 +403,7 @@
         dir_digests[top] = dir_dgst.digest()
 
 
-def normalized_mode_str(mode):
+def normalized_compatible_mode_str(mode):
     # XXX FIXME: Windows and "executable"
     modebits = stat.S_IMODE(mode)
     modestr = "%o" % (modebits,)
@@ -389,6 +412,13 @@
     return modestr
 
 
+def normalized_mode_str(mode):
+    modestr = "%o" % (mode,)
+    if not modestr.startswith("0"):
+        modestr = "0" + modestr
+    return modestr
+
+
 def format_bsd_line(digestname, value, filename, use_base64):
     ls = os.linesep if isinstance(os.linesep, bytes) \
         else os.linesep.encode("utf-8")