Files
Elydora-Python-SDK/elydora/plugins/_durable_file.py
T
雷电芽衣 ed34831c20 chore(python): audit plugins shared modules
_managed_files owns the file snapshot reader and public metadata helpers used by pinned directories, durable copies, and opaque snapshots; transaction staging owns read_target and same_identity_and_contents; dead helpers removed; hook_template no longer re-exports the guard generator; mypy targets win32 so _windows_security type-checks.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
2026-09-02 23:16:15 -04:00

124 lines
4.3 KiB
Python

"""Durable, identity-bound file copy and replacement primitives."""
from __future__ import annotations
import os
import stat
from typing import Any, Optional, Tuple
from ._managed_files import same_file_metadata
from ._pinned_directory import PinnedDirectory
_COPY_CHUNK_BYTES = 1024 * 1024
FileIdentity = Tuple[int, int]
def _regular_identity(
metadata: os.stat_result,
expected: FileIdentity,
path: str,
label: str,
) -> None:
if (
not stat.S_ISREG(metadata.st_mode)
or stat.S_ISLNK(metadata.st_mode)
or (metadata.st_dev, metadata.st_ino) != expected
):
raise OSError(f"{label} changed while copying: {path}")
def copy_into_reserved(
directory: PinnedDirectory,
source_path: str,
destination_path: str,
source_identity: FileIdentity,
destination_identity: FileIdentity,
mode: int,
label: str,
maximum_bytes: Optional[int] = None,
) -> None:
"""Copy one physical file into an existing private reservation and fsync it."""
source_name = directory.name_for(source_path)
destination_name = directory.name_for(destination_path)
read_flags = os.O_RDONLY | getattr(os, "O_BINARY", 0)
read_flags |= getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0)
write_flags = os.O_WRONLY | getattr(os, "O_BINARY", 0)
write_flags |= getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0)
source_descriptor = -1
destination_descriptor = -1
try:
source_descriptor = directory.open_file(source_name, read_flags)
before = os.fstat(source_descriptor)
_regular_identity(before, source_identity, source_path, label)
destination_descriptor = directory.open_file(destination_name, write_flags)
destination = os.fstat(destination_descriptor)
_regular_identity(
destination,
destination_identity,
destination_path,
f"{label} destination",
)
os.ftruncate(destination_descriptor, 0)
total = 0
while True:
chunk = os.read(source_descriptor, _COPY_CHUNK_BYTES)
if not chunk:
break
total += len(chunk)
if maximum_bytes is not None and total > maximum_bytes:
raise ValueError(
f"{label} exceeds {maximum_bytes} bytes: {source_path}"
)
offset = 0
while offset < len(chunk):
written = os.write(destination_descriptor, chunk[offset:])
if written <= 0:
raise OSError(f"Copy {label} made no progress: {destination_path}")
offset += written
directory.chmod_descriptor(destination_descriptor, mode)
os.fsync(destination_descriptor)
after = os.fstat(source_descriptor)
if not same_file_metadata(after, before):
raise OSError(f"{label} changed while copying: {source_path}")
finally:
for descriptor in (destination_descriptor, source_descriptor):
if descriptor >= 0:
os.close(descriptor)
visible = directory.stat_file(source_name)
_regular_identity(visible, source_identity, source_path, label)
directory.sync()
def atomic_replace(
directory: PinnedDirectory,
source_path: str,
destination_path: str,
) -> None:
"""Atomically replace one same-directory name and persist its entry."""
if os.name == "nt":
import ctypes as ctypes_module
from ctypes import wintypes
ctypes: Any = ctypes_module
source = directory.path_for(directory.name_for(source_path))
destination = directory.path_for(directory.name_for(destination_path))
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
move = kernel32.MoveFileExW
move.argtypes = [wintypes.LPCWSTR, wintypes.LPCWSTR, wintypes.DWORD]
move.restype = wintypes.BOOL
replace_existing = 0x00000001
write_through = 0x00000008
if not move(source, destination, replace_existing | write_through):
error = ctypes.get_last_error()
raise OSError(f"Atomically replace {destination}: {ctypes.WinError(error)}")
return
directory.replace_file(
directory.name_for(source_path),
directory.name_for(destination_path),
)
directory.sync()
__all__ = ["atomic_replace", "copy_into_reserved"]