Add bounded generation transition receipts
This commit is contained in:
parent
4cc6277054
commit
9a48233983
21 changed files with 3822 additions and 65 deletions
71
src/docforge/_fs_safety.py
Normal file
71
src/docforge/_fs_safety.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
"""Internal directory binding helpers for disposable publication paths."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
from .errors import DocForgeError
|
||||
|
||||
|
||||
def open_bound_directory(path: Path) -> int:
|
||||
"""Open one real directory and bind its current inode for later operations."""
|
||||
|
||||
try:
|
||||
path_status = path.lstat()
|
||||
if (
|
||||
stat.S_ISLNK(path_status.st_mode)
|
||||
or not stat.S_ISDIR(path_status.st_mode)
|
||||
or path.resolve(strict=True) != path
|
||||
):
|
||||
raise DocForgeError(
|
||||
"path_escape",
|
||||
"Derived cache root is not a safe real directory",
|
||||
)
|
||||
directory_fd = os.open(path, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
|
||||
except FileNotFoundError as error:
|
||||
raise DocForgeError(
|
||||
"missing_index",
|
||||
"Derived cache root does not exist",
|
||||
) from error
|
||||
except OSError as error:
|
||||
raise DocForgeError(
|
||||
"path_escape",
|
||||
"Derived cache root cannot be opened safely",
|
||||
) from error
|
||||
try:
|
||||
opened_status = os.fstat(directory_fd)
|
||||
if opened_status.st_dev != path_status.st_dev or opened_status.st_ino != path_status.st_ino:
|
||||
raise DocForgeError(
|
||||
"path_escape",
|
||||
"Derived cache root changed while opening",
|
||||
)
|
||||
except Exception:
|
||||
os.close(directory_fd)
|
||||
raise
|
||||
return directory_fd
|
||||
|
||||
|
||||
def require_bound_directory(path: Path, directory_fd: int) -> None:
|
||||
"""Require a path to still name the exact opened real directory."""
|
||||
|
||||
try:
|
||||
path_status = path.lstat()
|
||||
opened_status = os.fstat(directory_fd)
|
||||
if (
|
||||
stat.S_ISLNK(path_status.st_mode)
|
||||
or not stat.S_ISDIR(path_status.st_mode)
|
||||
or path.resolve(strict=True) != path
|
||||
or opened_status.st_dev != path_status.st_dev
|
||||
or opened_status.st_ino != path_status.st_ino
|
||||
):
|
||||
raise DocForgeError(
|
||||
"path_escape",
|
||||
"Derived cache root changed during publication",
|
||||
)
|
||||
except FileNotFoundError as error:
|
||||
raise DocForgeError(
|
||||
"path_escape",
|
||||
"Derived cache root disappeared during publication",
|
||||
) from error
|
||||
Loading…
Add table
Add a link
Reference in a new issue