diff --git a/pkg/sentry/fsimpl/fuse/connection_control.go b/pkg/sentry/fsimpl/fuse/connection_control.go index 14461f924..7e04479a1 100644 --- a/pkg/sentry/fsimpl/fuse/connection_control.go +++ b/pkg/sentry/fsimpl/fuse/connection_control.go @@ -141,6 +141,7 @@ func (conn *connection) initProcessReply(out *linux.FUSEInitOut, hasSysAdminCap conn.bigWrites = out.Flags&linux.FUSE_BIG_WRITES != 0 conn.dontMask = out.Flags&linux.FUSE_DONT_MASK != 0 conn.writebackCache = out.Flags&linux.FUSE_WRITEBACK_CACHE != 0 + conn.atomicOTrunc = out.Flags&linux.FUSE_ATOMIC_O_TRUNC != 0 // TODO(gvisor.dev/issue/3195): figure out how to use TimeGran (0 < TimeGran <= fuseMaxTimeGranNs). diff --git a/pkg/sentry/fsimpl/fuse/fusefs.go b/pkg/sentry/fsimpl/fuse/fusefs.go index 31087d87a..a536649d7 100644 --- a/pkg/sentry/fsimpl/fuse/fusefs.go +++ b/pkg/sentry/fsimpl/fuse/fusefs.go @@ -476,15 +476,20 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr fd.Nonseekable = true } - // If we don't send SETATTR before open (which is indicated by atomicOTrunc) - // and O_TRUNC is set, update the inode's version number and clean existing data - // by setting the file size to 0. - if i.fs.conn.atomicOTrunc && opts.Flags&linux.O_TRUNC != 0 { - i.fs.conn.mu.Lock() - i.attributeVersion.Store(i.fs.conn.attributeVersion.Add(1)) - i.size.Store(0) - i.fs.conn.mu.Unlock() - i.attributeTime = 0 + // If atomicOTrunc and O_TRUNC are set, just update the inode's version number + // and set its size to 0 since the truncation is handled by the FUSE daemon. + // Otherwise send a separate SETATTR to truncate the file size. + if opts.Flags&linux.O_TRUNC != 0 { + if i.fs.conn.atomicOTrunc { + i.fs.conn.mu.Lock() + i.attributeVersion.Store(i.fs.conn.attributeVersion.Add(1)) + i.size.Store(0) + i.fs.conn.mu.Unlock() + i.attributeTime = 0 + } else { + opts := vfs.SetStatOptions{Stat: linux.Statx{Size: 0, Mask: linux.STATX_SIZE}} + i.setAttr(ctx, i.fs.VFSFilesystem(), auth.CredentialsFromContext(ctx), opts, true, i.newFhData.fh) + } } if err := fd.vfsfd.Init(fdImpl, opts.Flags, rp.Mount(), d.VFSDentry(), fdOptions); err != nil { diff --git a/test/fuse/linux/fuse_base.h b/test/fuse/linux/fuse_base.h index 573fe730a..698943706 100644 --- a/test/fuse/linux/fuse_base.h +++ b/test/fuse/linux/fuse_base.h @@ -35,7 +35,8 @@ namespace testing { constexpr char kMountOpts[] = "rootmode=755,user_id=0,group_id=0"; -constexpr struct fuse_init_out kDefaultFUSEInitOutPayload = {.major = 7}; +constexpr struct fuse_init_out kDefaultFUSEInitOutPayload = { + .major = 7, .minor = 14, .flags = FUSE_ATOMIC_O_TRUNC}; // Internal commands used to communicate between testing thread and the FUSE // server. See test/fuse/README.md for further detail. diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 379fcfabf..ffaaf9f13 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -128,6 +128,7 @@ syscall_test( syscall_test( add_overlay = True, test = "//test/syscalls/linux:creat_test", + use_fusefs = True, ) syscall_test(