"""Restore byte-verified multi-archive objects to a fresh destination only.""" import argparse,contextlib,gzip,hashlib,json,os,tarfile from pathlib import Path def sha(path): with Path(path).open('rb')as f:return hashlib.file_digest(f,'sha256').hexdigest() p=argparse.ArgumentParser();p.add_argument('manifest');p.add_argument('destination');p.add_argument('archives',nargs='+');a=p.parse_args();mp=Path(a.manifest);raw=gzip.decompress(mp.read_bytes())if mp.suffix=='.gz'else mp.read_bytes();m=json.loads(raw);dest=Path(a.destination).resolve();rows=m['members'];expected={v['sha256']:k for k,v in m['archives'].items()};provided={} for archive in a.archives: h=sha(archive);assert h in expected and expected[h] not in provided;provided[expected[h]]=Path(archive) needed={x['archive']for x in rows if x['type']=='file'};assert needed<=provided.keys();assert len({x['path']for x in rows})==len(rows);by_hash={} for x in rows: path=Path(x['path']);assert not path.is_absolute()and '..'not in path.parts and '.'not in path.parts;q=dest/path;assert q.resolve().is_relative_to(dest)and not q.exists()and not q.is_symlink() if x['type']=='file':by_hash.setdefault(x['sha256'],[]).append(x) else:assert x['type']=='directory' for x in rows: if x['type']=='directory':(dest/x['path']).mkdir(parents=True,exist_ok=True) seen=set();restored=0 for label,path in provided.items(): with tarfile.open(path,'r|gz')as t: for entry in t: assert entry.isfile() if not entry.name.startswith('objects/'):continue key=entry.name[8:];group=[x for x in by_hash.get(key,[])if x['archive']==label] if not group:continue assert key not in seen;seen.add(key);assert all(x['bytes']==entry.size for x in group);digest=hashlib.sha256() with contextlib.ExitStack()as stack: handles=[stack.enter_context((dest/x['path']).open('xb'))for x in group];source=stack.enter_context(t.extractfile(entry)) for chunk in iter(lambda:source.read(1024**2),b''): digest.update(chunk) for f in handles:f.write(chunk) assert digest.hexdigest()==key for x in group: q=dest/x['path'];assert q.stat().st_size==x['bytes'];os.chmod(q,x['mode']);os.utime(q,ns=(x['mtime_ns'],x['mtime_ns']));restored+=1 assert seen==set(by_hash)and restored==m['files'] for x in reversed(rows): if x['type']=='directory':q=dest/x['path'];os.chmod(q,x['mode']);os.utime(q,ns=(x['mtime_ns'],x['mtime_ns'])) print('Restored and verified',restored,'files')