Skip to content

fix(safe-write-file): replace truncate-then-write with write-then-truncate#68

Merged
luojiyin1987 merged 1 commit into
masterfrom
fix/safe-write-inplace
Jul 5, 2026
Merged

fix(safe-write-file): replace truncate-then-write with write-then-truncate#68
luojiyin1987 merged 1 commit into
masterfrom
fix/safe-write-inplace

Conversation

@luojiyin1987

Copy link
Copy Markdown
Contributor

Closes #67

变更

  • 保留 in-place update 语义,不再使用 temp-file + rename 替换文件
  • O_WRONLY 改为 O_RDWR,继续使用 O_NOFOLLOW
  • truncate(0) → write 改为 write from offset 0 → truncate(newLength)
  • handle.write() 改为循环写入,不假设一次写完全部 buffer
  • 写入后调用 handle.sync(),请求将文件内容和 size 元数据刷盘

改进点

消除 truncate(0) 成功但写入失败时,目标文件变成空文件的最坏情况。

相比 temp-file + rename,本方案保留 in-place update 语义,更符合 lint-md --fix 对修改原文件的用户预期。它能保留 inode / hard link / owner / ACL / xattr / birthtime 等文件身份相关属性;mtime / ctime 会按正常文件修改语义更新。

仍不保证

  • 不是完全原子写入;崩溃时可能出现部分新内容或旧尾部残留
  • 不提供跨文件事务
  • 不额外处理极端掉电或文件系统异常场景

测试

  • 39 项测试通过,lint 和 build 通过
  • 新增 mode 保留测试、短内容尾部截断测试

…ncate

Keep in-place update semantics to preserve inode, hard link,
birthtime, owner, ACL, xattr etc. Write from offset 0 first,
then truncate to new length, avoiding the truncate(0) worst case
where a failed write leaves an empty file.

Add handle.sync() to request content and size metadata flush.
Add mode preservation and tail truncation tests.
@luojiyin1987 luojiyin1987 force-pushed the fix/safe-write-inplace branch from 511b721 to 78937f8 Compare July 5, 2026 16:17
@luojiyin1987 luojiyin1987 merged commit 65f8c0e into master Jul 5, 2026
5 checks passed
@luojiyin1987 luojiyin1987 deleted the fix/safe-write-inplace branch July 5, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

safe-write-file: truncate-then-write 可能导致文件变为空文件

1 participant