From 9c2130d6c2e54c45c6787fbc49a896a8544a5e47 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 17 Aug 2026 01:15:41 +0000 Subject: fusefrontend: take ContentLock for truncate through node.Setattr node.Setattr opens its own file handle and called truncate directly, without ContentLock. truncate ends in doWrite, which documents that the caller holds that lock, and file.Setattr and Allocate both take it. Besides leaving a path truncate unserialized against concurrent writes, this skipped the write-operation counter that ContentLock.Lock() increments. isConsecutiveWrite() reads that counter to notice foreign modifications, so an already-open handle kept assuming its next write appends, skipped writePadHole() and grew the file past a short last block. That block no longer decrypted, and every later operation on the file failed with EIO. Fixes #1024 --- internal/fusefrontend/node.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'internal/fusefrontend/node.go') diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index 28ebbd5..59f6b2f 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -246,6 +246,8 @@ func (n *Node) Setattr(ctx context.Context, f fs.FileHandle, in *fuse.SetAttrIn, } f2 := f.(*File) defer f2.Release(ctx) + f2.fileTableEntry.ContentLock.Lock() + defer f2.fileTableEntry.ContentLock.Unlock() errno = syscall.Errno(f2.truncate(sz)) if errno != 0 { return errno -- cgit v1.2.3