1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
|
#!/usr/bin/python3
import os
import sys
import shlex
import hashlib
import stat
import argparse
def log_error(error):
print("Readdir error: " + error)
def should_convert_whiteout(stbuf):
return args.whiteout and stat.S_ISCHR(stbuf.st_mode) and stbuf.st_rdev == os.makedev(0,0)
def has_whiteout_child(path):
for f in os.listdir(path):
s = os.lstat(os.path.join(path, f))
if should_convert_whiteout(s):
return True
return False
def dumpfile(file, root):
rel = os.path.relpath(file, root)
s = os.lstat(file)
st_mode = s.st_mode
content = None
xattrs = {}
if should_convert_whiteout(s):
content = b''
st_mode = (st_mode & ~stat.S_IFMT(st_mode)) | stat.S_IFREG
xattrs["trusted.overlay.overlay.whiteout"] = b''
xattrs["user.overlay.whiteout"] = b''
if stat.S_ISDIR(st_mode) and has_whiteout_child(file):
xattrs["trusted.overlay.overlay.whiteouts"] = b''
xattrs["user.overlay.whiteouts"] = b''
xattrs["trusted.overlay.overlay.opaque"] = b'x'
xattrs["user.overlay.opaque"] = b'x'
nlink = s.st_nlink;
if args.no_nlink or (args.no_root_nlink and file == root):
nlink = 1
print(f"{shlex.quote(rel)} {oct(st_mode)} {nlink} {s.st_uid}:{s.st_gid} {s.st_rdev} {s.st_mtime_ns}",end="")
if stat.S_ISREG(st_mode):
if content == None:
content = open(file,'rb').read()
digest = hashlib.sha256(content).hexdigest()
print(f" {s.st_size} sha256:{digest}",end="")
elif stat.S_ISLNK(st_mode):
link = os.readlink(file)
print(f" ->{shlex.quote(link)}",end="")
for attr in sorted(os.listxattr(file, follow_symlinks=False)):
v = os.getxattr(file, attr, follow_symlinks=False)
xattrs[attr] = v
for attr in sorted(xattrs.keys()):
v = xattrs[attr]
if args.userxattr and not attr.startswith("user."):
continue
if args.noescaped and attr.startswith("trusted.overlay.overlay."):
continue
print(f" {attr}={v}", end="")
print()
def dumpdir(root):
dumpfile(root, root)
for parent, dirs, files in os.walk(root, topdown=True, onerror=log_error):
dirs.sort()
for file in sorted(dirs + files):
dumpfile(os.path.join(parent, file), root)
argParser = argparse.ArgumentParser()
argParser.add_argument("--no-nlink", action='store_true')
argParser.add_argument("--no-root-nlink", action='store_true')
argParser.add_argument("--userxattr", action='store_true')
argParser.add_argument("--whiteout", action='store_true')
argParser.add_argument("--noescaped", action='store_true')
argParser.add_argument('path')
args = argParser.parse_args()
dumpdir(args.path)
|