diff --git a/pkg/abi/linux/fuse.go b/pkg/abi/linux/fuse.go index 0d7db57f8..6fe07bb0a 100644 --- a/pkg/abi/linux/fuse.go +++ b/pkg/abi/linux/fuse.go @@ -15,6 +15,8 @@ package linux import ( + "time" + "gvisor.dev/gvisor/pkg/marshal/primitive" ) @@ -348,6 +350,24 @@ type FUSEAttr struct { _ uint32 } +// ATimeNsec returns the last access time as the total time since the unix epoch +// in nanoseconds. +func (a FUSEAttr) ATimeNsec() int64 { + return int64(a.Atime)*time.Second.Nanoseconds() + int64(a.AtimeNsec) +} + +// MTimeNsec returns the last modification time as the total time since the unix +// epoch in nanoseconds. +func (a FUSEAttr) MTimeNsec() int64 { + return int64(a.Mtime)*time.Second.Nanoseconds() + int64(a.MtimeNsec) +} + +// CTimeNsec returns the last change time as the total time since the unix epoch +// in nanoseconds. +func (a FUSEAttr) CTimeNsec() int64 { + return int64(a.Ctime)*time.Second.Nanoseconds() + int64(a.CtimeNsec) +} + // FUSEAttrOut is the reply sent by the daemon to the kernel // for FUSEGetAttrIn and FUSESetAttrIn. // diff --git a/pkg/sentry/fsimpl/fuse/file.go b/pkg/sentry/fsimpl/fuse/file.go index 16927af24..fd68e7f00 100644 --- a/pkg/sentry/fsimpl/fuse/file.go +++ b/pkg/sentry/fsimpl/fuse/file.go @@ -133,6 +133,9 @@ func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) inode := fd.inode() inode.attrMu.Lock() defer inode.attrMu.Unlock() + if err := vfs.CheckSetStat(ctx, creds, &opts, inode.filemode(), auth.KUID(inode.uid.Load()), auth.KGID(inode.gid.Load())); err != nil { + return err + } return inode.setAttr(ctx, fs, creds, opts, fhOptions{useFh: true, fh: fd.Fh}) } diff --git a/pkg/sentry/fsimpl/fuse/inode.go b/pkg/sentry/fsimpl/fuse/inode.go index 8b7a08ed8..eb02755e8 100644 --- a/pkg/sentry/fsimpl/fuse/inode.go +++ b/pkg/sentry/fsimpl/fuse/inode.go @@ -17,6 +17,7 @@ package fuse import ( "fmt" "sync" + "time" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/atomicbitops" @@ -562,17 +563,21 @@ func (i *inode) Readlink(ctx context.Context, mnt *vfs.Mount) (string, error) { // // +checklocks:i.attrMu func (i *inode) getFUSEAttr() linux.FUSEAttr { + ns := time.Second.Nanoseconds() return linux.FUSEAttr{ - Ino: i.nodeID, - UID: i.uid.Load(), - GID: i.gid.Load(), - Size: i.size.Load(), - Mode: uint32(i.filemode()), - BlkSize: i.blockSize.Load(), - Atime: uint64(i.atime.Load()), - Mtime: uint64(i.mtime.Load()), - Ctime: uint64(i.ctime.Load()), - Nlink: i.nlink.Load(), + Ino: i.nodeID, + UID: i.uid.Load(), + GID: i.gid.Load(), + Size: i.size.Load(), + Mode: uint32(i.filemode()), + BlkSize: i.blockSize.Load(), + Atime: uint64(i.atime.Load() / ns), + Mtime: uint64(i.mtime.Load() / ns), + Ctime: uint64(i.ctime.Load() / ns), + AtimeNsec: uint32(i.atime.Load() % ns), + MtimeNsec: uint32(i.mtime.Load() % ns), + CtimeNsec: uint32(i.ctime.Load() % ns), + Nlink: i.nlink.Load(), } } @@ -771,11 +776,14 @@ func fattrMaskFromStats(mask uint32) uint32 { // SetStat implements kernfs.Inode.SetStat. func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error { + i.attrMu.Lock() + defer i.attrMu.Unlock() + if err := vfs.CheckSetStat(ctx, creds, &opts, i.filemode(), auth.KUID(i.uid.Load()), auth.KGID(i.gid.Load())); err != nil { + return err + } if opts.Stat.Mask == 0 { return nil } - i.attrMu.Lock() - defer i.attrMu.Unlock() return i.setAttr(ctx, fs, creds, opts, fhOptions{useFh: false}) } @@ -797,6 +805,12 @@ func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre if fhOpts.useFh { fattrMask |= linux.FATTR_FH } + if opts.Stat.Mask&linux.STATX_ATIME != 0 && opts.Stat.Atime.Nsec == linux.UTIME_NOW { + fattrMask |= linux.FATTR_ATIME_NOW + } + if opts.Stat.Mask&linux.STATX_MTIME != 0 && opts.Stat.Mtime.Nsec == linux.UTIME_NOW { + fattrMask |= linux.FATTR_ATIME_NOW + } in := linux.FUSESetAttrIn{ Valid: fattrMask, Fh: fhOpts.fh, @@ -840,9 +854,9 @@ func (i *inode) updateAttrs(attr linux.FUSEAttr, attrTimeout uint64) { i.uid.Store(attr.UID) i.gid.Store(attr.GID) - i.atime.Store(int64(attr.Atime)) - i.mtime.Store(int64(attr.Mtime)) - i.ctime.Store(int64(attr.Ctime)) + i.atime.Store(attr.ATimeNsec()) + i.mtime.Store(attr.MTimeNsec()) + i.ctime.Store(attr.CTimeNsec()) i.size.Store(attr.Size) i.nlink.Store(attr.Nlink) diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 6f36e352b..87da269c7 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -942,6 +942,7 @@ syscall_test( ) syscall_test( + add_fusefs = True, add_overlay = True, test = "//test/syscalls/linux:stat_times_test", ) @@ -1007,6 +1008,7 @@ syscall_test( ) syscall_test( + add_fusefs = True, add_overlay = True, test = "//test/syscalls/linux:truncate_test", )