#!/usr/bin/env python3 """File Hash Validator - verify SHA256 checksums for file integrity. Usage: python file_hash_validator.py python file_hash_validator.py --verify A checksum file format: " " (same as sha256sum output) """ import hashlib import sys from pathlib import Path def compute_sha256(filepath: str) -> str: """Compute SHA256 hash of a file.""" sha256_hash = hashlib.sha256() with open(filepath, "rb") as f: for chunk in iter(lambda: f.read(8192), b""): sha256_hash.update(chunk) return sha256_hash.hexdigest() def verify_file(filepath: str, expected_hash: str) -> bool: """Verify a file's hash matches expected value.""" if not Path(filepath).exists(): print(f"ERROR: File not found: {filepath}") return False actual = compute_sha256(filepath) if actual.lower() == expected_hash.lower(): print(f"OK: {filepath}") print(f" SHA256: {actual}") return True else: print(f"FAIL: {filepath}") print(f" Expected: {expected_hash}") print(f" Actual: {actual}") return False def verify_checksum_file(checksum_file: str) -> int: """Verify all files in a checksum file. Returns count of failed verifications.""" if not Path(checksum_file).exists(): print(f"ERROR: Checksum file not found: {checksum_file}") return 1 failed = 0 with open(checksum_file, "r") as f: for line in f: line = line.strip() if not line or line.startswith("#"): continue parts = line.split() if len(parts) >= 2: expected_hash = parts[0] filepath = parts[1] if not verify_file(filepath, expected_hash): failed += 1 else: print(f"SKIP: Malformed line: {line}") if failed == 0: print(f"\nAll files verified successfully.") else: print(f"\n{failed} file(s) failed verification.") return failed def main(): if len(sys.argv) < 3: print(__doc__) sys.exit(1) if sys.argv[1] == "--verify": exit_code = verify_checksum_file(sys.argv[2]) else: exit_code = 0 if verify_file(sys.argv[1], sys.argv[2]) else 1 sys.exit(exit_code) if __name__ == "__main__": main()