Files

28 lines
1.8 KiB
Python

"""Restore historical P23 impact proof into a fresh directory; no overwrite."""
import argparse,gzip,hashlib,json,os,tarfile
from pathlib import Path
def sha(p):
with p.open('rb') as f:return hashlib.file_digest(f,'sha256').hexdigest()
p=argparse.ArgumentParser();p.add_argument('manifest',type=Path);p.add_argument('archive',type=Path);p.add_argument('target',type=Path);a=p.parse_args()
with gzip.open(a.manifest,'rt') as f:m=json.load(f)
assert sha(a.archive)==m['archive']['sha256'] and a.archive.stat().st_size==m['archive']['bytes']
assert not a.target.exists();a.target.mkdir(parents=True)
files={x['path']:x for x in m['files']};dirs={x['path']:x for x in m['directories']}
with tarfile.open(a.archive,'r:gz') as t:
members=t.getmembers();assert len(members)==len(files)+len(dirs) and {x.name for x in members}==set(files)|set(dirs)
for x in members:
n=Path(x.name);assert not n.is_absolute() and '..' not in n.parts and not x.issym() and not x.islnk()
dest=a.target/n
if x.isdir():assert x.name in dirs;dest.mkdir(parents=True,exist_ok=True)
else:
assert x.isfile() and x.name in files
dest.parent.mkdir(parents=True,exist_ok=True)
with t.extractfile(x) as source,dest.open('xb') as out:
while block:=source.read(1024**2):out.write(block)
for row in m['files']:
f=a.target/row['path'];assert f.stat().st_size==row['bytes'] and sha(f)==row['sha256'];os.chmod(f,row['mode']);os.utime(f,ns=(row['mtime_ns'],row['mtime_ns']))
for row in sorted(m['directories'],key=lambda x:len(Path(x['path']).parts),reverse=True):
f=a.target/row['path'];os.chmod(f,row['mode']);os.utime(f,ns=(row['mtime_ns'],row['mtime_ns']))
print(json.dumps({'restored_files':len(files),'all_bytes_sha256_verified':True,'mode_mtime_ns_restored':True,'target':str(a.target)}))