Skip to content

fuse: make size merge and modified check atomic - #7304

Open
Xuyuchao-juice wants to merge 4 commits into
mainfrom
attr_length
Open

Xuyuchao-juice wants to merge 4 commits into
mainfrom
attr_length

Conversation

@Xuyuchao-juice

@Xuyuchao-juice Xuyuchao-juice commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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识别出这是一个过期的结果,不会缓存
结论:✅

@Xuyuchao-juice
Xuyuchao-juice force-pushed the attr_length branch 4 times, most recently from 4d39147 to e436a2a Compare July 22, 2026 07:54
@Xuyuchao-juice
Xuyuchao-juice marked this pull request as ready for review July 22, 2026 08:57
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.

Attributes consistency between kernel cache and userspace

1 participant