Reimplementazione fedele di cellar-cli.py (enne2/cellar @ f5216b1) in C99 POSIX.1-2008, mono-thread, senza dipendenze esterne: miniz e' vendored per deflate/inflate raw, mentre contenitore gzip, multipart/form-data in streaming, tar PAX/ustar, JSON e client HTTP/1.1 sono scritti a mano. - 7 comandi (list, upload, download, install, scan-local, wizard-upload, wizard-install) con stdout/stderr/exit code identici al client Python - 30/30 test di parita' (tests/parity_test.sh) contro due server mock indipendenti: output a confronto, alberi installati, interop tar con tarfile di Python e con GNU tar, archivio C equivalente a quello Python - build statiche x86-64 / i686 / aarch64: nessun simbolo GLIBC richiesto, nessuna syscall moderna (statx/openat2/memfd_create/getrandom), LFS 64 bit, resolver DNS di riserva (hosts + query UDP) per glibc statica senza NSS - miglioramento rispetto all'originale: upload multipart in streaming invece di leggere l'intero archivio in RAM - divergenze volute documentate nel README (help piu' sintetico, mtime dei symlink non ripristinato come tarfile, EOF nei prompt, niente TLS) Build: make | make 32 | make arm64 | make test | make verify
46 lines
1.5 KiB
Python
Executable File
46 lines
1.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Stampa una descrizione canonica di un archivio tar (per confronti di parita').
|
|
|
|
Uso: tar_dump.py <archivio.tar.gz> [--tolerance SECONDI]
|
|
|
|
Ogni riga: tipo modo uid gid mtime size linkname name sha256(contenuto)
|
|
Serve a verificare che l'archivio prodotto dal writer C sia strutturalmente
|
|
equivalente a quello prodotto da tarfile di Python.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import sys
|
|
import tarfile
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("archive")
|
|
ap.add_argument("--tolerance", type=float, default=1e-6)
|
|
args = ap.parse_args()
|
|
try:
|
|
with tarfile.open(args.archive, "r:gz") as tf:
|
|
members = tf.getmembers()
|
|
for m in sorted(members, key=lambda x: x.name):
|
|
digest = "-"
|
|
if m.isreg():
|
|
fh = tf.extractfile(m)
|
|
if fh is not None:
|
|
digest = hashlib.sha256(fh.read()).hexdigest()[:16]
|
|
link = m.linkname or "-"
|
|
print(
|
|
f"{m.type.decode() if isinstance(m.type, bytes) else m.type} "
|
|
f"{m.mode:04o} {m.uid} {m.gid} {m.mtime:.7f} {m.size} {link} "
|
|
f"{m.name} {digest}"
|
|
)
|
|
except Exception as exc: # noqa: BLE001
|
|
print(f"ERROR: {exc}", file=sys.stderr)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|