53 lines
1.7 KiB
Python
53 lines
1.7 KiB
Python
import shutil
|
|
from pathlib import Path
|
|
|
|
|
|
def contained_file(root: Path, storage_name: str) -> Path:
|
|
root = root.resolve()
|
|
candidate = (root / storage_name).resolve()
|
|
if candidate.parent != root:
|
|
raise ValueError("attachment path escapes storage root")
|
|
return candidate
|
|
|
|
|
|
def quarantine_files(root: Path, storage_names: list[str], quarantine: Path) -> list[tuple[Path, Path]]:
|
|
moved = []
|
|
quarantine.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
for name in storage_names:
|
|
source = contained_file(root, name)
|
|
if source.exists():
|
|
target = quarantine / name
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
source.replace(target)
|
|
moved.append((source, target))
|
|
return moved
|
|
except Exception:
|
|
restore_quarantine(moved)
|
|
raise
|
|
|
|
|
|
def restore_quarantine(moved: list[tuple[Path, Path]]) -> None:
|
|
for original, quarantined in reversed(moved):
|
|
if quarantined.exists():
|
|
original.parent.mkdir(parents=True, exist_ok=True)
|
|
quarantined.replace(original)
|
|
|
|
|
|
def restore_quarantine_dir(root: Path, quarantine: Path) -> None:
|
|
if not quarantine.exists():
|
|
return
|
|
for quarantined in sorted(quarantine.rglob("*")):
|
|
if not quarantined.is_file():
|
|
continue
|
|
relative = quarantined.relative_to(quarantine)
|
|
original = contained_file(root, relative.as_posix())
|
|
original.parent.mkdir(parents=True, exist_ok=True)
|
|
quarantined.replace(original)
|
|
shutil.rmtree(quarantine)
|
|
|
|
|
|
def remove_quarantine(path: Path) -> None:
|
|
if path.exists():
|
|
shutil.rmtree(path)
|