github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/vfs/filesystem.go (about) 1 // Copyright 2019 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 vfs 16 17 import ( 18 "github.com/ttpreport/gvisor-ligolo/pkg/abi/linux" 19 "github.com/ttpreport/gvisor-ligolo/pkg/context" 20 "github.com/ttpreport/gvisor-ligolo/pkg/fspath" 21 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel/auth" 22 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/socket/unix/transport" 23 ) 24 25 // A Filesystem is a tree of nodes represented by Dentries, which forms part of 26 // a VirtualFilesystem. 27 // 28 // Filesystems are reference-counted. Unless otherwise specified, all 29 // Filesystem methods require that a reference is held. 30 // 31 // Filesystem is analogous to Linux's struct super_block. 32 // 33 // +stateify savable 34 type Filesystem struct { 35 FilesystemRefs 36 37 // vfs is the VirtualFilesystem that uses this Filesystem. vfs is 38 // immutable. 39 vfs *VirtualFilesystem 40 41 // fsType is the FilesystemType of this Filesystem. 42 fsType FilesystemType 43 44 // impl is the FilesystemImpl associated with this Filesystem. impl is 45 // immutable. This should be the last field in Dentry. 46 impl FilesystemImpl 47 } 48 49 // Init must be called before first use of fs. 50 func (fs *Filesystem) Init(vfsObj *VirtualFilesystem, fsType FilesystemType, impl FilesystemImpl) { 51 fs.InitRefs() 52 fs.vfs = vfsObj 53 fs.fsType = fsType 54 fs.impl = impl 55 vfsObj.filesystemsMu.Lock() 56 vfsObj.filesystems[fs] = struct{}{} 57 vfsObj.filesystemsMu.Unlock() 58 } 59 60 // FilesystemType returns the FilesystemType for this Filesystem. 61 func (fs *Filesystem) FilesystemType() FilesystemType { 62 return fs.fsType 63 } 64 65 // VirtualFilesystem returns the containing VirtualFilesystem. 66 func (fs *Filesystem) VirtualFilesystem() *VirtualFilesystem { 67 return fs.vfs 68 } 69 70 // Impl returns the FilesystemImpl associated with fs. 71 func (fs *Filesystem) Impl() FilesystemImpl { 72 return fs.impl 73 } 74 75 // DecRef decrements fs' reference count. 76 func (fs *Filesystem) DecRef(ctx context.Context) { 77 fs.FilesystemRefs.DecRef(func() { 78 fs.vfs.filesystemsMu.Lock() 79 delete(fs.vfs.filesystems, fs) 80 fs.vfs.filesystemsMu.Unlock() 81 fs.impl.Release(ctx) 82 }) 83 } 84 85 // FilesystemImpl contains implementation details for a Filesystem. 86 // Implementations of FilesystemImpl should contain their associated Filesystem 87 // by value as their first field. 88 // 89 // All methods that take a ResolvingPath must resolve the path before 90 // performing any other checks, including rejection of the operation if not 91 // supported by the FilesystemImpl. This is because the final FilesystemImpl 92 // (responsible for actually implementing the operation) isn't known until path 93 // resolution is complete. 94 // 95 // Unless otherwise specified, FilesystemImpl methods are responsible for 96 // performing permission checks. In many cases, vfs package functions in 97 // permissions.go may be used to help perform these checks. 98 // 99 // When multiple specified error conditions apply to a given method call, the 100 // implementation may return any applicable errno unless otherwise specified, 101 // but returning the earliest error specified is preferable to maximize 102 // compatibility with Linux. 103 // 104 // All methods may return errors not specified, notably including: 105 // 106 // - ENOENT if a required path component does not exist. 107 // 108 // - ENOTDIR if an intermediate path component is not a directory. 109 // 110 // - Errors from vfs-package functions (ResolvingPath.Resolve*(), 111 // Mount.CheckBeginWrite(), permission-checking functions, etc.) 112 // 113 // For all methods that take or return linux.Statx, Statx.Uid and Statx.Gid 114 // should be interpreted as IDs in the root UserNamespace (i.e. as auth.KUID 115 // and auth.KGID respectively). 116 // 117 // FilesystemImpl combines elements of Linux's struct super_operations and 118 // struct inode_operations, for reasons described in the documentation for 119 // Dentry. 120 type FilesystemImpl interface { 121 // Release is called when the associated Filesystem reaches zero 122 // references. 123 Release(ctx context.Context) 124 125 // Sync "causes all pending modifications to filesystem metadata and cached 126 // file data to be written to the underlying [filesystem]", as by syncfs(2). 127 Sync(ctx context.Context) error 128 129 // AccessAt checks whether a user with creds can access the file at rp. 130 AccessAt(ctx context.Context, rp *ResolvingPath, creds *auth.Credentials, ats AccessTypes) error 131 132 // GetDentryAt returns a Dentry representing the file at rp. A reference is 133 // taken on the returned Dentry. 134 // 135 // GetDentryAt does not correspond directly to a Linux syscall; it is used 136 // in the implementation of: 137 // 138 // - Syscalls that need to resolve two paths: link(), linkat(). 139 // 140 // - Syscalls that need to refer to a filesystem position outside the 141 // context of a file description: chdir(), fchdir(), chroot(), mount(), 142 // umount(). 143 GetDentryAt(ctx context.Context, rp *ResolvingPath, opts GetDentryOptions) (*Dentry, error) 144 145 // GetParentDentryAt returns a Dentry representing the directory at the 146 // second-to-last path component in rp. (Note that, despite the name, this 147 // is not necessarily the parent directory of the file at rp, since the 148 // last path component in rp may be "." or "..".) A reference is taken on 149 // the returned Dentry. 150 // 151 // GetParentDentryAt does not correspond directly to a Linux syscall; it is 152 // used in the implementation of the rename() family of syscalls, which 153 // must resolve the parent directories of two paths. 154 // 155 // Preconditions: !rp.Done(). 156 // 157 // Postconditions: If GetParentDentryAt returns a nil error, then 158 // rp.Final(). If GetParentDentryAt returns an error returned by 159 // ResolvingPath.Resolve*(), then !rp.Done(). 160 GetParentDentryAt(ctx context.Context, rp *ResolvingPath) (*Dentry, error) 161 162 // LinkAt creates a hard link at rp representing the same file as vd. It 163 // does not take ownership of references on vd. 164 // 165 // Errors: 166 // 167 // - If the last path component in rp is "." or "..", LinkAt returns 168 // EEXIST. 169 // 170 // - If a file already exists at rp, LinkAt returns EEXIST. 171 // 172 // - If rp.MustBeDir(), LinkAt returns ENOENT. 173 // 174 // - If the directory in which the link would be created has been removed 175 // by RmdirAt or RenameAt, LinkAt returns ENOENT. 176 // 177 // - If rp.Mount != vd.Mount(), LinkAt returns EXDEV. 178 // 179 // - If vd represents a directory, LinkAt returns EPERM. 180 // 181 // - If vd represents a file for which all existing links have been 182 // removed, or a file created by open(O_TMPFILE|O_EXCL), LinkAt returns 183 // ENOENT. Equivalently, if vd represents a file with a link count of 0 not 184 // created by open(O_TMPFILE) without O_EXCL, LinkAt returns ENOENT. 185 // 186 // Preconditions: 187 // * !rp.Done(). 188 // * For the final path component in rp, !rp.ShouldFollowSymlink(). 189 // 190 // Postconditions: If LinkAt returns an error returned by 191 // ResolvingPath.Resolve*(), then !rp.Done(). 192 LinkAt(ctx context.Context, rp *ResolvingPath, vd VirtualDentry) error 193 194 // MkdirAt creates a directory at rp. 195 // 196 // Errors: 197 // 198 // - If the last path component in rp is "." or "..", MkdirAt returns 199 // EEXIST. 200 // 201 // - If a file already exists at rp, MkdirAt returns EEXIST. 202 // 203 // - If the directory in which the new directory would be created has been 204 // removed by RmdirAt or RenameAt, MkdirAt returns ENOENT. 205 // 206 // Preconditions: 207 // * !rp.Done(). 208 // * For the final path component in rp, !rp.ShouldFollowSymlink(). 209 // 210 // Postconditions: If MkdirAt returns an error returned by 211 // ResolvingPath.Resolve*(), then !rp.Done(). 212 MkdirAt(ctx context.Context, rp *ResolvingPath, opts MkdirOptions) error 213 214 // MknodAt creates a regular file, device special file, or named pipe at 215 // rp. 216 // 217 // Errors: 218 // 219 // - If the last path component in rp is "." or "..", MknodAt returns 220 // EEXIST. 221 // 222 // - If a file already exists at rp, MknodAt returns EEXIST. 223 // 224 // - If rp.MustBeDir(), MknodAt returns ENOENT. 225 // 226 // - If the directory in which the file would be created has been removed 227 // by RmdirAt or RenameAt, MknodAt returns ENOENT. 228 // 229 // Preconditions: 230 // * !rp.Done(). 231 // * For the final path component in rp, !rp.ShouldFollowSymlink(). 232 // 233 // Postconditions: If MknodAt returns an error returned by 234 // ResolvingPath.Resolve*(), then !rp.Done(). 235 MknodAt(ctx context.Context, rp *ResolvingPath, opts MknodOptions) error 236 237 // OpenAt returns an FileDescription providing access to the file at rp. A 238 // reference is taken on the returned FileDescription. 239 // 240 // Errors: 241 // 242 // - If opts.Flags specifies O_TMPFILE and this feature is unsupported by 243 // the implementation, OpenAt returns EOPNOTSUPP. (All other unsupported 244 // features are silently ignored, consistently with Linux's open*(2).) 245 OpenAt(ctx context.Context, rp *ResolvingPath, opts OpenOptions) (*FileDescription, error) 246 247 // ReadlinkAt returns the target of the symbolic link at rp. 248 // 249 // Errors: 250 // 251 // - If the file at rp is not a symbolic link, ReadlinkAt returns EINVAL. 252 ReadlinkAt(ctx context.Context, rp *ResolvingPath) (string, error) 253 254 // RenameAt renames the file named oldName in directory oldParentVD to rp. 255 // It does not take ownership of references on oldParentVD. 256 // 257 // Errors [1]: 258 // 259 // - If opts.Flags specifies unsupported options, RenameAt returns EINVAL. 260 // 261 // - If the last path component in rp is "." or "..", and opts.Flags 262 // contains RENAME_NOREPLACE, RenameAt returns EEXIST. 263 // 264 // - If the last path component in rp is "." or "..", and opts.Flags does 265 // not contain RENAME_NOREPLACE, RenameAt returns EBUSY. 266 // 267 // - If rp.Mount != oldParentVD.Mount(), RenameAt returns EXDEV. 268 // 269 // - If the renamed file is not a directory, and opts.MustBeDir is true, 270 // RenameAt returns ENOTDIR. 271 // 272 // - If renaming would replace an existing file and opts.Flags contains 273 // RENAME_NOREPLACE, RenameAt returns EEXIST. 274 // 275 // - If there is no existing file at rp and opts.Flags contains 276 // RENAME_EXCHANGE, RenameAt returns ENOENT. 277 // 278 // - If there is an existing non-directory file at rp, and rp.MustBeDir() 279 // is true, RenameAt returns ENOTDIR. 280 // 281 // - If the renamed file is not a directory, opts.Flags does not contain 282 // RENAME_EXCHANGE, and rp.MustBeDir() is true, RenameAt returns ENOTDIR. 283 // (This check is not subsumed by the check for directory replacement below 284 // since it applies even if there is no file to replace.) 285 // 286 // - If the renamed file is a directory, and the new parent directory of 287 // the renamed file is either the renamed directory or a descendant 288 // subdirectory of the renamed directory, RenameAt returns EINVAL. 289 // 290 // - If renaming would exchange the renamed file with an ancestor directory 291 // of the renamed file, RenameAt returns EINVAL. 292 // 293 // - If renaming would replace an ancestor directory of the renamed file, 294 // RenameAt returns ENOTEMPTY. (This check would be subsumed by the 295 // non-empty directory check below; however, this check takes place before 296 // the self-rename check.) 297 // 298 // - If the renamed file would replace or exchange with itself (i.e. the 299 // source and destination paths resolve to the same file), RenameAt returns 300 // nil, skipping the checks described below. 301 // 302 // - If the source or destination directory is not writable by the provider 303 // of rp.Credentials(), RenameAt returns EACCES. 304 // 305 // - If the renamed file is a directory, and renaming would replace a 306 // non-directory file, RenameAt returns ENOTDIR. 307 // 308 // - If the renamed file is not a directory, and renaming would replace a 309 // directory, RenameAt returns EISDIR. 310 // 311 // - If the new parent directory of the renamed file has been removed by 312 // RmdirAt or a preceding call to RenameAt, RenameAt returns ENOENT. 313 // 314 // - If the renamed file is a directory, it is not writable by the 315 // provider of rp.Credentials(), and the source and destination parent 316 // directories are different, RenameAt returns EACCES. (This is nominally 317 // required to change the ".." entry in the renamed directory.) 318 // 319 // - If renaming would replace a non-empty directory, RenameAt returns 320 // ENOTEMPTY. 321 // 322 // Preconditions: 323 // * !rp.Done(). 324 // * For the final path component in rp, !rp.ShouldFollowSymlink(). 325 // * oldParentVD.Dentry() was obtained from a previous call to 326 // oldParentVD.Mount().Filesystem().Impl().GetParentDentryAt(). 327 // * oldName is not "." or "..". 328 // 329 // Postconditions: If RenameAt returns an error returned by 330 // ResolvingPath.Resolve*(), then !rp.Done(). 331 // 332 // [1] "The worst of all namespace operations - renaming directory. 333 // "Perverted" doesn't even start to describe it. Somebody in UCB had a 334 // heck of a trip..." - fs/namei.c:vfs_rename() 335 RenameAt(ctx context.Context, rp *ResolvingPath, oldParentVD VirtualDentry, oldName string, opts RenameOptions) error 336 337 // RmdirAt removes the directory at rp. 338 // 339 // Errors: 340 // 341 // - If the last path component in rp is ".", RmdirAt returns EINVAL. 342 // 343 // - If the last path component in rp is "..", RmdirAt returns ENOTEMPTY. 344 // 345 // - If no file exists at rp, RmdirAt returns ENOENT. 346 // 347 // - If the file at rp exists but is not a directory, RmdirAt returns 348 // ENOTDIR. 349 // 350 // Preconditions: 351 // * !rp.Done(). 352 // * For the final path component in rp, !rp.ShouldFollowSymlink(). 353 // 354 // Postconditions: If RmdirAt returns an error returned by 355 // ResolvingPath.Resolve*(), then !rp.Done(). 356 RmdirAt(ctx context.Context, rp *ResolvingPath) error 357 358 // SetStatAt updates metadata for the file at the given path. Implementations 359 // are responsible for checking if the operation can be performed 360 // (see vfs.CheckSetStat() for common checks). 361 // 362 // Errors: 363 // 364 // - If opts specifies unsupported options, SetStatAt returns EINVAL. 365 SetStatAt(ctx context.Context, rp *ResolvingPath, opts SetStatOptions) error 366 367 // StatAt returns metadata for the file at rp. 368 StatAt(ctx context.Context, rp *ResolvingPath, opts StatOptions) (linux.Statx, error) 369 370 // StatFSAt returns metadata for the filesystem containing the file at rp. 371 // (This method takes a path because a FilesystemImpl may consist of any 372 // number of constituent filesystems.) 373 StatFSAt(ctx context.Context, rp *ResolvingPath) (linux.Statfs, error) 374 375 // SymlinkAt creates a symbolic link at rp referring to the given target. 376 // 377 // Errors: 378 // 379 // - If the last path component in rp is "." or "..", SymlinkAt returns 380 // EEXIST. 381 // 382 // - If a file already exists at rp, SymlinkAt returns EEXIST. 383 // 384 // - If rp.MustBeDir(), SymlinkAt returns ENOENT. 385 // 386 // - If the directory in which the symbolic link would be created has been 387 // removed by RmdirAt or RenameAt, SymlinkAt returns ENOENT. 388 // 389 // Preconditions: 390 // * !rp.Done(). 391 // * For the final path component in rp, !rp.ShouldFollowSymlink(). 392 // 393 // Postconditions: If SymlinkAt returns an error returned by 394 // ResolvingPath.Resolve*(), then !rp.Done(). 395 SymlinkAt(ctx context.Context, rp *ResolvingPath, target string) error 396 397 // UnlinkAt removes the file at rp. 398 // 399 // Errors: 400 // 401 // - If the last path component in rp is "." or "..", UnlinkAt returns 402 // EISDIR. 403 // 404 // - If no file exists at rp, UnlinkAt returns ENOENT. 405 // 406 // - If rp.MustBeDir(), and the file at rp exists and is not a directory, 407 // UnlinkAt returns ENOTDIR. 408 // 409 // - If the file at rp exists but is a directory, UnlinkAt returns EISDIR. 410 // 411 // Preconditions: 412 // * !rp.Done(). 413 // * For the final path component in rp, !rp.ShouldFollowSymlink(). 414 // 415 // Postconditions: If UnlinkAt returns an error returned by 416 // ResolvingPath.Resolve*(), then !rp.Done(). 417 UnlinkAt(ctx context.Context, rp *ResolvingPath) error 418 419 // ListXattrAt returns all extended attribute names for the file at rp. 420 // 421 // Errors: 422 // 423 // - If extended attributes are not supported by the filesystem, 424 // ListXattrAt returns ENOTSUP. 425 // 426 // - If the size of the list (including a NUL terminating byte after every 427 // entry) would exceed size, ERANGE may be returned. Note that 428 // implementations are free to ignore size entirely and return without 429 // error). In all cases, if size is 0, the list should be returned without 430 // error, regardless of size. 431 ListXattrAt(ctx context.Context, rp *ResolvingPath, size uint64) ([]string, error) 432 433 // GetXattrAt returns the value associated with the given extended 434 // attribute for the file at rp. 435 // 436 // Errors: 437 // 438 // - If extended attributes are not supported by the filesystem, GetXattrAt 439 // returns ENOTSUP. 440 // 441 // - If an extended attribute named opts.Name does not exist, ENODATA is 442 // returned. 443 // 444 // - If the size of the return value exceeds opts.Size, ERANGE may be 445 // returned (note that implementations are free to ignore opts.Size entirely 446 // and return without error). In all cases, if opts.Size is 0, the value 447 // should be returned without error, regardless of size. 448 GetXattrAt(ctx context.Context, rp *ResolvingPath, opts GetXattrOptions) (string, error) 449 450 // SetXattrAt changes the value associated with the given extended 451 // attribute for the file at rp. 452 // 453 // Errors: 454 // 455 // - If extended attributes are not supported by the filesystem, SetXattrAt 456 // returns ENOTSUP. 457 // 458 // - If XATTR_CREATE is set in opts.Flag and opts.Name already exists, 459 // EEXIST is returned. If XATTR_REPLACE is set and opts.Name does not exist, 460 // ENODATA is returned. 461 SetXattrAt(ctx context.Context, rp *ResolvingPath, opts SetXattrOptions) error 462 463 // RemoveXattrAt removes the given extended attribute from the file at rp. 464 // 465 // Errors: 466 // 467 // - If extended attributes are not supported by the filesystem, 468 // RemoveXattrAt returns ENOTSUP. 469 // 470 // - If name does not exist, ENODATA is returned. 471 RemoveXattrAt(ctx context.Context, rp *ResolvingPath, name string) error 472 473 // BoundEndpointAt returns the Unix socket endpoint bound at the path rp. 474 // 475 // Errors: 476 // 477 // - If the file does not have write permissions, then BoundEndpointAt 478 // returns EACCES. 479 // 480 // - If a non-socket file exists at rp, then BoundEndpointAt returns 481 // ECONNREFUSED. 482 BoundEndpointAt(ctx context.Context, rp *ResolvingPath, opts BoundEndpointOptions) (transport.BoundEndpoint, error) 483 484 // PrependPath prepends a path from vd to vd.Mount().Root() to b. 485 // 486 // If vfsroot.Ok(), it is the contextual VFS root; if it is encountered 487 // before vd.Mount().Root(), PrependPath should stop prepending path 488 // components and return a PrependPathAtVFSRootError. 489 // 490 // If traversal of vd.Dentry()'s ancestors encounters an independent 491 // ("root") Dentry that is not vd.Mount().Root() (i.e. vd.Dentry() is not a 492 // descendant of vd.Mount().Root()), PrependPath should stop prepending 493 // path components and return a PrependPathAtNonMountRootError. 494 // 495 // Filesystems for which Dentries do not have meaningful paths may prepend 496 // an arbitrary descriptive string to b and then return a 497 // PrependPathSyntheticError. 498 // 499 // Most implementations can acquire the appropriate locks to ensure that 500 // Dentry.Name() and Dentry.Parent() are fixed for vd.Dentry() and all of 501 // its ancestors, then call GenericPrependPath. 502 // 503 // Preconditions: vd.Mount().Filesystem().Impl() == this FilesystemImpl. 504 PrependPath(ctx context.Context, vfsroot, vd VirtualDentry, b *fspath.Builder) error 505 506 // MountOptions returns mount options for the current filesystem. This 507 // should only return options specific to the filesystem (i.e. don't return 508 // "ro", "rw", etc). Options should be returned as a comma-separated string, 509 // similar to the input to the 5th argument to mount. 510 // 511 // If the implementation has no filesystem-specific options, it should 512 // return the empty string. 513 MountOptions() string 514 } 515 516 // PrependPathAtVFSRootError is returned by implementations of 517 // FilesystemImpl.PrependPath() when they encounter the contextual VFS root. 518 // 519 // +stateify savable 520 type PrependPathAtVFSRootError struct{} 521 522 // Error implements error.Error. 523 func (PrependPathAtVFSRootError) Error() string { 524 return "vfs.FilesystemImpl.PrependPath() reached VFS root" 525 } 526 527 // PrependPathAtNonMountRootError is returned by implementations of 528 // FilesystemImpl.PrependPath() when they encounter an independent ancestor 529 // Dentry that is not the Mount root. 530 // 531 // +stateify savable 532 type PrependPathAtNonMountRootError struct{} 533 534 // Error implements error.Error. 535 func (PrependPathAtNonMountRootError) Error() string { 536 return "vfs.FilesystemImpl.PrependPath() reached root other than Mount root" 537 } 538 539 // PrependPathSyntheticError is returned by implementations of 540 // FilesystemImpl.PrependPath() for which prepended names do not represent real 541 // paths. 542 // 543 // +stateify savable 544 type PrependPathSyntheticError struct{} 545 546 // Error implements error.Error. 547 func (PrependPathSyntheticError) Error() string { 548 return "vfs.FilesystemImpl.PrependPath() prepended synthetic name" 549 }