github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/fuse/fusefs.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package fuse implements fusefs.
    16  package fuse
    17  
    18  import (
    19  	"math"
    20  	"strconv"
    21  
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/log"
    26  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsimpl/kernfs"
    27  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel"
    28  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth"
    29  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/time"
    30  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs"
    31  )
    32  
    33  // Name is the default filesystem name.
    34  const Name = "fuse"
    35  
    36  // maxActiveRequestsDefault is the default setting controlling the upper bound
    37  // on the number of active requests at any given time.
    38  const maxActiveRequestsDefault = 10000
    39  
    40  // FilesystemType implements vfs.FilesystemType.
    41  //
    42  // +stateify savable
    43  type FilesystemType struct{}
    44  
    45  // +stateify savable
    46  type filesystemOptions struct {
    47  	// mopts contains the raw, unparsed mount options passed to this filesystem.
    48  	mopts string
    49  
    50  	// uid of the mount owner.
    51  	uid auth.KUID
    52  
    53  	// gid of the mount owner.
    54  	gid auth.KGID
    55  
    56  	// rootMode specifies the the file mode of the filesystem's root.
    57  	rootMode linux.FileMode
    58  
    59  	// maxActiveRequests specifies the maximum number of active requests that can
    60  	// exist at any time. Any further requests will block when trying to
    61  	// Call the server.
    62  	maxActiveRequests uint64
    63  
    64  	// maxRead is the max number of bytes to read,
    65  	// specified as "max_read" in fs parameters.
    66  	// If not specified by user, use math.MaxUint32 as default value.
    67  	maxRead uint32
    68  
    69  	// defaultPermissions is the default_permissions mount option. It instructs
    70  	// the kernel to perform a standard unix permission checks based on
    71  	// ownership and mode bits, instead of deferring the check to the server.
    72  	//
    73  	// Immutable after mount.
    74  	defaultPermissions bool
    75  
    76  	// allowOther is the allow_other mount option. It allows processes that
    77  	// don't own the FUSE mount to call into it.
    78  	//
    79  	// Immutable after mount.
    80  	allowOther bool
    81  }
    82  
    83  // filesystem implements vfs.FilesystemImpl.
    84  //
    85  // +stateify savable
    86  type filesystem struct {
    87  	kernfs.Filesystem
    88  	devMinor uint32
    89  
    90  	// conn is used for communication between the FUSE server
    91  	// daemon and the sentry fusefs.
    92  	conn *connection
    93  
    94  	// opts is the options the fusefs is initialized with.
    95  	opts *filesystemOptions
    96  
    97  	// clock is a real-time clock used to set timestamps in file operations.
    98  	clock time.Clock
    99  }
   100  
   101  // Name implements vfs.FilesystemType.Name.
   102  func (FilesystemType) Name() string {
   103  	return Name
   104  }
   105  
   106  // Release implements vfs.FilesystemType.Release.
   107  func (FilesystemType) Release(ctx context.Context) {}
   108  
   109  // GetFilesystem implements vfs.FilesystemType.GetFilesystem.
   110  func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
   111  	devMinor, err := vfsObj.GetAnonBlockDevMinor()
   112  	if err != nil {
   113  		return nil, nil, err
   114  	}
   115  
   116  	fsopts := filesystemOptions{mopts: opts.Data}
   117  	mopts := vfs.GenericParseMountOptions(opts.Data)
   118  	deviceDescriptorStr, ok := mopts["fd"]
   119  	if !ok {
   120  		ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option fd missing")
   121  		return nil, nil, linuxerr.EINVAL
   122  	}
   123  	delete(mopts, "fd")
   124  
   125  	deviceDescriptor, err := strconv.ParseInt(deviceDescriptorStr, 10 /* base */, 32 /* bitSize */)
   126  	if err != nil {
   127  		ctx.Debugf("fusefs.FilesystemType.GetFilesystem: invalid fd: %q (%v)", deviceDescriptorStr, err)
   128  		return nil, nil, linuxerr.EINVAL
   129  	}
   130  
   131  	kernelTask := kernel.TaskFromContext(ctx)
   132  	if kernelTask == nil {
   133  		log.Warningf("%s.GetFilesystem: couldn't get kernel task from context", fsType.Name())
   134  		return nil, nil, linuxerr.EINVAL
   135  	}
   136  	fuseFDGeneric := kernelTask.GetFile(int32(deviceDescriptor))
   137  	if fuseFDGeneric == nil {
   138  		return nil, nil, linuxerr.EINVAL
   139  	}
   140  	defer fuseFDGeneric.DecRef(ctx)
   141  	fuseFD, ok := fuseFDGeneric.Impl().(*DeviceFD)
   142  	if !ok {
   143  		log.Warningf("%s.GetFilesystem: device FD is %T, not a FUSE device", fsType.Name, fuseFDGeneric)
   144  		return nil, nil, linuxerr.EINVAL
   145  	}
   146  
   147  	// Parse and set all the other supported FUSE mount options.
   148  	// TODO(gVisor.dev/issue/3229): Expand the supported mount options.
   149  	if uidStr, ok := mopts["user_id"]; ok {
   150  		delete(mopts, "user_id")
   151  		uid, err := strconv.ParseUint(uidStr, 10, 32)
   152  		if err != nil {
   153  			log.Warningf("%s.GetFilesystem: invalid user_id: user_id=%s", fsType.Name(), uidStr)
   154  			return nil, nil, linuxerr.EINVAL
   155  		}
   156  		kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
   157  		if !kuid.Ok() {
   158  			ctx.Warningf("fusefs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
   159  			return nil, nil, linuxerr.EINVAL
   160  		}
   161  		fsopts.uid = kuid
   162  	} else {
   163  		ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option user_id missing")
   164  		return nil, nil, linuxerr.EINVAL
   165  	}
   166  
   167  	if gidStr, ok := mopts["group_id"]; ok {
   168  		delete(mopts, "group_id")
   169  		gid, err := strconv.ParseUint(gidStr, 10, 32)
   170  		if err != nil {
   171  			log.Warningf("%s.GetFilesystem: invalid group_id: group_id=%s", fsType.Name(), gidStr)
   172  			return nil, nil, linuxerr.EINVAL
   173  		}
   174  		kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
   175  		if !kgid.Ok() {
   176  			ctx.Warningf("fusefs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
   177  			return nil, nil, linuxerr.EINVAL
   178  		}
   179  		fsopts.gid = kgid
   180  	} else {
   181  		ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option group_id missing")
   182  		return nil, nil, linuxerr.EINVAL
   183  	}
   184  
   185  	if modeStr, ok := mopts["rootmode"]; ok {
   186  		delete(mopts, "rootmode")
   187  		mode, err := strconv.ParseUint(modeStr, 8, 32)
   188  		if err != nil {
   189  			log.Warningf("%s.GetFilesystem: invalid mode: %q", fsType.Name(), modeStr)
   190  			return nil, nil, linuxerr.EINVAL
   191  		}
   192  		fsopts.rootMode = linux.FileMode(mode)
   193  	} else {
   194  		ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option rootmode missing")
   195  		return nil, nil, linuxerr.EINVAL
   196  	}
   197  
   198  	// Set the maxInFlightRequests option.
   199  	fsopts.maxActiveRequests = maxActiveRequestsDefault
   200  
   201  	if maxReadStr, ok := mopts["max_read"]; ok {
   202  		delete(mopts, "max_read")
   203  		maxRead, err := strconv.ParseUint(maxReadStr, 10, 32)
   204  		if err != nil {
   205  			log.Warningf("%s.GetFilesystem: invalid max_read: max_read=%s", fsType.Name(), maxReadStr)
   206  			return nil, nil, linuxerr.EINVAL
   207  		}
   208  		if maxRead < fuseMinMaxRead {
   209  			maxRead = fuseMinMaxRead
   210  		}
   211  		fsopts.maxRead = uint32(maxRead)
   212  	} else {
   213  		fsopts.maxRead = math.MaxUint32
   214  	}
   215  
   216  	if _, ok := mopts["default_permissions"]; ok {
   217  		delete(mopts, "default_permissions")
   218  		fsopts.defaultPermissions = true
   219  	}
   220  
   221  	if _, ok := mopts["allow_other"]; ok {
   222  		delete(mopts, "allow_other")
   223  		fsopts.allowOther = true
   224  	}
   225  
   226  	// Check for unparsed options.
   227  	if len(mopts) != 0 {
   228  		log.Warningf("%s.GetFilesystem: unsupported or unknown options: %v", fsType.Name(), mopts)
   229  		return nil, nil, linuxerr.EINVAL
   230  	}
   231  
   232  	fuseFD.mu.Lock()
   233  	connected := fuseFD.connected()
   234  	// Create a new FUSE filesystem.
   235  	fs, err := newFUSEFilesystem(ctx, vfsObj, &fsType, fuseFD, devMinor, &fsopts)
   236  	if err != nil {
   237  		log.Warningf("%s.NewFUSEFilesystem: failed with error: %v", fsType.Name(), err)
   238  		fuseFD.mu.Unlock()
   239  		return nil, nil, err
   240  	}
   241  	fuseFD.mu.Unlock()
   242  
   243  	// Send a FUSE_INIT request to the FUSE daemon server before returning.
   244  	// This call is not blocking.
   245  	if !connected {
   246  		if err := fs.conn.InitSend(creds, uint32(kernelTask.ThreadID())); err != nil {
   247  			log.Warningf("%s.InitSend: failed with error: %v", fsType.Name(), err)
   248  			return nil, nil, err
   249  		}
   250  	}
   251  
   252  	// root is the fusefs root directory.
   253  	root := fs.newRoot(ctx, creds, fsopts.rootMode)
   254  
   255  	return fs.VFSFilesystem(), root.VFSDentry(), nil
   256  }
   257  
   258  // newFUSEFilesystem creates a new FUSE filesystem.
   259  // +checklocks:fuseFD.mu
   260  func newFUSEFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, fsType *FilesystemType, fuseFD *DeviceFD, devMinor uint32, opts *filesystemOptions) (*filesystem, error) {
   261  	if !fuseFD.connected() {
   262  		conn, err := newFUSEConnection(ctx, fuseFD, opts)
   263  		if err != nil {
   264  			log.Warningf("fuse.NewFUSEFilesystem: NewFUSEConnection failed with error: %v", err)
   265  			return nil, linuxerr.EINVAL
   266  		}
   267  		fuseFD.conn = conn
   268  	}
   269  
   270  	fs := &filesystem{
   271  		devMinor: devMinor,
   272  		opts:     opts,
   273  		conn:     fuseFD.conn,
   274  		clock:    time.RealtimeClockFromContext(ctx),
   275  	}
   276  	fs.VFSFilesystem().Init(vfsObj, fsType, fs)
   277  	return fs, nil
   278  }
   279  
   280  // Release implements vfs.FilesystemImpl.Release.
   281  func (fs *filesystem) Release(ctx context.Context) {
   282  	fs.Filesystem.VFSFilesystem().VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
   283  	fs.Filesystem.Release(ctx)
   284  }
   285  
   286  // MountOptions implements vfs.FilesystemImpl.MountOptions.
   287  func (fs *filesystem) MountOptions() string {
   288  	return fs.opts.mopts
   289  }
   290  
   291  func (fs *filesystem) newRoot(ctx context.Context, creds *auth.Credentials, mode linux.FileMode) *kernfs.Dentry {
   292  	i := &inode{fs: fs, nodeID: 1}
   293  	i.attrMu.Lock()
   294  	i.init(creds, linux.UNNAMED_MAJOR, fs.devMinor, 1, linux.ModeDirectory|0755, 2)
   295  	i.attrMu.Unlock()
   296  	i.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
   297  	i.InitRefs()
   298  
   299  	var d kernfs.Dentry
   300  	d.InitRoot(&fs.Filesystem, i)
   301  	return &d
   302  }
   303  
   304  func (fs *filesystem) newInode(ctx context.Context, nodeID uint64, attr linux.FUSEAttr) kernfs.Inode {
   305  	i := &inode{fs: fs, nodeID: nodeID}
   306  	creds := auth.Credentials{EffectiveKGID: auth.KGID(attr.UID), EffectiveKUID: auth.KUID(attr.UID)}
   307  	i.attrMu.Lock()
   308  	i.init(&creds, linux.UNNAMED_MAJOR, fs.devMinor, nodeID, linux.FileMode(attr.Mode), attr.Nlink)
   309  	i.size.Store(attr.Size)
   310  	i.attrMu.Unlock()
   311  	i.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
   312  	i.InitRefs()
   313  	return i
   314  }