comparison cutils/treesum.py @ 125:12d6ec1f8613

Implement "--logical" and "--physical" to control following symlinked directories when given on the commandline
author Franz Glasner <fzglas.hg@dom66.de>
date Thu, 02 Jan 2025 17:03:10 +0100
parents 3bd3f32b5e60
children 6a50d02fe0ca
comparison
equal deleted inserted replaced
124:3bd3f32b5e60 125:12d6ec1f8613
42 help="Append to the output file instead of overwriting it.") 42 help="Append to the output file instead of overwriting it.")
43 aparser.add_argument( 43 aparser.add_argument(
44 "--base64", action="store_true", 44 "--base64", action="store_true",
45 help="Output checksums in base64 notation, not hexadecimal (OpenBSD).") 45 help="Output checksums in base64 notation, not hexadecimal (OpenBSD).")
46 aparser.add_argument( 46 aparser.add_argument(
47 "--logical", "-L", dest="logical", action="store_true", default=None,
48 help="Follow symbolic links given on command line arguments."
49 " Note that this is a different setting as to follow symbolic"
50 " links to directories when traversing a directory tree.")
51 aparser.add_argument(
47 "--mmap", action="store_true", dest="mmap", default=None, 52 "--mmap", action="store_true", dest="mmap", default=None,
48 help="Use mmap if available. Default is to determine automatically " 53 help="Use mmap if available. Default is to determine automatically "
49 "from the filesize.") 54 "from the filesize.")
50 aparser.add_argument( 55 aparser.add_argument(
51 "--no-mmap", action="store_false", dest="mmap", default=None, 56 "--no-mmap", action="store_false", dest="mmap", default=None,
52 help="Dont use mmap.") 57 help="Dont use mmap.")
53 aparser.add_argument( 58 aparser.add_argument(
54 "--output", "-o", action="store", metavar="OUTPUT", 59 "--output", "-o", action="store", metavar="OUTPUT",
55 help="Put the checksum into given file. If not given of if it is given" 60 help="Put the checksum into given file. If not given of if it is given"
56 " as `-' then stdout is used.") 61 " as `-' then stdout is used.")
62 aparser.add_argument(
63 "--physical", "-P", dest="logical", action="store_false", default=None,
64 help="Do not follow symbolic links given on comment line arguments."
65 " This is the default.")
57 aparser.add_argument( 66 aparser.add_argument(
58 "--version", "-v", action="version", 67 "--version", "-v", action="version",
59 version="%s (rv:%s)" % (__version__, __revision__)) 68 version="%s (rv:%s)" % (__version__, __revision__))
60 aparser.add_argument( 69 aparser.add_argument(
61 "directories", nargs="*", metavar="DIRECTORY") 70 "directories", nargs="*", metavar="DIRECTORY")
70 79
71 def gen_opts(directories=[], 80 def gen_opts(directories=[],
72 algorithm="BLAKE2b-256", 81 algorithm="BLAKE2b-256",
73 append_output=False, 82 append_output=False,
74 base64=False, 83 base64=False,
84 logical=None,
75 mmap=None, 85 mmap=None,
76 output=None): 86 output=None):
77 opts = argparse.Namespace(directories=directories, 87 opts = argparse.Namespace(directories=directories,
78 algorithm=(util.algotag2algotype(algorithm), 88 algorithm=(util.algotag2algotype(algorithm),
79 algorithm), 89 algorithm),
80 append_output=append_output, 90 append_output=append_output,
81 base64=base64, 91 base64=base64,
92 logical=logical,
82 mmap=mmap, 93 mmap=mmap,
83 output=output) 94 output=output)
84 return opts 95 return opts
85 96
86 97
105 out_cm = open(opts.output, "wb") 116 out_cm = open(opts.output, "wb")
106 117
107 with out_cm as outfp: 118 with out_cm as outfp:
108 for d in opts.directories: 119 for d in opts.directories:
109 generate_treesum_for_directory( 120 generate_treesum_for_directory(
110 outfp, d, opts.algorithm, opts.mmap, opts.base64) 121 outfp, d, opts.algorithm, opts.mmap, opts.base64, opts.logical)
111 122
112 123
113 def generate_treesum_for_directory( 124 def generate_treesum_for_directory(
114 outfp, root, algorithm, use_mmap, use_base64): 125 outfp, root, algorithm, use_mmap, use_base64, handle_root_logical):
115 """ 126 """
116 127
117 :param outfp: a *binary* file with a "write()" and a "flush()" method 128 :param outfp: a *binary* file with a "write()" and a "flush()" method
118 129
119 """ 130 """
120 outfp.write(format_bsd_line("ROOT", None, root, False)) 131 outfp.write(format_bsd_line("ROOT", None, root, False))
121 outfp.flush() 132 outfp.flush()
133
134 # Note given non-default flags that are relevant for directory traversal
135 flags = []
136 if handle_root_logical:
137 flags.append(b"logical")
138 if flags:
139 outfp.write(format_bsd_line("FLAGS", None, b",".join(flags), False))
140
122 dir_digests = {} 141 dir_digests = {}
142
143 if not handle_root_logical and os.path.islink(root):
144 linktgt = util.fsencode(os.readlink(root))
145 linkdgst = algorithm[0]()
146 linkdgst.update(linktgt)
147 dir_dgst = algorithm[0]()
148 dir_dgst.update(b"1:S,4:/./@,")
149 dir_dgst.update(linkdgst.digest())
150 outfp.write(format_bsd_line(
151 algorithm[1],
152 dir_dgst.digest(),
153 "/./@",
154 use_base64))
155 outfp.flush()
156 return
123 157
124 for top, dirs, nondirs in walk.walk(root, follow_symlinks=False): 158 for top, dirs, nondirs in walk.walk(root, follow_symlinks=False):
125 dir_dgst = algorithm[0]() 159 dir_dgst = algorithm[0]()
126 for dn in dirs: 160 for dn in dirs:
127 if dn.is_symlink: 161 if dn.is_symlink: