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 }