"""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