27 lines
2.1 KiB
Python
27 lines
2.1 KiB
Python
"""Restore archived historical ZIPs and exact distribution bytes to a fresh folder."""
|
|
import argparse,hashlib,json,os,shutil,zipfile
|
|
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');p.add_argument('destination');p.add_argument('archives',nargs='+');a=p.parse_args();m=json.loads(Path(a.manifest).read_text());root=Path(a.destination).resolve();archives={}
|
|
for name in a.archives:
|
|
path=Path(name);h=sha(path);assert h not in archives;archives[h]=path
|
|
assert set(archives)=={x['zip']['sha256']for x in m['items']}
|
|
for item in m['items']:
|
|
dest=root/item['name'];assert dest.resolve().is_relative_to(root)and not dest.exists()and not dest.is_symlink();source=archives[item['zip']['sha256']];assert source.stat().st_size==item['zip']['bytes'];dest.mkdir(parents=True)
|
|
target=dest/Path(item['zip']['path']).name
|
|
with source.open('rb')as inp,target.open('xb')as out:shutil.copyfileobj(inp,out,1024**2)
|
|
assert sha(target)==item['zip']['sha256'];os.chmod(target,item['zip']['mode']);os.utime(target,ns=(item['zip']['mtime_ns'],item['zip']['mtime_ns']))
|
|
files={x['relative_path']:x for x in item['files']}
|
|
with zipfile.ZipFile(source)as archive:
|
|
assert set(archive.namelist())==set(files)and len(archive.infolist())==len(files)
|
|
for name,x in files.items():
|
|
relative=Path(name);assert not relative.is_absolute()and '..'not in relative.parts;path=dest/'distribution'/relative;assert path.resolve().is_relative_to(dest)and not path.exists();path.parent.mkdir(parents=True,exist_ok=True);h=hashlib.sha256()
|
|
with archive.open(name)as inp,path.open('xb')as out:
|
|
for chunk in iter(lambda:inp.read(1024**2),b''):h.update(chunk);out.write(chunk)
|
|
assert h.hexdigest()==x['sha256']and path.stat().st_size==x['bytes'];os.chmod(path,x['mode']);os.utime(path,ns=(x['mtime_ns'],x['mtime_ns']))
|
|
for d in reversed(item['directories']):
|
|
path=dest/'distribution'/d['path'];os.chmod(path,d['mode']);os.utime(path,ns=(d['mtime_ns'],d['mtime_ns']))
|
|
print('Verified and restored',len(m['items']),'historical packages')
|