github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/vfs/resolving_path.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 "fmt" 19 20 "github.com/ttpreport/gvisor-ligolo/pkg/abi/linux" 21 "github.com/ttpreport/gvisor-ligolo/pkg/context" 22 "github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr" 23 "github.com/ttpreport/gvisor-ligolo/pkg/fspath" 24 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel/auth" 25 "github.com/ttpreport/gvisor-ligolo/pkg/sync" 26 ) 27 28 // ResolvingPath represents the state of an in-progress path resolution, shared 29 // between VFS and FilesystemImpl methods that take a path. 30 // 31 // From the perspective of FilesystemImpl methods, a ResolvingPath represents a 32 // starting Dentry on the associated Filesystem (on which a reference is 33 // already held), a stream of path components relative to that Dentry, and 34 // elements of the invoking Context that are commonly required by 35 // FilesystemImpl methods. 36 // 37 // ResolvingPath is loosely analogous to Linux's struct nameidata. 38 // 39 // +stateify savable 40 type ResolvingPath struct { 41 vfs *VirtualFilesystem 42 root VirtualDentry // refs borrowed from PathOperation 43 mount *Mount 44 start *Dentry 45 pit fspath.Iterator 46 47 flags uint16 48 mustBeDir bool // final file must be a directory? 49 symlinks uint8 // number of symlinks traversed 50 curPart uint8 // index into parts 51 52 creds *auth.Credentials 53 54 // Data associated with resolve*Errors, stored in ResolvingPath so that 55 // those errors don't need to allocate. 56 nextMount *Mount // ref held if not nil 57 nextStart *Dentry // ref held if not nil 58 absSymlinkTarget fspath.Path 59 60 // ResolvingPath tracks relative paths, which is updated whenever a relative 61 // symlink is encountered. 62 parts [1 + linux.MaxSymlinkTraversals]fspath.Iterator 63 } 64 65 const ( 66 rpflagsHaveMountRef = 1 << iota // do we hold a reference on mount? 67 rpflagsHaveStartRef // do we hold a reference on start? 68 rpflagsFollowFinalSymlink // same as PathOperation.FollowFinalSymlink 69 ) 70 71 func init() { 72 if maxParts := len(ResolvingPath{}.parts); maxParts > 255 { 73 panic(fmt.Sprintf("uint8 is insufficient to accommodate len(ResolvingPath.parts) (%d)", maxParts)) 74 } 75 } 76 77 // Error types that communicate state from the FilesystemImpl-caller, 78 // VFS-callee side of path resolution (i.e. errors returned by 79 // ResolvingPath.Resolve*()) to the VFS-caller, FilesystemImpl-callee side 80 // (i.e. VFS methods => ResolvingPath.handleError()). These are empty structs 81 // rather than error values because Go doesn't support non-primitive constants, 82 // so error "constants" are really mutable vars, necessitating somewhat 83 // expensive interface object comparisons. 84 85 // +stateify savable 86 type resolveMountRootOrJumpError struct{} 87 88 // Error implements error.Error. 89 func (resolveMountRootOrJumpError) Error() string { 90 return "resolving mount root or jump" 91 } 92 93 // +stateify savable 94 type resolveMountPointError struct{} 95 96 // Error implements error.Error. 97 func (resolveMountPointError) Error() string { 98 return "resolving mount point" 99 } 100 101 // +stateify savable 102 type resolveAbsSymlinkError struct{} 103 104 // Error implements error.Error. 105 func (resolveAbsSymlinkError) Error() string { 106 return "resolving absolute symlink" 107 } 108 109 var resolvingPathPool = sync.Pool{ 110 New: func() any { 111 return &ResolvingPath{} 112 }, 113 } 114 115 // getResolvingPath gets a new ResolvingPath from the pool. Caller must call 116 // ResolvingPath.Release() when done. 117 func (vfs *VirtualFilesystem) getResolvingPath(creds *auth.Credentials, pop *PathOperation) *ResolvingPath { 118 rp := resolvingPathPool.Get().(*ResolvingPath) 119 rp.vfs = vfs 120 rp.root = pop.Root 121 rp.mount = pop.Start.mount 122 rp.start = pop.Start.dentry 123 rp.pit = pop.Path.Begin 124 rp.flags = 0 125 if pop.FollowFinalSymlink { 126 rp.flags |= rpflagsFollowFinalSymlink 127 } 128 rp.mustBeDir = pop.Path.Dir 129 rp.symlinks = 0 130 rp.curPart = 0 131 rp.creds = creds 132 rp.parts[0] = pop.Path.Begin 133 return rp 134 } 135 136 // Copy creates another ResolvingPath with the same state as the original. 137 // Copies are independent, using the copy does not change the original and 138 // vice-versa. 139 // 140 // Caller must call Resease() when done. 141 func (rp *ResolvingPath) Copy() *ResolvingPath { 142 copy := resolvingPathPool.Get().(*ResolvingPath) 143 *copy = *rp // All fields all shallow copiable. 144 145 // Take extra reference for the copy if the original had them. 146 if copy.flags&rpflagsHaveStartRef != 0 { 147 copy.start.IncRef() 148 } 149 if copy.flags&rpflagsHaveMountRef != 0 { 150 copy.mount.IncRef() 151 } 152 // Reset error state. 153 copy.nextStart = nil 154 copy.nextMount = nil 155 return copy 156 } 157 158 // Release decrements references if needed and returns the object to the pool. 159 func (rp *ResolvingPath) Release(ctx context.Context) { 160 rp.root = VirtualDentry{} 161 rp.decRefStartAndMount(ctx) 162 rp.mount = nil 163 rp.start = nil 164 rp.releaseErrorState(ctx) 165 resolvingPathPool.Put(rp) 166 } 167 168 func (rp *ResolvingPath) decRefStartAndMount(ctx context.Context) { 169 if rp.flags&rpflagsHaveStartRef != 0 { 170 rp.start.DecRef(ctx) 171 } 172 if rp.flags&rpflagsHaveMountRef != 0 { 173 rp.mount.DecRef(ctx) 174 } 175 } 176 177 func (rp *ResolvingPath) releaseErrorState(ctx context.Context) { 178 if rp.nextStart != nil { 179 rp.nextStart.DecRef(ctx) 180 rp.nextStart = nil 181 } 182 if rp.nextMount != nil { 183 rp.nextMount.DecRef(ctx) 184 rp.nextMount = nil 185 } 186 } 187 188 // VirtualFilesystem returns the containing VirtualFilesystem. 189 func (rp *ResolvingPath) VirtualFilesystem() *VirtualFilesystem { 190 return rp.vfs 191 } 192 193 // Credentials returns the credentials of rp's provider. 194 func (rp *ResolvingPath) Credentials() *auth.Credentials { 195 return rp.creds 196 } 197 198 // Mount returns the Mount on which path resolution is currently occurring. It 199 // does not take a reference on the returned Mount. 200 func (rp *ResolvingPath) Mount() *Mount { 201 return rp.mount 202 } 203 204 // Start returns the starting Dentry represented by rp. It does not take a 205 // reference on the returned Dentry. 206 func (rp *ResolvingPath) Start() *Dentry { 207 return rp.start 208 } 209 210 // Done returns true if there are no remaining path components in the stream 211 // represented by rp. 212 func (rp *ResolvingPath) Done() bool { 213 // We don't need to check for rp.curPart == 0 because rp.Advance() won't 214 // set rp.pit to a terminal iterator otherwise. 215 return !rp.pit.Ok() 216 } 217 218 // Final returns true if there is exactly one remaining path component in the 219 // stream represented by rp. 220 // 221 // Preconditions: !rp.Done(). 222 func (rp *ResolvingPath) Final() bool { 223 return rp.curPart == 0 && !rp.pit.NextOk() 224 } 225 226 // Component returns the current path component in the stream represented by 227 // rp. 228 // 229 // Preconditions: !rp.Done(). 230 func (rp *ResolvingPath) Component() string { 231 if checkInvariants { 232 if !rp.pit.Ok() { 233 panic("ResolvingPath.Component() called at end of relative path") 234 } 235 } 236 return rp.pit.String() 237 } 238 239 // Advance advances the stream of path components represented by rp. 240 // 241 // Preconditions: !rp.Done(). 242 func (rp *ResolvingPath) Advance() { 243 if checkInvariants { 244 if !rp.pit.Ok() { 245 panic("ResolvingPath.Advance() called at end of relative path") 246 } 247 } 248 next := rp.pit.Next() 249 if next.Ok() || rp.curPart == 0 { // have next component, or at end of path 250 rp.pit = next 251 } else { // at end of path segment, continue with next one 252 rp.curPart-- 253 rp.pit = rp.parts[rp.curPart] 254 } 255 } 256 257 // GetComponents emits all the remaining path components in rp. It does *not* 258 // update rp state. It halts if emit() returns false. If excludeLast is true, 259 // then the last path component is not emitted. 260 func (rp *ResolvingPath) GetComponents(excludeLast bool, emit func(string) bool) { 261 // Copy rp state. 262 cur := rp.pit 263 curPart := rp.curPart 264 for cur.Ok() { 265 if excludeLast && curPart == 0 && !cur.NextOk() { 266 break 267 } 268 if !emit(cur.String()) { 269 break 270 } 271 cur = cur.Next() 272 if !cur.Ok() && curPart > 0 { 273 curPart-- 274 cur = rp.parts[curPart] 275 } 276 } 277 } 278 279 // CheckRoot is called before resolving the parent of the Dentry d. If the 280 // Dentry is contextually a VFS root, such that path resolution should treat 281 // d's parent as itself, CheckRoot returns (true, nil). If the Dentry is the 282 // root of a non-root mount, such that path resolution should switch to another 283 // Mount, CheckRoot returns (unspecified, non-nil error). Otherwise, path 284 // resolution should resolve d's parent normally, and CheckRoot returns (false, 285 // nil). 286 func (rp *ResolvingPath) CheckRoot(ctx context.Context, d *Dentry) (bool, error) { 287 if d == rp.root.dentry && rp.mount == rp.root.mount { 288 // At contextual VFS root (due to e.g. chroot(2)). 289 return true, nil 290 } else if d == rp.mount.root { 291 // At mount root ... 292 vd := rp.vfs.getMountpointAt(ctx, rp.mount, rp.root) 293 if vd.Ok() { 294 // ... of non-root mount. 295 rp.nextMount = vd.mount 296 rp.nextStart = vd.dentry 297 return false, resolveMountRootOrJumpError{} 298 } 299 // ... of root mount. 300 return true, nil 301 } 302 return false, nil 303 } 304 305 // CheckMount is called after resolving the parent or child of another Dentry 306 // to d. If d is a mount point, such that path resolution should switch to 307 // another Mount, CheckMount returns a non-nil error. Otherwise, CheckMount 308 // returns nil. 309 func (rp *ResolvingPath) CheckMount(ctx context.Context, d *Dentry) error { 310 if !d.isMounted() { 311 return nil 312 } 313 if mnt := rp.vfs.getMountAt(ctx, rp.mount, d); mnt != nil { 314 rp.nextMount = mnt 315 return resolveMountPointError{} 316 } 317 return nil 318 } 319 320 // ShouldFollowSymlink returns true if, supposing that the current path 321 // component in pcs represents a symbolic link, the symbolic link should be 322 // followed. 323 // 324 // If path is terminated with '/', the '/' is considered the last element and 325 // any symlink before that is followed: 326 // 327 // - For most non-creating walks, the last path component is handled by 328 // fs/namei.c:lookup_last(), which sets LOOKUP_FOLLOW if the first byte 329 // after the path component is non-NULL (which is only possible if it's '/') 330 // and the path component is of type LAST_NORM. 331 // 332 // - For open/openat/openat2 without O_CREAT, the last path component is 333 // handled by fs/namei.c:do_last(), which does the same, though without the 334 // LAST_NORM check. 335 // 336 // Preconditions: !rp.Done(). 337 func (rp *ResolvingPath) ShouldFollowSymlink() bool { 338 // Non-final symlinks are always followed. Paths terminated with '/' are also 339 // always followed. 340 return rp.flags&rpflagsFollowFinalSymlink != 0 || !rp.Final() || rp.MustBeDir() 341 } 342 343 // HandleSymlink is called when the current path component is a symbolic link 344 // to the given target. If the calling Filesystem method should continue path 345 // traversal, HandleSymlink updates the path component stream to reflect the 346 // symlink target and returns nil. Otherwise it returns a non-nil error. It 347 // also returns whether the symlink was successfully followed, which can be 348 // true even when a non-nil error like resolveAbsSymlinkError is returned. 349 // 350 // Preconditions: !rp.Done(). 351 // 352 // Postconditions: If HandleSymlink returns a nil error, then !rp.Done(). 353 func (rp *ResolvingPath) HandleSymlink(target string) (bool, error) { 354 if rp.symlinks >= linux.MaxSymlinkTraversals { 355 return false, linuxerr.ELOOP 356 } 357 if len(target) == 0 { 358 return false, linuxerr.ENOENT 359 } 360 rp.symlinks++ 361 targetPath := fspath.Parse(target) 362 if targetPath.Absolute { 363 rp.absSymlinkTarget = targetPath 364 return true, resolveAbsSymlinkError{} 365 } 366 // Consume the path component that represented the symlink. 367 rp.Advance() 368 // Prepend the symlink target to the relative path. 369 if checkInvariants { 370 if !targetPath.HasComponents() { 371 panic(fmt.Sprintf("non-empty pathname %q parsed to relative path with no components", target)) 372 } 373 } 374 rp.relpathPrepend(targetPath) 375 return true, nil 376 } 377 378 // Preconditions: path.HasComponents(). 379 func (rp *ResolvingPath) relpathPrepend(path fspath.Path) { 380 if rp.pit.Ok() { 381 rp.parts[rp.curPart] = rp.pit 382 rp.pit = path.Begin 383 rp.curPart++ 384 } else { 385 // The symlink was the final path component, so now the symlink target 386 // is the whole path. 387 rp.pit = path.Begin 388 // Symlink targets can set rp.mustBeDir (if they end in a trailing /), 389 // but can't unset it. 390 if path.Dir { 391 rp.mustBeDir = true 392 } 393 } 394 } 395 396 // HandleJump is called when the current path component is a "magic" link to 397 // the given VirtualDentry, like /proc/[pid]/fd/[fd]. If the calling Filesystem 398 // method should continue path traversal, HandleJump updates the path 399 // component stream to reflect the magic link target and returns nil. Otherwise 400 // it returns a non-nil error. It also returns whether the magic link was 401 // followed, which can be true even when a non-nil error like 402 // resolveMountRootOrJumpError is returned. 403 // 404 // Preconditions: !rp.Done(). 405 func (rp *ResolvingPath) HandleJump(target VirtualDentry) (bool, error) { 406 if rp.symlinks >= linux.MaxSymlinkTraversals { 407 return false, linuxerr.ELOOP 408 } 409 rp.symlinks++ 410 // Consume the path component that represented the magic link. 411 rp.Advance() 412 // Unconditionally return a resolveMountRootOrJumpError, even if the Mount 413 // isn't changing, to force restarting at the new Dentry. 414 target.IncRef() 415 rp.nextMount = target.mount 416 rp.nextStart = target.dentry 417 return true, resolveMountRootOrJumpError{} 418 } 419 420 func (rp *ResolvingPath) handleError(ctx context.Context, err error) bool { 421 switch err.(type) { 422 case resolveMountRootOrJumpError: 423 // Switch to the new Mount. We hold references on the Mount and Dentry. 424 rp.decRefStartAndMount(ctx) 425 rp.mount = rp.nextMount 426 rp.start = rp.nextStart 427 rp.flags |= rpflagsHaveMountRef | rpflagsHaveStartRef 428 rp.nextMount = nil 429 rp.nextStart = nil 430 // Don't consume the path component that caused us to traverse 431 // through the mount root - i.e. the ".." - because we still need to 432 // resolve the mount point's parent in the new FilesystemImpl. 433 // 434 // Restart path resolution on the new Mount. Don't bother calling 435 // rp.releaseErrorState() since we already set nextMount and nextStart 436 // to nil above. 437 return true 438 439 case resolveMountPointError: 440 // Switch to the new Mount. We hold a reference on the Mount, but 441 // borrow the reference on the mount root from the Mount. 442 rp.decRefStartAndMount(ctx) 443 rp.mount = rp.nextMount 444 rp.start = rp.nextMount.root 445 rp.flags = rp.flags&^rpflagsHaveStartRef | rpflagsHaveMountRef 446 rp.nextMount = nil 447 // Consume the path component that represented the mount point. 448 rp.Advance() 449 // Restart path resolution on the new Mount. 450 rp.releaseErrorState(ctx) 451 return true 452 453 case resolveAbsSymlinkError: 454 // Switch to the new Mount. References are borrowed from rp.root. 455 rp.decRefStartAndMount(ctx) 456 rp.mount = rp.root.mount 457 rp.start = rp.root.dentry 458 rp.flags &^= rpflagsHaveMountRef | rpflagsHaveStartRef 459 // Consume the path component that represented the symlink. 460 rp.Advance() 461 if rp.absSymlinkTarget.HasComponents() { 462 // Prepend the symlink target to the relative path. 463 rp.relpathPrepend(rp.absSymlinkTarget) 464 } 465 // Restart path resolution on the new Mount. 466 rp.releaseErrorState(ctx) 467 return true 468 469 default: 470 // Not an error we can handle. 471 return false 472 } 473 } 474 475 // canHandleError returns true if err is an error returned by rp.Resolve*() 476 // that rp.handleError() may attempt to handle. 477 func (rp *ResolvingPath) canHandleError(err error) bool { 478 switch err.(type) { 479 case resolveMountRootOrJumpError, resolveMountPointError, resolveAbsSymlinkError: 480 return true 481 default: 482 return false 483 } 484 } 485 486 // MustBeDir returns true if the file traversed by rp must be a directory. 487 func (rp *ResolvingPath) MustBeDir() bool { 488 return rp.mustBeDir 489 }