System Security / Foundations
A backup needs a recovery test
Perform a real file backup and isolated restore, then detect both a missing file and corrupted content with a SHA-256 manifest.
Start hands-on practice · เริ่มภาคปฏิบัติ ↓Practical guide · คู่มือลงมือทำ
Download worksheet · ดาวน์โหลดขั้นตอนDetailed practical guide
Prepare your workspace and inputs
Save the program as restore_check.py and run python3 restore_check.py. It creates three folders inside a new temporary workspace and never accepts a user-supplied deletion path. The source data is synthetic.
Platform context: Python 3.10+; local temporary directories and standard library. This is a file-level recovery test, not a database-consistent backup.
Scenario to reproduce or investigate
A backup job reports success but nobody has checked whether the restored files are complete and unchanged. The test must validate a restore, not just the existence of a backup directory.
- Actual target and platform release
- Exact inputs or substituted values
- Baseline result
- Per-step expected and observed output
- Failure encountered and corrective action
- Final acceptance and recovery state
-
Define the recovery boundary and file inventory
The backup boundary contains three small UTF-8 files. Record filenames and SHA-256 digests before copying. A file written after this manifest belongs to a later recovery point and must not silently enter the expected result.
Command, configuration or input
Expected files: customers.txt, settings.txt, readme.txt Expected recovery point: the source state immediately before copytreeExpected result · ผลที่ควรเห็นThe recovery checklist names exact files and their hashes rather than relying on the folder size.
If it fails · เมื่อไม่ผ่านFor a database or live application, replace a plain file copy with that product’s supported consistent backup method; this exercise does not freeze live writes.
-
Create, restore and deliberately corrupt the copy
Run the complete script. It verifies a successful restore, changes one restored file, removes another and confirms that each failure is detected. The assertions exercise real filesystem operations in a disposable directory.
Save and run this example
from pathlib import Path from tempfile import TemporaryDirectory from hashlib import sha256 from time import perf_counter import shutil def manifest(root): records = {} for path in sorted(root.rglob("*")): if path.is_file(): digest = sha256() with path.open("rb") as handle: for chunk in iter(lambda: handle.read(1024 * 1024), b""): digest.update(chunk) records[str(path.relative_to(root))] = digest.hexdigest() return records with TemporaryDirectory(prefix="sorlavit-restore-") as directory: workspace = Path(directory) source = workspace / "source" source.mkdir() for name, value in {"customers.txt": "customer-001\n", "settings.txt": "region=lab\n", "readme.txt": "synthetic backup\n"}.items(): (source / name).write_text(value, encoding="utf-8") expected = manifest(source) backup = workspace / "backup" restored = workspace / "restored" shutil.copytree(source, backup) (source / "settings.txt").write_text("region=changed-after-backup\n", encoding="utf-8") started = perf_counter() shutil.copytree(backup, restored) assert manifest(restored) == expected duration = perf_counter() - started (restored / "settings.txt").write_text("corrupt\n", encoding="utf-8") assert manifest(restored) != expected shutil.copy2(backup / "settings.txt", restored / "settings.txt") (restored / "readme.txt").unlink() assert manifest(restored) != expected shutil.copy2(backup / "readme.txt", restored / "readme.txt") assert manifest(restored) == expected print("PASS: 3 files restored; corruption and missing-file checks detected") print("Observed restore seconds:", round(duration, 6))Expected result · ผลที่ควรเห็นThe script prints PASS: 3 files restored; corruption and missing-file checks detected, followed by the measured restore duration.
If it fails · เมื่อไม่ผ่านA permission or space error means the restore did not pass. Keep the backup and record the failing operation before retrying on a destination with sufficient capacity.
-
Run the same verification against an approved backup copy
For a real drill, choose an existing recovery point and restore it to an isolated destination through the supported backup product. Compare the restored file manifest with the inventory recorded at that recovery point, open representative documents and check permissions. Record restore start, ready-for-use time, missing files and the person accepting the result. Measure application recovery separately from copying bytes.
Command, configuration or input
Recovery point | expected files | restored files | hash mismatches | permission result | usable-at timeExpected result · ผลที่ควรเห็นA recovery record identifies the actual restore point and has evidence that a user can use the restored data.
If it fails · เมื่อไม่ผ่านIf no historical manifest exists, record that limitation and verify known application records or document samples. Do not call the current source folder the historical truth.
Check the result before marking this passed
All three restored files match the pre-copy manifest, and the intentionally corrupted and missing-file cases are detected. The real drill additionally needs permission and application-use checks appropriate to its backup type.
Record the observed result and evidence location in the downloaded worksheet. Mark unresolved or untested conditions explicitly.
Recovery and cleanup
The script removes only its own TemporaryDirectory. For a real drill, obtain acceptance before removing the isolated restore; keep the original backup untouched and record any changes to recovery permissions.
Apply to actual work and known limits
Use this manifest approach to supplement, not replace, a supported backup product. Add encryption-key recovery, identity dependencies and measured application recovery time to the production runbook.
Observed local fixture output · ผลรันตัวอย่าง
Create, restore and deliberately corrupt the copy
PASS: 3 files restored; corruption and missing-file checks detected Observed restore seconds: 0.000788
Background, explanations and original notes
Prepare the exact environment
Save the program as restore_check.py and run python3 restore_check.py. It creates three folders inside a new temporary workspace and never accepts a user-supplied deletion path. The source data is synthetic.
Define the recovery boundary and file inventory
The backup boundary contains three small UTF-8 files. Record filenames and SHA-256 digests before copying. A file written after this manifest belongs to a later recovery point and must not silently enter the expected result.
Expected files: customers.txt, settings.txt, readme.txt
Expected recovery point: the source state immediately before copytree
Create, restore and deliberately corrupt the copy
Run the complete script. It verifies a successful restore, changes one restored file, removes another and confirms that each failure is detected. The assertions exercise real filesystem operations in a disposable directory.
from pathlib import Path
from tempfile import TemporaryDirectory
from hashlib import sha256
from time import perf_counter
import shutil
def manifest(root):
records = {}
for path in sorted(root.rglob("*")):
if path.is_file():
digest = sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
records[str(path.relative_to(root))] = digest.hexdigest()
return records
with TemporaryDirectory(prefix="sorlavit-restore-") as directory:
workspace = Path(directory)
source = workspace / "source"
source.mkdir()
for name, value in {"customers.txt": "customer-001\n", "settings.txt": "region=lab\n", "readme.txt": "synthetic backup\n"}.items():
(source / name).write_text(value, encoding="utf-8")
expected = manifest(source)
backup = workspace / "backup"
restored = workspace / "restored"
shutil.copytree(source, backup)
(source / "settings.txt").write_text("region=changed-after-backup\n", encoding="utf-8")
started = perf_counter()
shutil.copytree(backup, restored)
assert manifest(restored) == expected
duration = perf_counter() - started
(restored / "settings.txt").write_text("corrupt\n", encoding="utf-8")
assert manifest(restored) != expected
shutil.copy2(backup / "settings.txt", restored / "settings.txt")
(restored / "readme.txt").unlink()
assert manifest(restored) != expected
shutil.copy2(backup / "readme.txt", restored / "readme.txt")
assert manifest(restored) == expected
print("PASS: 3 files restored; corruption and missing-file checks detected")
print("Observed restore seconds:", round(duration, 6))
Run the same verification against an approved backup copy
For a real drill, choose an existing recovery point and restore it to an isolated destination through the supported backup product. Compare the restored file manifest with the inventory recorded at that recovery point, open representative documents and check permissions. Record restore start, ready-for-use time, missing files and the person accepting the result. Measure application recovery separately from copying bytes.
Recovery point | expected files | restored files | hash mismatches | permission result | usable-at time
Acceptance check
All three restored files match the pre-copy manifest, and the intentionally corrupted and missing-file cases are detected. The real drill additionally needs permission and application-use checks appropriate to its backup type.
Recovery and cleanup
The script removes only its own TemporaryDirectory. For a real drill, obtain acceptance before removing the isolated restore; keep the original backup untouched and record any changes to recovery permissions.
Apply to actual work and known limits
Use this manifest approach to supplement, not replace, a supported backup product. Add encryption-key recovery, identity dependencies and measured application recovery time to the production runbook.