Files
codex-app-server/cloud/chv/atomic_file.py
T
host c39715659e fix(chv): P2-20/P2-19/P2-17/P2-18 残留收尾 + NO_PROXY 合并
- P2-20:_guest_env_delivered 加专用锁,读写只走
  _delivered_secrets/_mark/_clear 三个入口,并发 hammer 测试钉死
- P2-19:_record_net_entries 失败发 net_registry_write_failed
  独立审计并返回 False;_boot_vm 见 False 即拒起(wake 随后清半醒 VM)
- P2-17:net.json 删失败发 vm_net_registry_delete_failed 审计;
  _write_dirty_marker 失败发 dirty_marker_write_failed 审计并返回
  False,stop 不抛的契约不变
- P2-18:新增 cloud/chv/atomic_file.py(mkstemp 随机名 + 文件
  fsync + rename + 目录 fsync),net.json/known_hosts/
  snapshots.json/pool registry/脏标记/spill 全部走它,固定 .tmp 清零
- H1 附带:NO_PROXY 实测确有覆盖 bug(env 重复赋值以后者为准,
  调用方 NO_PROXY 被 egress 的 localhost 值静默覆盖),改为合并
  单条发出;egress 已设时调用方代理变量(含小写)一律不发
- 测试:新增 cloud/tests/test_chv_residual.py(23 项),旧 closeup
  两处钉死旧语义的测试按新语义更新
2026-11-06 18:41:50 +00:00

72 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""CHV 宿主侧小文件的原子写(P2-18 收尾)。
此前 net.json / snapshots.json / pool registry /
known_hosts 各自用固定名 `<path>.tmp` + 直接 os.replace,
存在两个问题:
1. 固定 tmp 名:同一进程内两个写者(或一次崩溃残留的
.tmp)会互相覆盖/误读;不同 VM 的文件虽在不同目录,
但同一目录内的快照登记与 net 登记仍共用可预测的
tmp 名,崩溃后残留的 .tmp 会被下一次写静默覆盖,
丢了“上次写了一半”的信号。
2. 无 fsync:os.replace 只保证目录项原子,不保证内容
已落盘;宿主断电/崩溃后新文件可能回到旧内容或
0 字节,而调用方已认为登记成功。
本模块是唯一写路径:mkstemp 随机名(0600)→ 写 →
flush + fsync 文件 → os.replace → fsync 父目录。
写失败时删临时文件再原样抛错,调用方按各自语义
处理(审计/拒绝/脏标记),本模块不吞错、不记日志。
"""
import os
import tempfile
def atomic_write(path: str, data: bytes, mode: int = 0o600) -> None:
"""原子写 bytes 到 path(随机 tmp 名 + 双 fsync)。
mode 是最终文件的权限(默认 0600:这些文件都在
0700 的 state_dir 内、且含登记/密钥类内容,按敏感
文件对待;调用方要放宽时显式传)。
"""
directory = os.path.dirname(path) or "."
fd, tmp_path = tempfile.mkstemp(prefix=".tmp-", dir=directory)
try:
os.fchmod(fd, mode)
with os.fdopen(fd, "wb") as fh:
fh.write(data)
fh.flush()
os.fsync(fh.fileno())
os.replace(tmp_path, path)
_fsync_dir(directory)
except BaseException:
try:
os.unlink(tmp_path)
except OSError:
pass
raise
def atomic_write_text(path: str, text: str, mode: int = 0o600) -> None:
"""atomic_write 的 str 版(UTF-8 编码)。"""
atomic_write(path, text.encode("utf-8"), mode=mode)
def _fsync_dir(directory: str) -> None:
"""fsync 父目录,让 rename 本身落盘(best-effort:
某些文件系统不支持目录 fsync,失败即跳过——文件
内容的 fsync 已在 atomic_write 里做完,目录 fsync
丢了只影响“rename 是否在崩溃后可见”,不影响内容)。
"""
try:
dir_fd = os.open(directory, os.O_RDONLY)
except OSError:
return
try:
os.fsync(dir_fd)
except OSError:
pass
finally:
os.close(dir_fd)