fuse: make size merge and modified check atomic - #7304
Open
Xuyuchao-juice wants to merge 4 commits into
Open
Xuyuchao-juice wants to merge 4 commits into
Xuyuchao-juice wants to merge 4 commits into
Conversation
Xuyuchao-juice
force-pushed
the
attr_length
branch
4 times, most recently
from
July 22, 2026 07:54
4d39147 to
e436a2a
Compare
Xuyuchao-juice
force-pushed
the
attr_length
branch
from
July 22, 2026 08:41
e436a2a to
1c3360c
Compare
Xuyuchao-juice
marked this pull request as ready for review
July 22, 2026 08:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
close #7271
Introduce VFS.MergeWriterLength to atomically combine modified-since detection with writer-length merge (and reader length sync) under modM.
Use it in FUSE replyAttr and WinFSP readdir-plus stat fill, including re-merge after GetAttr refresh, to avoid stale size caching and split-check TOCTOU races.
Also refactor UpdateLength to reuse a shared mergeLength helper.
增加vfs.MergeWriterLength,用于在modLock 下原子化的将 上次修改时间检测和 writer.length合并结合。
在fuse.replyAttr和 winfsp的 readdir-plus中使用 这个功能,可以解决之前遇到的一系列竞态问题,详细case如下:
case1: (Initial stale size issue)
t0: Process p1 calls write to write 100 bytes, then calls close(). Inside close(), it needs to wait for commitThread.
t1: Process p2 calls stat, first invoking meta.GetAttr() and obtaining a size of 0 (since commitThread has not yet completed).
t2: Process p2 calls MergeWriterLength, retrieving the correct writer.Length for merging. Due to the lock, close() will wait until this operation completes before releasing the writer.
Conclusion: ✅
case2:
t0: Process p1 calls write to write 100 bytes, then calls close(). Inside close(), it needs to wait for commitThread.
t1: Process p2 calls stat, first invoking meta.GetAttr() and obtaining size 0 (because commitThread has not yet completed).
t2: Process p1 completes the commit and releases the writer.
t3: Process p2 calls mergeWriterLength, detects that modifiedTime has changed, and triggers a second m.GetAttr().
Conclusion: ✅
case3:
t0: Process p2 calls stat first, invoking meta.GetAttr() to get size 0.
t1: Process p1 calls write to write 100 bytes, then close(), waiting for commitThread.
t2: Process p2 calls MergeWriterLength, obtaining the correct writer.Length for merging. Since a lock is held, close() will wait until this operation completes before releasing the writer.
Conclusion: ✅
case4:
t0: Process p2 first calls stat, invoking meta.GetAttr() to obtain a size of 0.
t1: Process p2 calls MergeWriterLength; the size remains 0.
t2: Process p1 begins writing 100 bytes and completes all close operations.
t3: Process p2 returns result 0 to the kernel, which is still an outdated value.
The correctness of this case is guaranteed by the kernel. If the write occurs after the stat, the kernel can recognize it as stale via attr_version and will not cache it.
Conclusion: ✅
case1:(最初的stale size问题)
t0: p1 进程调用write 写入100字节,然后调用close() ,close内部需要等待commitThread
t1: p2 进程调用stat,先调用meta.GetAttr() 得到size 0 (因为commitThread还未完成)
t2: p2 进程调用MergeWriterLength,拿到正确的writer.Length进行merge。因为加了锁,此时close会等待这里完成才释放writer
结论:✅
case2:
t0: p1 进程调用write 写入100字节,然后调用close() ,close内部需要等待commitThread
t1: p2 进程调用stat,先调用meta.GetAttr() 得到size 0 (因为commitThread还未完成)
t2: p1 进程 commit完成,释放writer
t3: p2 调用mergeWriterLength,发现modifiedTime被更改,触发二次m.GetAttr()。
结论:✅
case3:
t0: p2进程 先stat,调用meta.GetAttr() 拿到size 0
t1: p1 进程调用write写入100字节,然后close() 等待commitThread
t2: p2 进程调用MergeWriterLength,拿到正确的writer.Length进行merge。因为加了锁,此时close会等待这里完成才释放writer
结论:✅
case4:
t0: p2进程 先stat,调用meta.GetAttr() 拿到size 0
t1: p2 进程调用MergeWriterLength,size依然是0
t2: p1 进程开始write 100字节 ,并完成所有close操作。
t3: p2 返回结果 0 给内核,依然是过期的结果。
这个case的正确性由内核保证,如果write发生在stat以后,kernel可以通过attr_version识别出这是一个过期的结果,不会缓存
结论:✅