Have fuse.DeviceFD hold reference on fuse.filesystem.

This is actually just b/168751672 again; cl/332394146 was incorrectly reverted
by cl/341411151. Document the reference holder to reduce the likelihood that
this happens again.

Also document a few other bugs observed in the process.

PiperOrigin-RevId: 342339144
This commit is contained in:
Jamie Liu
2020-11-13 14:31:28 -08:00
committed by gVisor bot
parent 839dd97008
commit 89517eca41
5 changed files with 31 additions and 31 deletions
+6 -6
View File
@@ -21,7 +21,6 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -193,11 +192,12 @@ func (conn *connection) loadInitializedChan(closed bool) {
}
}
// newFUSEConnection creates a FUSE connection to fd.
func newFUSEConnection(_ context.Context, fd *vfs.FileDescription, opts *filesystemOptions) (*connection, error) {
// Mark the device as ready so it can be used. /dev/fuse can only be used if the FD was used to
// mount a FUSE filesystem.
fuseFD := fd.Impl().(*DeviceFD)
// newFUSEConnection creates a FUSE connection to fuseFD.
func newFUSEConnection(_ context.Context, fuseFD *DeviceFD, opts *filesystemOptions) (*connection, error) {
// Mark the device as ready so it can be used.
// FIXME(gvisor.dev/issue/4813): fuseFD's fields are accessed without
// synchronization and without checking if fuseFD has already been used to
// mount another filesystem.
// Create the writeBuf for the header to be stored in.
hdrLen := uint32((*linux.FUSEHeaderOut)(nil).SizeBytes())
@@ -198,7 +198,6 @@ func (conn *connection) Abort(ctx context.Context) {
if !conn.connected {
conn.asyncMu.Unlock()
conn.mu.Unlock()
conn.fd.mu.Unlock()
return
}
+2 -7
View File
@@ -94,7 +94,8 @@ type DeviceFD struct {
// unprocessed in-flight requests.
fullQueueCh chan struct{} `state:".(int)"`
// fs is the FUSE filesystem that this FD is being used for.
// fs is the FUSE filesystem that this FD is being used for. A reference is
// held on fs.
fs *filesystem
}
@@ -135,12 +136,6 @@ func (fd *DeviceFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.R
return 0, syserror.EPERM
}
// Return ENODEV if the filesystem is umounted.
if fd.fs.umounted {
// TODO(gvisor.dev/issue/3525): return ECONNABORTED if aborted via fuse control fs.
return 0, syserror.ENODEV
}
// We require that any Read done on this filesystem have a sane minimum
// read buffer. It must have the capacity for the fixed parts of any request
// header (Linux uses the request header and the FUSEWriteIn header for this
+20 -7
View File
@@ -127,7 +127,13 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
log.Warningf("%s.GetFilesystem: couldn't get kernel task from context", fsType.Name())
return nil, nil, syserror.EINVAL
}
fuseFd := kernelTask.GetFileVFS2(int32(deviceDescriptor))
fuseFDGeneric := kernelTask.GetFileVFS2(int32(deviceDescriptor))
defer fuseFDGeneric.DecRef(ctx)
fuseFD, ok := fuseFDGeneric.Impl().(*DeviceFD)
if !ok {
log.Warningf("%s.GetFilesystem: device FD is %T, not a FUSE device", fsType.Name, fuseFDGeneric)
return nil, nil, syserror.EINVAL
}
// Parse and set all the other supported FUSE mount options.
// TODO(gVisor.dev/issue/3229): Expand the supported mount options.
@@ -189,18 +195,17 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
}
// Create a new FUSE filesystem.
fs, err := newFUSEFilesystem(ctx, devMinor, &fsopts, fuseFd)
fs, err := newFUSEFilesystem(ctx, vfsObj, &fsType, fuseFD, devMinor, &fsopts)
if err != nil {
log.Warningf("%s.NewFUSEFilesystem: failed with error: %v", fsType.Name(), err)
return nil, nil, err
}
fs.VFSFilesystem().Init(vfsObj, &fsType, fs)
// Send a FUSE_INIT request to the FUSE daemon server before returning.
// This call is not blocking.
if err := fs.conn.InitSend(creds, uint32(kernelTask.ThreadID())); err != nil {
log.Warningf("%s.InitSend: failed with error: %v", fsType.Name(), err)
fs.VFSFilesystem().DecRef(ctx) // returned by newFUSEFilesystem
return nil, nil, err
}
@@ -211,20 +216,28 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
}
// newFUSEFilesystem creates a new FUSE filesystem.
func newFUSEFilesystem(ctx context.Context, devMinor uint32, opts *filesystemOptions, device *vfs.FileDescription) (*filesystem, error) {
conn, err := newFUSEConnection(ctx, device, opts)
func newFUSEFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, fsType *FilesystemType, fuseFD *DeviceFD, devMinor uint32, opts *filesystemOptions) (*filesystem, error) {
conn, err := newFUSEConnection(ctx, fuseFD, opts)
if err != nil {
log.Warningf("fuse.NewFUSEFilesystem: NewFUSEConnection failed with error: %v", err)
return nil, syserror.EINVAL
}
fuseFD := device.Impl().(*DeviceFD)
fs := &filesystem{
devMinor: devMinor,
opts: opts,
conn: conn,
}
fs.VFSFilesystem().Init(vfsObj, fsType, fs)
// FIXME(gvisor.dev/issue/4813): Doesn't conn or fs need to hold a
// reference on fuseFD, since conn uses fuseFD for communication with the
// server? Wouldn't doing so create a circular reference?
fs.VFSFilesystem().IncRef() // for fuseFD.fs
// FIXME(gvisor.dev/issue/4813): fuseFD.fs is accessed without
// synchronization.
fuseFD.fs = fs
return fs, nil
}
+3 -10
View File
@@ -52,28 +52,21 @@ func setup(t *testing.T) *testutil.System {
// newTestConnection creates a fuse connection that the sentry can communicate with
// and the FD for the server to communicate with.
func newTestConnection(system *testutil.System, k *kernel.Kernel, maxActiveRequests uint64) (*connection, *vfs.FileDescription, error) {
vfsObj := &vfs.VirtualFilesystem{}
fuseDev := &DeviceFD{}
if err := vfsObj.Init(system.Ctx); err != nil {
return nil, nil, err
}
vd := vfsObj.NewAnonVirtualDentry("genCountFD")
vd := system.VFS.NewAnonVirtualDentry("fuse")
defer vd.DecRef(system.Ctx)
if err := fuseDev.vfsfd.Init(fuseDev, linux.O_RDWR|linux.O_CREAT, vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{}); err != nil {
if err := fuseDev.vfsfd.Init(fuseDev, linux.O_RDWR, vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{}); err != nil {
return nil, nil, err
}
fsopts := filesystemOptions{
maxActiveRequests: maxActiveRequests,
}
fs, err := newFUSEFilesystem(system.Ctx, 0, &fsopts, &fuseDev.vfsfd)
fs, err := newFUSEFilesystem(system.Ctx, system.VFS, &FilesystemType{}, fuseDev, 0, &fsopts)
if err != nil {
return nil, nil, err
}
fs.VFSFilesystem().Init(vfsObj, nil, fs)
return fs.conn, &fuseDev.vfsfd, nil
}