Add NAME_MAX checks and update file times
NAME_MAX should be enforced per filesystem implementation because other file systems may not have the same restriction. Gofer filesystem now keeps a reference to the kernel clock to avoid lookup in the Context on file access to update atime. Update access, modification, and status change times in tmpfs. Updates #1197, #1198. PiperOrigin-RevId: 304527148
This commit is contained in:
parent
4582a2f188
commit
dd3bc49997
|
@ -56,14 +56,19 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba
|
|||
fd.mu.Lock()
|
||||
defer fd.mu.Unlock()
|
||||
|
||||
d := fd.dentry()
|
||||
if fd.dirents == nil {
|
||||
ds, err := fd.dentry().getDirents(ctx)
|
||||
ds, err := d.getDirents(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd.dirents = ds
|
||||
}
|
||||
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
d.touchAtime(fd.vfsfd.Mount())
|
||||
}
|
||||
|
||||
for fd.off < int64(len(fd.dirents)) {
|
||||
if err := cb.Handle(fd.dirents[fd.off]); err != nil {
|
||||
return err
|
||||
|
|
|
@ -356,7 +356,9 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
|
|||
if err := create(parent, name); err != nil {
|
||||
return err
|
||||
}
|
||||
parent.touchCMtime(ctx)
|
||||
if fs.opts.interop != InteropModeShared {
|
||||
parent.touchCMtime()
|
||||
}
|
||||
delete(parent.negativeChildren, name)
|
||||
parent.dirents = nil
|
||||
return nil
|
||||
|
@ -454,7 +456,7 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
|
|||
return err
|
||||
}
|
||||
if fs.opts.interop != InteropModeShared {
|
||||
parent.touchCMtime(ctx)
|
||||
parent.touchCMtime()
|
||||
if dir {
|
||||
parent.decLinks()
|
||||
}
|
||||
|
@ -802,7 +804,6 @@ func (d *dentry) createAndOpenChildLocked(ctx context.Context, rp *vfs.Resolving
|
|||
d.IncRef() // reference held by child on its parent d
|
||||
d.vfsd.InsertChild(&child.vfsd, name)
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
d.touchCMtime(ctx)
|
||||
delete(d.negativeChildren, name)
|
||||
d.dirents = nil
|
||||
}
|
||||
|
@ -834,6 +835,9 @@ func (d *dentry) createAndOpenChildLocked(ctx context.Context, rp *vfs.Resolving
|
|||
}
|
||||
childVFSFD = &fd.vfsfd
|
||||
}
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
d.touchCMtime()
|
||||
}
|
||||
return childVFSFD, nil
|
||||
}
|
||||
|
||||
|
@ -975,6 +979,9 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
|
|||
oldParent.decLinks()
|
||||
newParent.incLinks()
|
||||
}
|
||||
oldParent.touchCMtime()
|
||||
newParent.touchCMtime()
|
||||
renamed.touchCtime()
|
||||
}
|
||||
vfsObj.CommitRenameReplaceDentry(&renamed.vfsd, &newParent.vfsd, newName, replacedVFSD)
|
||||
return nil
|
||||
|
|
|
@ -44,6 +44,7 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
|
@ -72,6 +73,9 @@ type filesystem struct {
|
|||
// client is the client used by this filesystem. client is immutable.
|
||||
client *p9.Client
|
||||
|
||||
// clock is a realtime clock used to set timestamps in file operations.
|
||||
clock ktime.Clock
|
||||
|
||||
// uid and gid are the effective KUID and KGID of the filesystem's creator,
|
||||
// and are used as the owner and group for files that don't specify one.
|
||||
// uid and gid are immutable.
|
||||
|
@ -376,6 +380,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
|
|||
uid: creds.EffectiveKUID,
|
||||
gid: creds.EffectiveKGID,
|
||||
client: client,
|
||||
clock: ktime.RealtimeClockFromContext(ctx),
|
||||
dentries: make(map[*dentry]struct{}),
|
||||
specialFileFDs: make(map[*specialFileFD]struct{}),
|
||||
}
|
||||
|
@ -779,10 +784,7 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, stat *lin
|
|||
// data, so there's no cache to truncate either.)
|
||||
return nil
|
||||
}
|
||||
now, haveNow := nowFromContext(ctx)
|
||||
if !haveNow {
|
||||
ctx.Warningf("gofer.dentry.setStat: current time not available")
|
||||
}
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
if stat.Mask&linux.STATX_MODE != 0 {
|
||||
atomic.StoreUint32(&d.mode, d.fileType()|uint32(stat.Mode))
|
||||
}
|
||||
|
@ -794,25 +796,19 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, stat *lin
|
|||
}
|
||||
if setLocalAtime {
|
||||
if stat.Atime.Nsec == linux.UTIME_NOW {
|
||||
if haveNow {
|
||||
atomic.StoreInt64(&d.atime, now)
|
||||
}
|
||||
atomic.StoreInt64(&d.atime, now)
|
||||
} else {
|
||||
atomic.StoreInt64(&d.atime, dentryTimestampFromStatx(stat.Atime))
|
||||
}
|
||||
}
|
||||
if setLocalMtime {
|
||||
if stat.Mtime.Nsec == linux.UTIME_NOW {
|
||||
if haveNow {
|
||||
atomic.StoreInt64(&d.mtime, now)
|
||||
}
|
||||
atomic.StoreInt64(&d.mtime, now)
|
||||
} else {
|
||||
atomic.StoreInt64(&d.mtime, dentryTimestampFromStatx(stat.Mtime))
|
||||
}
|
||||
}
|
||||
if haveNow {
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
}
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
if stat.Mask&linux.STATX_SIZE != 0 {
|
||||
d.dataMu.Lock()
|
||||
oldSize := d.size
|
||||
|
|
|
@ -104,7 +104,7 @@ func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
|
|||
putDentryReadWriter(rw)
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
// Compare Linux's mm/filemap.c:do_generic_file_read() => file_accessed().
|
||||
d.touchAtime(ctx, fd.vfsfd.Mount())
|
||||
d.touchAtime(fd.vfsfd.Mount())
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
@ -139,10 +139,7 @@ func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, off
|
|||
// Compare Linux's mm/filemap.c:__generic_file_write_iter() =>
|
||||
// file_update_time(). This is d.touchCMtime(), but without locking
|
||||
// d.metadataMu (recursively).
|
||||
if now, ok := nowFromContext(ctx); ok {
|
||||
atomic.StoreInt64(&d.mtime, now)
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
}
|
||||
d.touchCMtimeLocked()
|
||||
}
|
||||
if fd.vfsfd.StatusFlags()&linux.O_DIRECT != 0 {
|
||||
// Write dirty cached pages that will be touched by the write back to
|
||||
|
|
|
@ -76,7 +76,7 @@ func (fd *specialFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
|
|||
// hold here since specialFileFD doesn't client-cache data. Just buffer the
|
||||
// read instead.
|
||||
if d := fd.dentry(); d.fs.opts.interop != InteropModeShared {
|
||||
d.touchAtime(ctx, fd.vfsfd.Mount())
|
||||
d.touchAtime(fd.vfsfd.Mount())
|
||||
}
|
||||
buf := make([]byte, dst.NumBytes())
|
||||
n, err := fd.handle.readToBlocksAt(ctx, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)), uint64(offset))
|
||||
|
@ -117,7 +117,7 @@ func (fd *specialFileFD) PWrite(ctx context.Context, src usermem.IOSequence, off
|
|||
|
||||
// Do a buffered write. See rationale in PRead.
|
||||
if d := fd.dentry(); d.fs.opts.interop != InteropModeShared {
|
||||
d.touchCMtime(ctx)
|
||||
d.touchCMtime()
|
||||
}
|
||||
buf := make([]byte, src.NumBytes())
|
||||
// Don't do partial writes if we get a partial read from src.
|
||||
|
|
|
@ -27,7 +27,7 @@ func (d *dentry) isSymlink() bool {
|
|||
// Precondition: d.isSymlink().
|
||||
func (d *dentry) readlink(ctx context.Context, mnt *vfs.Mount) (string, error) {
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
d.touchAtime(ctx, mnt)
|
||||
d.touchAtime(mnt)
|
||||
d.dataMu.Lock()
|
||||
if d.haveTarget {
|
||||
target := d.target
|
||||
|
|
|
@ -18,8 +18,6 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
|
@ -38,23 +36,12 @@ func statxTimestampFromDentry(ns int64) linux.StatxTimestamp {
|
|||
}
|
||||
}
|
||||
|
||||
func nowFromContext(ctx context.Context) (int64, bool) {
|
||||
if clock := ktime.RealtimeClockFromContext(ctx); clock != nil {
|
||||
return clock.Now().Nanoseconds(), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Preconditions: fs.interop != InteropModeShared.
|
||||
func (d *dentry) touchAtime(ctx context.Context, mnt *vfs.Mount) {
|
||||
func (d *dentry) touchAtime(mnt *vfs.Mount) {
|
||||
if err := mnt.CheckBeginWrite(); err != nil {
|
||||
return
|
||||
}
|
||||
now, ok := nowFromContext(ctx)
|
||||
if !ok {
|
||||
mnt.EndWrite()
|
||||
return
|
||||
}
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
d.metadataMu.Lock()
|
||||
atomic.StoreInt64(&d.atime, now)
|
||||
d.metadataMu.Unlock()
|
||||
|
@ -63,13 +50,25 @@ func (d *dentry) touchAtime(ctx context.Context, mnt *vfs.Mount) {
|
|||
|
||||
// Preconditions: fs.interop != InteropModeShared. The caller has successfully
|
||||
// called vfs.Mount.CheckBeginWrite().
|
||||
func (d *dentry) touchCMtime(ctx context.Context) {
|
||||
now, ok := nowFromContext(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
func (d *dentry) touchCtime() {
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
d.metadataMu.Lock()
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
d.metadataMu.Unlock()
|
||||
}
|
||||
|
||||
// Preconditions: fs.interop != InteropModeShared. The caller has successfully
|
||||
// called vfs.Mount.CheckBeginWrite().
|
||||
func (d *dentry) touchCMtime() {
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
d.metadataMu.Lock()
|
||||
atomic.StoreInt64(&d.mtime, now)
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
d.metadataMu.Unlock()
|
||||
}
|
||||
|
||||
func (d *dentry) touchCMtimeLocked() {
|
||||
now := d.fs.clock.Now().Nanoseconds()
|
||||
atomic.StoreInt64(&d.mtime, now)
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
}
|
||||
|
|
|
@ -63,6 +63,9 @@ afterSymlink:
|
|||
rp.Advance()
|
||||
return nextVFSD, nil
|
||||
}
|
||||
if len(name) > linux.NAME_MAX {
|
||||
return nil, syserror.ENAMETOOLONG
|
||||
}
|
||||
d.dirMu.Lock()
|
||||
nextVFSD, err := rp.ResolveChild(vfsd, name)
|
||||
if err != nil {
|
||||
|
@ -191,6 +194,9 @@ func checkCreateLocked(ctx context.Context, rp *vfs.ResolvingPath, parentVFSD *v
|
|||
if pc == "." || pc == ".." {
|
||||
return "", syserror.EEXIST
|
||||
}
|
||||
if len(pc) > linux.NAME_MAX {
|
||||
return "", syserror.ENAMETOOLONG
|
||||
}
|
||||
childVFSD, err := rp.ResolveChild(parentVFSD, pc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -433,6 +439,9 @@ afterTrailingSymlink:
|
|||
if pc == "." || pc == ".." {
|
||||
return nil, syserror.EISDIR
|
||||
}
|
||||
if len(pc) > linux.NAME_MAX {
|
||||
return nil, syserror.ENAMETOOLONG
|
||||
}
|
||||
// Determine whether or not we need to create a file.
|
||||
childVFSD, err := rp.ResolveChild(parentVFSD, pc)
|
||||
if err != nil {
|
||||
|
|
|
@ -68,6 +68,8 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba
|
|||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
fd.inode().touchAtime(fd.vfsfd.Mount())
|
||||
|
||||
if fd.off == 0 {
|
||||
if err := cb.Handle(vfs.Dirent{
|
||||
Name: ".",
|
||||
|
|
|
@ -46,6 +46,9 @@ func stepLocked(rp *vfs.ResolvingPath, d *dentry) (*dentry, error) {
|
|||
return nil, err
|
||||
}
|
||||
afterSymlink:
|
||||
if len(rp.Component()) > linux.NAME_MAX {
|
||||
return nil, syserror.ENAMETOOLONG
|
||||
}
|
||||
nextVFSD, err := rp.ResolveComponent(&d.vfsd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -133,6 +136,9 @@ func (fs *filesystem) doCreateAt(rp *vfs.ResolvingPath, dir bool, create func(pa
|
|||
if name == "." || name == ".." {
|
||||
return syserror.EEXIST
|
||||
}
|
||||
if len(name) > linux.NAME_MAX {
|
||||
return syserror.ENAMETOOLONG
|
||||
}
|
||||
// Call parent.vfsd.Child() instead of stepLocked() or rp.ResolveChild(),
|
||||
// because if the child exists we want to return EEXIST immediately instead
|
||||
// of attempting symlink/mount traversal.
|
||||
|
@ -153,7 +159,11 @@ func (fs *filesystem) doCreateAt(rp *vfs.ResolvingPath, dir bool, create func(pa
|
|||
return err
|
||||
}
|
||||
defer mnt.EndWrite()
|
||||
return create(parent, name)
|
||||
if err := create(parent, name); err != nil {
|
||||
return err
|
||||
}
|
||||
parent.inode.touchCMtime()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
||||
|
@ -328,7 +338,12 @@ afterTrailingSymlink:
|
|||
child := fs.newDentry(fs.newRegularFile(rp.Credentials(), opts.Mode))
|
||||
parent.vfsd.InsertChild(&child.vfsd, name)
|
||||
parent.inode.impl.(*directory).childList.PushBack(child)
|
||||
return child.open(ctx, rp, &opts, true)
|
||||
fd, err := child.open(ctx, rp, &opts, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parent.inode.touchCMtime()
|
||||
return fd, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -398,6 +413,7 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
|
|||
if !ok {
|
||||
return "", syserror.EINVAL
|
||||
}
|
||||
symlink.inode.touchAtime(rp.Mount())
|
||||
return symlink.target, nil
|
||||
}
|
||||
|
||||
|
@ -515,6 +531,9 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
|
|||
oldParent.inode.decLinksLocked()
|
||||
newParent.inode.incLinksLocked()
|
||||
}
|
||||
oldParent.inode.touchCMtime()
|
||||
newParent.inode.touchCMtime()
|
||||
renamed.inode.touchCtime()
|
||||
// TODO(gvisor.dev/issue/1197): Update timestamps and parent directory
|
||||
// sizes.
|
||||
vfsObj.CommitRenameReplaceDentry(renamedVFSD, &newParent.vfsd, newName, replacedVFSD)
|
||||
|
@ -565,6 +584,7 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
|||
parent.inode.decLinksLocked() // from child's ".."
|
||||
child.inode.decLinksLocked()
|
||||
vfsObj.CommitDeleteDentry(childVFSD)
|
||||
parent.inode.touchCMtime()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -654,6 +674,7 @@ func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
|||
parent.inode.impl.(*directory).childList.Remove(child)
|
||||
child.inode.decLinksLocked()
|
||||
vfsObj.CommitDeleteDentry(childVFSD)
|
||||
parent.inode.touchCMtime()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -286,7 +286,8 @@ func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
|
|||
rw := getRegularFileReadWriter(f, offset)
|
||||
n, err := dst.CopyOutFrom(ctx, rw)
|
||||
putRegularFileReadWriter(rw)
|
||||
return int64(n), err
|
||||
fd.inode().touchAtime(fd.vfsfd.Mount())
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Read implements vfs.FileDescriptionImpl.Read.
|
||||
|
@ -323,6 +324,7 @@ func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, off
|
|||
f.inode.mu.Lock()
|
||||
rw := getRegularFileReadWriter(f, offset)
|
||||
n, err := src.CopyInTo(ctx, rw)
|
||||
fd.inode().touchCMtimeLocked()
|
||||
f.inode.mu.Unlock()
|
||||
putRegularFileReadWriter(rw)
|
||||
return n, err
|
||||
|
|
|
@ -385,28 +385,41 @@ func (i *inode) setStat(ctx context.Context, creds *auth.Credentials, stat *linu
|
|||
return syserror.EINVAL
|
||||
}
|
||||
}
|
||||
now := i.clock.Now().Nanoseconds()
|
||||
if mask&linux.STATX_ATIME != 0 {
|
||||
atomic.StoreInt64(&i.atime, stat.Atime.ToNsecCapped())
|
||||
if stat.Atime.Nsec == linux.UTIME_NOW {
|
||||
atomic.StoreInt64(&i.atime, now)
|
||||
} else {
|
||||
atomic.StoreInt64(&i.atime, stat.Atime.ToNsecCapped())
|
||||
}
|
||||
needsCtimeBump = true
|
||||
}
|
||||
if mask&linux.STATX_MTIME != 0 {
|
||||
atomic.StoreInt64(&i.mtime, stat.Mtime.ToNsecCapped())
|
||||
if stat.Mtime.Nsec == linux.UTIME_NOW {
|
||||
atomic.StoreInt64(&i.mtime, now)
|
||||
} else {
|
||||
atomic.StoreInt64(&i.mtime, stat.Mtime.ToNsecCapped())
|
||||
}
|
||||
needsCtimeBump = true
|
||||
// Ignore the mtime bump, since we just set it ourselves.
|
||||
needsMtimeBump = false
|
||||
}
|
||||
if mask&linux.STATX_CTIME != 0 {
|
||||
atomic.StoreInt64(&i.ctime, stat.Ctime.ToNsecCapped())
|
||||
if stat.Ctime.Nsec == linux.UTIME_NOW {
|
||||
atomic.StoreInt64(&i.ctime, now)
|
||||
} else {
|
||||
atomic.StoreInt64(&i.ctime, stat.Ctime.ToNsecCapped())
|
||||
}
|
||||
// Ignore the ctime bump, since we just set it ourselves.
|
||||
needsCtimeBump = false
|
||||
}
|
||||
now := i.clock.Now().Nanoseconds()
|
||||
if needsMtimeBump {
|
||||
atomic.StoreInt64(&i.mtime, now)
|
||||
}
|
||||
if needsCtimeBump {
|
||||
atomic.StoreInt64(&i.ctime, now)
|
||||
}
|
||||
|
||||
i.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
@ -484,6 +497,42 @@ func (i *inode) isDir() bool {
|
|||
return linux.FileMode(i.mode).FileType() == linux.S_IFDIR
|
||||
}
|
||||
|
||||
func (i *inode) touchAtime(mnt *vfs.Mount) {
|
||||
if err := mnt.CheckBeginWrite(); err != nil {
|
||||
return
|
||||
}
|
||||
now := i.clock.Now().Nanoseconds()
|
||||
i.mu.Lock()
|
||||
atomic.StoreInt64(&i.atime, now)
|
||||
i.mu.Unlock()
|
||||
mnt.EndWrite()
|
||||
}
|
||||
|
||||
// Preconditions: The caller has called vfs.Mount.CheckBeginWrite().
|
||||
func (i *inode) touchCtime() {
|
||||
now := i.clock.Now().Nanoseconds()
|
||||
i.mu.Lock()
|
||||
atomic.StoreInt64(&i.ctime, now)
|
||||
i.mu.Unlock()
|
||||
}
|
||||
|
||||
// Preconditions: The caller has called vfs.Mount.CheckBeginWrite().
|
||||
func (i *inode) touchCMtime() {
|
||||
now := i.clock.Now().Nanoseconds()
|
||||
i.mu.Lock()
|
||||
atomic.StoreInt64(&i.mtime, now)
|
||||
atomic.StoreInt64(&i.ctime, now)
|
||||
i.mu.Unlock()
|
||||
}
|
||||
|
||||
// Preconditions: The caller has called vfs.Mount.CheckBeginWrite() and holds
|
||||
// inode.mu.
|
||||
func (i *inode) touchCMtimeLocked() {
|
||||
now := i.clock.Now().Nanoseconds()
|
||||
atomic.StoreInt64(&i.mtime, now)
|
||||
atomic.StoreInt64(&i.ctime, now)
|
||||
}
|
||||
|
||||
// fileDescription is embedded by tmpfs implementations of
|
||||
// vfs.FileDescriptionImpl.
|
||||
type fileDescription struct {
|
||||
|
|
Loading…
Reference in New Issue