summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fsimpl/gofer
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fsimpl/gofer')
-rw-r--r--pkg/sentry/fsimpl/gofer/filesystem.go58
-rw-r--r--pkg/sentry/fsimpl/gofer/gofer.go14
-rw-r--r--pkg/sentry/fsimpl/gofer/regular_file.go44
-rw-r--r--pkg/sentry/fsimpl/gofer/special_file.go19
4 files changed, 66 insertions, 69 deletions
diff --git a/pkg/sentry/fsimpl/gofer/filesystem.go b/pkg/sentry/fsimpl/gofer/filesystem.go
index 652e5fe77..05b776c2e 100644
--- a/pkg/sentry/fsimpl/gofer/filesystem.go
+++ b/pkg/sentry/fsimpl/gofer/filesystem.go
@@ -39,26 +39,14 @@ import (
// Sync implements vfs.FilesystemImpl.Sync.
func (fs *filesystem) Sync(ctx context.Context) error {
// Snapshot current syncable dentries and special file FDs.
- fs.renameMu.RLock()
fs.syncMu.Lock()
ds := make([]*dentry, 0, len(fs.syncableDentries))
for d := range fs.syncableDentries {
- // It's safe to use IncRef here even though fs.syncableDentries doesn't
- // hold references since we hold fs.renameMu. Note that we can't use
- // TryIncRef since cached dentries at zero references should still be
- // synced.
- d.IncRef()
ds = append(ds, d)
}
- fs.renameMu.RUnlock()
sffds := make([]*specialFileFD, 0, len(fs.specialFileFDs))
for sffd := range fs.specialFileFDs {
- // As above, fs.specialFileFDs doesn't hold references. However, unlike
- // dentries, an FD that has reached zero references can't be
- // resurrected, so we can use TryIncRef.
- if sffd.vfsfd.TryIncRef() {
- sffds = append(sffds, sffd)
- }
+ sffds = append(sffds, sffd)
}
fs.syncMu.Unlock()
@@ -68,9 +56,7 @@ func (fs *filesystem) Sync(ctx context.Context) error {
// Sync syncable dentries.
for _, d := range ds {
- err := d.syncCachedFile(ctx, true /* forFilesystemSync */)
- d.DecRef(ctx)
- if err != nil {
+ if err := d.syncCachedFile(ctx, true /* forFilesystemSync */); err != nil {
ctx.Infof("gofer.filesystem.Sync: dentry.syncCachedFile failed: %v", err)
if retErr == nil {
retErr = err
@@ -81,9 +67,7 @@ func (fs *filesystem) Sync(ctx context.Context) error {
// Sync special files, which may be writable but do not use dentry shared
// handles (so they won't be synced by the above).
for _, sffd := range sffds {
- err := sffd.sync(ctx, true /* forFilesystemSync */)
- sffd.vfsfd.DecRef(ctx)
- if err != nil {
+ if err := sffd.sync(ctx, true /* forFilesystemSync */); err != nil {
ctx.Infof("gofer.filesystem.Sync: specialFileFD.sync failed: %v", err)
if retErr == nil {
retErr = err
@@ -189,7 +173,7 @@ func (fs *filesystem) renameMuUnlockAndCheckCaching(ctx context.Context, ds **[]
// Postconditions: The returned dentry's cached metadata is up to date.
func (fs *filesystem) stepLocked(ctx context.Context, rp *vfs.ResolvingPath, d *dentry, mayFollowSymlinks bool, ds **[]*dentry) (*dentry, bool, error) {
if !d.isDir() {
- return nil, false, syserror.ENOTDIR
+ return nil, false, linuxerr.ENOTDIR
}
if err := d.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
return nil, false, err
@@ -305,7 +289,7 @@ func (fs *filesystem) walkParentDirLocked(ctx context.Context, rp *vfs.Resolving
}
}
if !d.isDir() {
- return nil, syserror.ENOTDIR
+ return nil, linuxerr.ENOTDIR
}
return d, nil
}
@@ -333,7 +317,7 @@ func (fs *filesystem) resolveLocked(ctx context.Context, rp *vfs.ResolvingPath,
}
}
if rp.MustBeDir() && !d.isDir() {
- return nil, syserror.ENOTDIR
+ return nil, linuxerr.ENOTDIR
}
return d, nil
}
@@ -362,7 +346,7 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
}
name := rp.Component()
if name == "." || name == ".." {
- return syserror.EEXIST
+ return linuxerr.EEXIST
}
if parent.isDeleted() {
return syserror.ENOENT
@@ -382,13 +366,13 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
// checks for writability fail below. Existence check is done by the creation
// RPCs themselves.
if child, ok := parent.children[name]; ok && child != nil {
- return syserror.EEXIST
+ return linuxerr.EEXIST
}
checkExistence := func() error {
if child, err := fs.getChildLocked(ctx, parent, name, &ds); err != nil && !linuxerr.Equals(linuxerr.ENOENT, err) {
return err
} else if child != nil {
- return syserror.EEXIST
+ return linuxerr.EEXIST
}
return nil
}
@@ -553,7 +537,7 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
if child.cachedMetadataAuthoritative() {
if !child.isDir() {
vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: see above.
- return syserror.ENOTDIR
+ return linuxerr.ENOTDIR
}
for _, grandchild := range child.children {
if grandchild != nil {
@@ -574,7 +558,7 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
if child != nil {
vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: see above.
}
- return syserror.ENOTDIR
+ return linuxerr.ENOTDIR
}
}
if parent.isSynthetic() {
@@ -645,7 +629,7 @@ func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, op
}
if opts.CheckSearchable {
if !d.isDir() {
- return nil, syserror.ENOTDIR
+ return nil, linuxerr.ENOTDIR
}
if err := d.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
return nil, err
@@ -767,7 +751,7 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
switch {
case err == nil:
// Step succeeded, another file exists.
- return syserror.EEXIST
+ return linuxerr.EEXIST
case !linuxerr.Equals(linuxerr.ENOENT, err):
// Unexpected error.
return err
@@ -807,7 +791,7 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
// support, and it isn't clear that there's any way to implement this in
// 9P.
if opts.Flags&linux.O_TMPFILE != 0 {
- return nil, syserror.EOPNOTSUPP
+ return nil, linuxerr.EOPNOTSUPP
}
mayCreate := opts.Flags&linux.O_CREAT != 0
mustCreate := opts.Flags&(linux.O_CREAT|linux.O_EXCL) == (linux.O_CREAT | linux.O_EXCL)
@@ -830,7 +814,7 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
return nil, syserror.EISDIR
}
if mustCreate {
- return nil, syserror.EEXIST
+ return nil, linuxerr.EEXIST
}
if !start.cachedMetadataAuthoritative() {
// Refresh dentry's attributes before opening.
@@ -879,7 +863,7 @@ afterTrailingSymlink:
return nil, err
}
if mustCreate {
- return nil, syserror.EEXIST
+ return nil, linuxerr.EEXIST
}
// Open existing child or follow symlink.
if child.isSymlink() && rp.ShouldFollowSymlink() {
@@ -894,7 +878,7 @@ afterTrailingSymlink:
goto afterTrailingSymlink
}
if rp.MustBeDir() && !child.isDir() {
- return nil, syserror.ENOTDIR
+ return nil, linuxerr.ENOTDIR
}
child.IncRef()
defer child.DecRef(ctx)
@@ -1218,7 +1202,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
newName := rp.Component()
if newName == "." || newName == ".." {
if opts.Flags&linux.RENAME_NOREPLACE != 0 {
- return syserror.EEXIST
+ return linuxerr.EEXIST
}
return linuxerr.EBUSY
}
@@ -1272,7 +1256,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
} else {
if opts.MustBeDir || rp.MustBeDir() {
- return syserror.ENOTDIR
+ return linuxerr.ENOTDIR
}
}
@@ -1293,7 +1277,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
var replacedVFSD *vfs.Dentry
if replaced != nil {
if opts.Flags&linux.RENAME_NOREPLACE != 0 {
- return syserror.EEXIST
+ return linuxerr.EEXIST
}
replacedVFSD = &replaced.vfsd
if replaced.isDir() {
@@ -1305,7 +1289,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
} else {
if rp.MustBeDir() || renamed.isDir() {
- return syserror.ENOTDIR
+ return linuxerr.ENOTDIR
}
}
}
diff --git a/pkg/sentry/fsimpl/gofer/gofer.go b/pkg/sentry/fsimpl/gofer/gofer.go
index 2f85215d9..ec8d58cc9 100644
--- a/pkg/sentry/fsimpl/gofer/gofer.go
+++ b/pkg/sentry/fsimpl/gofer/gofer.go
@@ -582,10 +582,10 @@ func (fs *filesystem) Release(ctx context.Context) {
d.dataMu.Unlock()
// Close host FDs if they exist.
if d.readFD >= 0 {
- unix.Close(int(d.readFD))
+ _ = unix.Close(int(d.readFD))
}
if d.writeFD >= 0 && d.readFD != d.writeFD {
- unix.Close(int(d.writeFD))
+ _ = unix.Close(int(d.writeFD))
}
d.readFD = -1
d.writeFD = -1
@@ -1292,7 +1292,7 @@ func (d *dentry) checkXattrPermissions(creds *auth.Credentials, name string, ats
// to the remote filesystem. This is inconsistent with Linux's 9p client,
// but consistent with other filesystems (e.g. FUSE).
if strings.HasPrefix(name, linux.XATTR_SECURITY_PREFIX) || strings.HasPrefix(name, linux.XATTR_SYSTEM_PREFIX) {
- return syserror.EOPNOTSUPP
+ return linuxerr.EOPNOTSUPP
}
mode := linux.FileMode(atomic.LoadUint32(&d.mode))
kuid := auth.KUID(atomic.LoadUint32(&d.uid))
@@ -1637,18 +1637,18 @@ func (d *dentry) destroyLocked(ctx context.Context) {
d.dataMu.Unlock()
// Clunk open fids and close open host FDs.
if !d.readFile.isNil() {
- d.readFile.close(ctx)
+ _ = d.readFile.close(ctx)
}
if !d.writeFile.isNil() && d.readFile != d.writeFile {
- d.writeFile.close(ctx)
+ _ = d.writeFile.close(ctx)
}
d.readFile = p9file{}
d.writeFile = p9file{}
if d.readFD >= 0 {
- unix.Close(int(d.readFD))
+ _ = unix.Close(int(d.readFD))
}
if d.writeFD >= 0 && d.readFD != d.writeFD {
- unix.Close(int(d.writeFD))
+ _ = unix.Close(int(d.writeFD))
}
d.readFD = -1
d.writeFD = -1
diff --git a/pkg/sentry/fsimpl/gofer/regular_file.go b/pkg/sentry/fsimpl/gofer/regular_file.go
index 7f458dd05..947dbe05f 100644
--- a/pkg/sentry/fsimpl/gofer/regular_file.go
+++ b/pkg/sentry/fsimpl/gofer/regular_file.go
@@ -35,7 +35,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/usage"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -80,17 +79,22 @@ func (fd *regularFileFD) OnClose(ctx context.Context) error {
if !fd.vfsfd.IsWritable() {
return nil
}
- // Skip flushing if there are client-buffered writes, since (as with the
- // VFS1 client) we don't flush buffered writes on close anyway.
d := fd.dentry()
- if d.fs.opts.interop != InteropModeExclusive {
- return nil
- }
- d.dataMu.RLock()
- haveDirtyPages := !d.dirty.IsEmpty()
- d.dataMu.RUnlock()
- if haveDirtyPages {
- return nil
+ if d.fs.opts.interop == InteropModeExclusive {
+ // d may have dirty pages that we won't write back now (and wouldn't
+ // have in VFS1), making a flushf RPC ineffective. If this is the case,
+ // skip the flushf.
+ //
+ // Note that it's also possible to have dirty pages under other interop
+ // modes if forcePageCache is in effect; we conservatively assume that
+ // applications have some way of tolerating this and still want the
+ // flushf.
+ d.dataMu.RLock()
+ haveDirtyPages := !d.dirty.IsEmpty()
+ d.dataMu.RUnlock()
+ if haveDirtyPages {
+ return nil
+ }
}
d.handleMu.RLock()
defer d.handleMu.RUnlock()
@@ -132,7 +136,7 @@ func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
//
// TODO(gvisor.dev/issue/2601): Support select preadv2 flags.
if opts.Flags&^linux.RWF_HIPRI != 0 {
- return 0, syserror.EOPNOTSUPP
+ return 0, linuxerr.EOPNOTSUPP
}
// Check for reading at EOF before calling into MM (but not under
@@ -202,7 +206,7 @@ func (fd *regularFileFD) pwrite(ctx context.Context, src usermem.IOSequence, off
//
// TODO(gvisor.dev/issue/2601): Support select pwritev2 flags.
if opts.Flags&^linux.RWF_HIPRI != 0 {
- return 0, offset, syserror.EOPNOTSUPP
+ return 0, offset, linuxerr.EOPNOTSUPP
}
d := fd.dentry()
@@ -708,14 +712,8 @@ func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpt
return vfs.GenericConfigureMMap(&fd.vfsfd, d, opts)
}
-func (d *dentry) mayCachePages() bool {
- if d.fs.opts.forcePageCache {
- return true
- }
- if d.fs.opts.interop == InteropModeShared {
- return false
- }
- return atomic.LoadInt32(&d.mmapFD) >= 0
+func (fs *filesystem) mayCachePagesInMemoryFile() bool {
+ return fs.opts.forcePageCache || fs.opts.interop != InteropModeShared
}
// AddMapping implements memmap.Mappable.AddMapping.
@@ -727,7 +725,7 @@ func (d *dentry) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar host
for _, r := range mapped {
d.pf.hostFileMapper.IncRefOn(r)
}
- if d.mayCachePages() {
+ if d.fs.mayCachePagesInMemoryFile() {
// d.Evict() will refuse to evict memory-mapped pages, so tell the
// MemoryFile to not bother trying.
mf := d.fs.mfp.MemoryFile()
@@ -746,7 +744,7 @@ func (d *dentry) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar h
for _, r := range unmapped {
d.pf.hostFileMapper.DecRefOn(r)
}
- if d.mayCachePages() {
+ if d.fs.mayCachePagesInMemoryFile() {
// Pages that are no longer referenced by any application memory
// mappings are now considered unused; allow MemoryFile to evict them
// when necessary.
diff --git a/pkg/sentry/fsimpl/gofer/special_file.go b/pkg/sentry/fsimpl/gofer/special_file.go
index 690cde707..4b59c1c3c 100644
--- a/pkg/sentry/fsimpl/gofer/special_file.go
+++ b/pkg/sentry/fsimpl/gofer/special_file.go
@@ -42,6 +42,11 @@ import (
type specialFileFD struct {
fileDescription
+ // releaseMu synchronizes the closing of fd.handle with fd.sync(). It's safe
+ // to access fd.handle without locking for operations that require a ref to
+ // be held by the caller, e.g. vfs.FileDescriptionImpl implementations.
+ releaseMu sync.RWMutex `state:"nosave"`
+
// handle is used for file I/O. handle is immutable.
handle handle `state:"nosave"`
@@ -117,7 +122,10 @@ func (fd *specialFileFD) Release(ctx context.Context) {
if fd.haveQueue {
fdnotifier.RemoveFD(fd.handle.fd)
}
+ fd.releaseMu.Lock()
fd.handle.close(ctx)
+ fd.releaseMu.Unlock()
+
fs := fd.vfsfd.Mount().Filesystem().Impl().(*filesystem)
fs.syncMu.Lock()
delete(fs.specialFileFDs, fd)
@@ -191,7 +199,7 @@ func (fd *specialFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
//
// TODO(gvisor.dev/issue/2601): Support select preadv2 flags.
if opts.Flags&^linux.RWF_HIPRI != 0 {
- return 0, syserror.EOPNOTSUPP
+ return 0, linuxerr.EOPNOTSUPP
}
if d := fd.dentry(); d.cachedMetadataAuthoritative() {
@@ -271,7 +279,7 @@ func (fd *specialFileFD) pwrite(ctx context.Context, src usermem.IOSequence, off
//
// TODO(gvisor.dev/issue/2601): Support select pwritev2 flags.
if opts.Flags&^linux.RWF_HIPRI != 0 {
- return 0, offset, syserror.EOPNOTSUPP
+ return 0, offset, linuxerr.EOPNOTSUPP
}
d := fd.dentry()
@@ -373,6 +381,13 @@ func (fd *specialFileFD) Sync(ctx context.Context) error {
}
func (fd *specialFileFD) sync(ctx context.Context, forFilesystemSync bool) error {
+ // Locks to ensure it didn't race with fd.Release().
+ fd.releaseMu.RLock()
+ defer fd.releaseMu.RUnlock()
+
+ if !fd.handle.isOpen() {
+ return nil
+ }
err := func() error {
// If we have a host FD, fsyncing it is likely to be faster than an fsync
// RPC.