github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/kernfs/inode_impl_util.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 kernfs 16 17 import ( 18 "fmt" 19 "sync/atomic" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 24 "github.com/SagerNet/gvisor/pkg/hostarch" 25 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 26 ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time" 27 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 28 "github.com/SagerNet/gvisor/pkg/sync" 29 "github.com/SagerNet/gvisor/pkg/syserror" 30 ) 31 32 // InodeNoopRefCount partially implements the Inode interface, specifically the 33 // inodeRefs sub interface. InodeNoopRefCount implements a simple reference 34 // count for inodes, performing no extra actions when references are obtained or 35 // released. This is suitable for simple file inodes that don't reference any 36 // resources. 37 // 38 // +stateify savable 39 type InodeNoopRefCount struct { 40 InodeTemporary 41 } 42 43 // IncRef implements Inode.IncRef. 44 func (InodeNoopRefCount) IncRef() { 45 } 46 47 // DecRef implements Inode.DecRef. 48 func (InodeNoopRefCount) DecRef(context.Context) { 49 } 50 51 // TryIncRef implements Inode.TryIncRef. 52 func (InodeNoopRefCount) TryIncRef() bool { 53 return true 54 } 55 56 // InodeDirectoryNoNewChildren partially implements the Inode interface. 57 // InodeDirectoryNoNewChildren represents a directory inode which does not 58 // support creation of new children. 59 // 60 // +stateify savable 61 type InodeDirectoryNoNewChildren struct{} 62 63 // NewFile implements Inode.NewFile. 64 func (InodeDirectoryNoNewChildren) NewFile(context.Context, string, vfs.OpenOptions) (Inode, error) { 65 return nil, linuxerr.EPERM 66 } 67 68 // NewDir implements Inode.NewDir. 69 func (InodeDirectoryNoNewChildren) NewDir(context.Context, string, vfs.MkdirOptions) (Inode, error) { 70 return nil, linuxerr.EPERM 71 } 72 73 // NewLink implements Inode.NewLink. 74 func (InodeDirectoryNoNewChildren) NewLink(context.Context, string, Inode) (Inode, error) { 75 return nil, linuxerr.EPERM 76 } 77 78 // NewSymlink implements Inode.NewSymlink. 79 func (InodeDirectoryNoNewChildren) NewSymlink(context.Context, string, string) (Inode, error) { 80 return nil, linuxerr.EPERM 81 } 82 83 // NewNode implements Inode.NewNode. 84 func (InodeDirectoryNoNewChildren) NewNode(context.Context, string, vfs.MknodOptions) (Inode, error) { 85 return nil, linuxerr.EPERM 86 } 87 88 // InodeNotDirectory partially implements the Inode interface, specifically the 89 // inodeDirectory and inodeDynamicDirectory sub interfaces. Inodes that do not 90 // represent directories can embed this to provide no-op implementations for 91 // directory-related functions. 92 // 93 // +stateify savable 94 type InodeNotDirectory struct { 95 InodeAlwaysValid 96 } 97 98 // HasChildren implements Inode.HasChildren. 99 func (InodeNotDirectory) HasChildren() bool { 100 return false 101 } 102 103 // NewFile implements Inode.NewFile. 104 func (InodeNotDirectory) NewFile(context.Context, string, vfs.OpenOptions) (Inode, error) { 105 panic("NewFile called on non-directory inode") 106 } 107 108 // NewDir implements Inode.NewDir. 109 func (InodeNotDirectory) NewDir(context.Context, string, vfs.MkdirOptions) (Inode, error) { 110 panic("NewDir called on non-directory inode") 111 } 112 113 // NewLink implements Inode.NewLinkink. 114 func (InodeNotDirectory) NewLink(context.Context, string, Inode) (Inode, error) { 115 panic("NewLink called on non-directory inode") 116 } 117 118 // NewSymlink implements Inode.NewSymlink. 119 func (InodeNotDirectory) NewSymlink(context.Context, string, string) (Inode, error) { 120 panic("NewSymlink called on non-directory inode") 121 } 122 123 // NewNode implements Inode.NewNode. 124 func (InodeNotDirectory) NewNode(context.Context, string, vfs.MknodOptions) (Inode, error) { 125 panic("NewNode called on non-directory inode") 126 } 127 128 // Unlink implements Inode.Unlink. 129 func (InodeNotDirectory) Unlink(context.Context, string, Inode) error { 130 panic("Unlink called on non-directory inode") 131 } 132 133 // RmDir implements Inode.RmDir. 134 func (InodeNotDirectory) RmDir(context.Context, string, Inode) error { 135 panic("RmDir called on non-directory inode") 136 } 137 138 // Rename implements Inode.Rename. 139 func (InodeNotDirectory) Rename(context.Context, string, string, Inode, Inode) error { 140 panic("Rename called on non-directory inode") 141 } 142 143 // Lookup implements Inode.Lookup. 144 func (InodeNotDirectory) Lookup(ctx context.Context, name string) (Inode, error) { 145 panic("Lookup called on non-directory inode") 146 } 147 148 // IterDirents implements Inode.IterDirents. 149 func (InodeNotDirectory) IterDirents(ctx context.Context, mnt *vfs.Mount, callback vfs.IterDirentsCallback, offset, relOffset int64) (newOffset int64, err error) { 150 panic("IterDirents called on non-directory inode") 151 } 152 153 // InodeNotSymlink partially implements the Inode interface, specifically the 154 // inodeSymlink sub interface. All inodes that are not symlinks may embed this 155 // to return the appropriate errors from symlink-related functions. 156 // 157 // +stateify savable 158 type InodeNotSymlink struct{} 159 160 // Readlink implements Inode.Readlink. 161 func (InodeNotSymlink) Readlink(context.Context, *vfs.Mount) (string, error) { 162 return "", linuxerr.EINVAL 163 } 164 165 // Getlink implements Inode.Getlink. 166 func (InodeNotSymlink) Getlink(context.Context, *vfs.Mount) (vfs.VirtualDentry, string, error) { 167 return vfs.VirtualDentry{}, "", linuxerr.EINVAL 168 } 169 170 // InodeAttrs partially implements the Inode interface, specifically the 171 // inodeMetadata sub interface. InodeAttrs provides functionality related to 172 // inode attributes. 173 // 174 // Must be initialized by Init prior to first use. 175 // 176 // +stateify savable 177 type InodeAttrs struct { 178 devMajor uint32 179 devMinor uint32 180 ino uint64 181 mode uint32 182 uid uint32 183 gid uint32 184 nlink uint32 185 blockSize uint32 186 187 // Timestamps, all nsecs from the Unix epoch. 188 atime int64 189 mtime int64 190 ctime int64 191 } 192 193 // Init initializes this InodeAttrs. 194 func (a *InodeAttrs) Init(ctx context.Context, creds *auth.Credentials, devMajor, devMinor uint32, ino uint64, mode linux.FileMode) { 195 if mode.FileType() == 0 { 196 panic(fmt.Sprintf("No file type specified in 'mode' for InodeAttrs.Init(): mode=0%o", mode)) 197 } 198 199 nlink := uint32(1) 200 if mode.FileType() == linux.ModeDirectory { 201 nlink = 2 202 } 203 a.devMajor = devMajor 204 a.devMinor = devMinor 205 atomic.StoreUint64(&a.ino, ino) 206 atomic.StoreUint32(&a.mode, uint32(mode)) 207 atomic.StoreUint32(&a.uid, uint32(creds.EffectiveKUID)) 208 atomic.StoreUint32(&a.gid, uint32(creds.EffectiveKGID)) 209 atomic.StoreUint32(&a.nlink, nlink) 210 atomic.StoreUint32(&a.blockSize, hostarch.PageSize) 211 now := ktime.NowFromContext(ctx).Nanoseconds() 212 atomic.StoreInt64(&a.atime, now) 213 atomic.StoreInt64(&a.mtime, now) 214 atomic.StoreInt64(&a.ctime, now) 215 } 216 217 // DevMajor returns the device major number. 218 func (a *InodeAttrs) DevMajor() uint32 { 219 return a.devMajor 220 } 221 222 // DevMinor returns the device minor number. 223 func (a *InodeAttrs) DevMinor() uint32 { 224 return a.devMinor 225 } 226 227 // Ino returns the inode id. 228 func (a *InodeAttrs) Ino() uint64 { 229 return atomic.LoadUint64(&a.ino) 230 } 231 232 // Mode implements Inode.Mode. 233 func (a *InodeAttrs) Mode() linux.FileMode { 234 return linux.FileMode(atomic.LoadUint32(&a.mode)) 235 } 236 237 // TouchAtime updates a.atime to the current time. 238 func (a *InodeAttrs) TouchAtime(ctx context.Context, mnt *vfs.Mount) { 239 if mnt.Flags.NoATime || mnt.ReadOnly() { 240 return 241 } 242 if err := mnt.CheckBeginWrite(); err != nil { 243 return 244 } 245 atomic.StoreInt64(&a.atime, ktime.NowFromContext(ctx).Nanoseconds()) 246 mnt.EndWrite() 247 } 248 249 // TouchCMtime updates a.{c/m}time to the current time. The caller should 250 // synchronize calls to this so that ctime and mtime are updated to the same 251 // value. 252 func (a *InodeAttrs) TouchCMtime(ctx context.Context) { 253 now := ktime.NowFromContext(ctx).Nanoseconds() 254 atomic.StoreInt64(&a.mtime, now) 255 atomic.StoreInt64(&a.ctime, now) 256 } 257 258 // Stat partially implements Inode.Stat. Note that this function doesn't provide 259 // all the stat fields, and the embedder should consider extending the result 260 // with filesystem-specific fields. 261 func (a *InodeAttrs) Stat(context.Context, *vfs.Filesystem, vfs.StatOptions) (linux.Statx, error) { 262 var stat linux.Statx 263 stat.Mask = linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID | linux.STATX_INO | linux.STATX_NLINK | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME 264 stat.DevMajor = a.devMajor 265 stat.DevMinor = a.devMinor 266 stat.Ino = atomic.LoadUint64(&a.ino) 267 stat.Mode = uint16(a.Mode()) 268 stat.UID = atomic.LoadUint32(&a.uid) 269 stat.GID = atomic.LoadUint32(&a.gid) 270 stat.Nlink = atomic.LoadUint32(&a.nlink) 271 stat.Blksize = atomic.LoadUint32(&a.blockSize) 272 stat.Atime = linux.NsecToStatxTimestamp(atomic.LoadInt64(&a.atime)) 273 stat.Mtime = linux.NsecToStatxTimestamp(atomic.LoadInt64(&a.mtime)) 274 stat.Ctime = linux.NsecToStatxTimestamp(atomic.LoadInt64(&a.ctime)) 275 return stat, nil 276 } 277 278 // SetStat implements Inode.SetStat. 279 func (a *InodeAttrs) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error { 280 if opts.Stat.Mask == 0 { 281 return nil 282 } 283 284 // Note that not all fields are modifiable. For example, the file type and 285 // inode numbers are immutable after node creation. Setting the size is often 286 // allowed by kernfs files but does not do anything. If some other behavior is 287 // needed, the embedder should consider extending SetStat. 288 if opts.Stat.Mask&^(linux.STATX_MODE|linux.STATX_UID|linux.STATX_GID|linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_SIZE) != 0 { 289 return linuxerr.EPERM 290 } 291 if opts.Stat.Mask&linux.STATX_SIZE != 0 && a.Mode().IsDir() { 292 return syserror.EISDIR 293 } 294 if err := vfs.CheckSetStat(ctx, creds, &opts, a.Mode(), auth.KUID(atomic.LoadUint32(&a.uid)), auth.KGID(atomic.LoadUint32(&a.gid))); err != nil { 295 return err 296 } 297 298 clearSID := false 299 stat := opts.Stat 300 if stat.Mask&linux.STATX_UID != 0 { 301 atomic.StoreUint32(&a.uid, stat.UID) 302 clearSID = true 303 } 304 if stat.Mask&linux.STATX_GID != 0 { 305 atomic.StoreUint32(&a.gid, stat.GID) 306 clearSID = true 307 } 308 if stat.Mask&linux.STATX_MODE != 0 { 309 for { 310 old := atomic.LoadUint32(&a.mode) 311 ft := old & linux.S_IFMT 312 newMode := ft | uint32(stat.Mode & ^uint16(linux.S_IFMT)) 313 if clearSID { 314 newMode = vfs.ClearSUIDAndSGID(newMode) 315 } 316 if swapped := atomic.CompareAndSwapUint32(&a.mode, old, newMode); swapped { 317 clearSID = false 318 break 319 } 320 } 321 } 322 323 // We may have to clear the SUID/SGID bits, but didn't do so as part of 324 // STATX_MODE. 325 if clearSID { 326 for { 327 old := atomic.LoadUint32(&a.mode) 328 newMode := vfs.ClearSUIDAndSGID(old) 329 if swapped := atomic.CompareAndSwapUint32(&a.mode, old, newMode); swapped { 330 break 331 } 332 } 333 } 334 335 now := ktime.NowFromContext(ctx).Nanoseconds() 336 if stat.Mask&linux.STATX_ATIME != 0 { 337 if stat.Atime.Nsec == linux.UTIME_NOW { 338 stat.Atime = linux.NsecToStatxTimestamp(now) 339 } 340 atomic.StoreInt64(&a.atime, stat.Atime.ToNsec()) 341 } 342 if stat.Mask&linux.STATX_MTIME != 0 { 343 if stat.Mtime.Nsec == linux.UTIME_NOW { 344 stat.Mtime = linux.NsecToStatxTimestamp(now) 345 } 346 atomic.StoreInt64(&a.mtime, stat.Mtime.ToNsec()) 347 } 348 349 return nil 350 } 351 352 // CheckPermissions implements Inode.CheckPermissions. 353 func (a *InodeAttrs) CheckPermissions(_ context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error { 354 return vfs.GenericCheckPermissions( 355 creds, 356 ats, 357 a.Mode(), 358 auth.KUID(atomic.LoadUint32(&a.uid)), 359 auth.KGID(atomic.LoadUint32(&a.gid)), 360 ) 361 } 362 363 // IncLinks implements Inode.IncLinks. 364 func (a *InodeAttrs) IncLinks(n uint32) { 365 if atomic.AddUint32(&a.nlink, n) <= n { 366 panic("InodeLink.IncLinks called with no existing links") 367 } 368 } 369 370 // DecLinks implements Inode.DecLinks. 371 func (a *InodeAttrs) DecLinks() { 372 if nlink := atomic.AddUint32(&a.nlink, ^uint32(0)); nlink == ^uint32(0) { 373 // Negative overflow 374 panic("Inode.DecLinks called at 0 links") 375 } 376 } 377 378 // +stateify savable 379 type slot struct { 380 name string 381 inode Inode 382 static bool 383 slotEntry 384 } 385 386 // OrderedChildrenOptions contains initialization options for OrderedChildren. 387 // 388 // +stateify savable 389 type OrderedChildrenOptions struct { 390 // Writable indicates whether vfs.FilesystemImpl methods implemented by 391 // OrderedChildren may modify the tracked children. This applies to 392 // operations related to rename, unlink and rmdir. If an OrderedChildren is 393 // not writable, these operations all fail with EPERM. 394 // 395 // Note that writable users must implement the sticky bit (I_SVTX). 396 Writable bool 397 } 398 399 // OrderedChildren partially implements the Inode interface. OrderedChildren can 400 // be embedded in directory inodes to keep track of children in the 401 // directory, and can then be used to implement a generic directory FD -- see 402 // GenericDirectoryFD. 403 // 404 // OrderedChildren can represent a node in an Inode tree. The children inodes 405 // might be directories themselves using OrderedChildren; hence extending the 406 // tree. The parent inode (OrderedChildren user) holds a ref on all its static 407 // children. This lets the static inodes outlive their associated dentry. 408 // While the dentry might have to be regenerated via a Lookup() call, we can 409 // keep reusing the same static inode. These static children inodes are finally 410 // DecRef'd when this directory inode is being destroyed. This makes 411 // OrderedChildren suitable for static directory entries as well. 412 // 413 // Must be initialize with Init before first use. 414 // 415 // +stateify savable 416 type OrderedChildren struct { 417 // Can children be modified by user syscalls? It set to false, interface 418 // methods that would modify the children return EPERM. Immutable. 419 writable bool 420 421 mu sync.RWMutex `state:"nosave"` 422 order slotList 423 set map[string]*slot 424 } 425 426 // Init initializes an OrderedChildren. 427 func (o *OrderedChildren) Init(opts OrderedChildrenOptions) { 428 o.writable = opts.Writable 429 o.set = make(map[string]*slot) 430 } 431 432 // Destroy clears the children stored in o. It should be called by structs 433 // embedding OrderedChildren upon destruction, i.e. when their reference count 434 // reaches zero. 435 func (o *OrderedChildren) Destroy(ctx context.Context) { 436 o.mu.Lock() 437 defer o.mu.Unlock() 438 // Drop the ref that o owns on the static inodes it holds. 439 for _, s := range o.set { 440 if s.static { 441 s.inode.DecRef(ctx) 442 } 443 } 444 o.order.Reset() 445 o.set = nil 446 } 447 448 // Populate inserts static children into this OrderedChildren. 449 // Populate returns the number of directories inserted, which the caller 450 // may use to update the link count for the parent directory. 451 // 452 // Precondition: 453 // * d must represent a directory inode. 454 // * children must not contain any conflicting entries already in o. 455 // * Caller must hold a reference on all inodes passed. 456 // 457 // Postcondition: Caller's references on inodes are transferred to o. 458 func (o *OrderedChildren) Populate(children map[string]Inode) uint32 { 459 var links uint32 460 for name, child := range children { 461 if child.Mode().IsDir() { 462 links++ 463 } 464 if err := o.insert(name, child, true); err != nil { 465 panic(fmt.Sprintf("Collision when attempting to insert child %q (%+v)", name, child)) 466 } 467 } 468 return links 469 } 470 471 // Lookup implements Inode.Lookup. 472 func (o *OrderedChildren) Lookup(ctx context.Context, name string) (Inode, error) { 473 o.mu.RLock() 474 defer o.mu.RUnlock() 475 476 s, ok := o.set[name] 477 if !ok { 478 return nil, syserror.ENOENT 479 } 480 481 s.inode.IncRef() // This ref is passed to the dentry upon creation via Init. 482 return s.inode, nil 483 } 484 485 // IterDirents implements Inode.IterDirents. 486 func (o *OrderedChildren) IterDirents(ctx context.Context, mnt *vfs.Mount, cb vfs.IterDirentsCallback, offset, relOffset int64) (newOffset int64, err error) { 487 // All entries from OrderedChildren have already been handled in 488 // GenericDirectoryFD.IterDirents. 489 return offset, nil 490 } 491 492 // HasChildren implements Inode.HasChildren. 493 func (o *OrderedChildren) HasChildren() bool { 494 o.mu.RLock() 495 defer o.mu.RUnlock() 496 return len(o.set) > 0 497 } 498 499 // Insert inserts a dynamic child into o. This ignores the writability of o, as 500 // this is not part of the vfs.FilesystemImpl interface, and is a lower-level operation. 501 func (o *OrderedChildren) Insert(name string, child Inode) error { 502 return o.insert(name, child, false) 503 } 504 505 // insert inserts child into o. 506 // 507 // Precondition: Caller must be holding a ref on child if static is true. 508 // 509 // Postcondition: Caller's ref on child is transferred to o if static is true. 510 func (o *OrderedChildren) insert(name string, child Inode, static bool) error { 511 o.mu.Lock() 512 defer o.mu.Unlock() 513 if _, ok := o.set[name]; ok { 514 return syserror.EEXIST 515 } 516 s := &slot{ 517 name: name, 518 inode: child, 519 static: static, 520 } 521 o.order.PushBack(s) 522 o.set[name] = s 523 return nil 524 } 525 526 // Precondition: caller must hold o.mu for writing. 527 func (o *OrderedChildren) removeLocked(name string) { 528 if s, ok := o.set[name]; ok { 529 if s.static { 530 panic(fmt.Sprintf("removeLocked called on a static inode: %v", s.inode)) 531 } 532 delete(o.set, name) 533 o.order.Remove(s) 534 } 535 } 536 537 // Precondition: caller must hold o.mu for writing. 538 func (o *OrderedChildren) replaceChildLocked(ctx context.Context, name string, newI Inode) { 539 if s, ok := o.set[name]; ok { 540 if s.static { 541 panic(fmt.Sprintf("replacing a static inode: %v", s.inode)) 542 } 543 544 // Existing slot with given name, simply replace the dentry. 545 s.inode = newI 546 } 547 548 // No existing slot with given name, create and hash new slot. 549 s := &slot{ 550 name: name, 551 inode: newI, 552 static: false, 553 } 554 o.order.PushBack(s) 555 o.set[name] = s 556 } 557 558 // Precondition: caller must hold o.mu for reading or writing. 559 func (o *OrderedChildren) checkExistingLocked(name string, child Inode) error { 560 s, ok := o.set[name] 561 if !ok { 562 return syserror.ENOENT 563 } 564 if s.inode != child { 565 panic(fmt.Sprintf("Inode doesn't match what kernfs thinks! OrderedChild: %+v, kernfs: %+v", s.inode, child)) 566 } 567 return nil 568 } 569 570 // Unlink implements Inode.Unlink. 571 func (o *OrderedChildren) Unlink(ctx context.Context, name string, child Inode) error { 572 if !o.writable { 573 return linuxerr.EPERM 574 } 575 o.mu.Lock() 576 defer o.mu.Unlock() 577 if err := o.checkExistingLocked(name, child); err != nil { 578 return err 579 } 580 581 o.removeLocked(name) 582 return nil 583 } 584 585 // RmDir implements Inode.RmDir. 586 func (o *OrderedChildren) RmDir(ctx context.Context, name string, child Inode) error { 587 // We're not responsible for checking that child is a directory, that it's 588 // empty, or updating any link counts; so this is the same as unlink. 589 return o.Unlink(ctx, name, child) 590 } 591 592 // Rename implements Inode.Rename. 593 // 594 // Precondition: Rename may only be called across two directory inodes with 595 // identical implementations of Rename. Practically, this means filesystems that 596 // implement Rename by embedding OrderedChildren for any directory 597 // implementation must use OrderedChildren for all directory implementations 598 // that will support Rename. 599 // 600 // Postcondition: reference on any replaced dentry transferred to caller. 601 func (o *OrderedChildren) Rename(ctx context.Context, oldname, newname string, child, dstDir Inode) error { 602 if !o.writable { 603 return linuxerr.EPERM 604 } 605 606 dst, ok := dstDir.(interface{}).(*OrderedChildren) 607 if !ok { 608 return linuxerr.EXDEV 609 } 610 if !dst.writable { 611 return linuxerr.EPERM 612 } 613 614 // Note: There's a potential deadlock below if concurrent calls to Rename 615 // refer to the same src and dst directories in reverse. We avoid any 616 // ordering issues because the caller is required to serialize concurrent 617 // calls to Rename in accordance with the interface declaration. 618 o.mu.Lock() 619 defer o.mu.Unlock() 620 if dst != o { 621 dst.mu.Lock() 622 defer dst.mu.Unlock() 623 } 624 if err := o.checkExistingLocked(oldname, child); err != nil { 625 return err 626 } 627 o.removeLocked(oldname) 628 629 dst.replaceChildLocked(ctx, newname, child) 630 return nil 631 } 632 633 // nthLocked returns an iterator to the nth child tracked by this object. The 634 // iterator is valid until the caller releases o.mu. Returns nil if the 635 // requested index falls out of bounds. 636 // 637 // Preconditon: Caller must hold o.mu for reading. 638 func (o *OrderedChildren) nthLocked(i int64) *slot { 639 for it := o.order.Front(); it != nil && i >= 0; it = it.Next() { 640 if i == 0 { 641 return it 642 } 643 i-- 644 } 645 return nil 646 } 647 648 // InodeSymlink partially implements Inode interface for symlinks. 649 // 650 // +stateify savable 651 type InodeSymlink struct { 652 InodeNotDirectory 653 } 654 655 // Open implements Inode.Open. 656 func (InodeSymlink) Open(ctx context.Context, rp *vfs.ResolvingPath, d *Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 657 return nil, linuxerr.ELOOP 658 } 659 660 // StaticDirectory is a standard implementation of a directory with static 661 // contents. 662 // 663 // +stateify savable 664 type StaticDirectory struct { 665 InodeAlwaysValid 666 InodeAttrs 667 InodeDirectoryNoNewChildren 668 InodeNoStatFS 669 InodeNotSymlink 670 InodeTemporary 671 OrderedChildren 672 StaticDirectoryRefs 673 674 locks vfs.FileLocks 675 fdOpts GenericDirectoryFDOptions 676 } 677 678 var _ Inode = (*StaticDirectory)(nil) 679 680 // NewStaticDir creates a new static directory and returns its dentry. 681 func NewStaticDir(ctx context.Context, creds *auth.Credentials, devMajor, devMinor uint32, ino uint64, perm linux.FileMode, children map[string]Inode, fdOpts GenericDirectoryFDOptions) Inode { 682 inode := &StaticDirectory{} 683 inode.Init(ctx, creds, devMajor, devMinor, ino, perm, fdOpts) 684 inode.InitRefs() 685 686 inode.OrderedChildren.Init(OrderedChildrenOptions{}) 687 links := inode.OrderedChildren.Populate(children) 688 inode.IncLinks(links) 689 690 return inode 691 } 692 693 // Init initializes StaticDirectory. 694 func (s *StaticDirectory) Init(ctx context.Context, creds *auth.Credentials, devMajor, devMinor uint32, ino uint64, perm linux.FileMode, fdOpts GenericDirectoryFDOptions) { 695 if perm&^linux.PermissionsMask != 0 { 696 panic(fmt.Sprintf("Only permission mask must be set: %x", perm&linux.PermissionsMask)) 697 } 698 s.fdOpts = fdOpts 699 s.InodeAttrs.Init(ctx, creds, devMajor, devMinor, ino, linux.ModeDirectory|perm) 700 } 701 702 // Open implements Inode.Open. 703 func (s *StaticDirectory) Open(ctx context.Context, rp *vfs.ResolvingPath, d *Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 704 fd, err := NewGenericDirectoryFD(rp.Mount(), d, &s.OrderedChildren, &s.locks, &opts, s.fdOpts) 705 if err != nil { 706 return nil, err 707 } 708 return fd.VFSFileDescription(), nil 709 } 710 711 // SetStat implements Inode.SetStat not allowing inode attributes to be changed. 712 func (*StaticDirectory) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error { 713 return linuxerr.EPERM 714 } 715 716 // DecRef implements Inode.DecRef. 717 func (s *StaticDirectory) DecRef(ctx context.Context) { 718 s.StaticDirectoryRefs.DecRef(func() { s.Destroy(ctx) }) 719 } 720 721 // InodeAlwaysValid partially implements Inode. 722 // 723 // +stateify savable 724 type InodeAlwaysValid struct{} 725 726 // Valid implements Inode.Valid. 727 func (*InodeAlwaysValid) Valid(context.Context) bool { 728 return true 729 } 730 731 // InodeTemporary partially implements Inode. 732 // 733 // +stateify savable 734 type InodeTemporary struct{} 735 736 // Keep implements Inode.Keep. 737 func (*InodeTemporary) Keep() bool { 738 return false 739 } 740 741 // InodeNoStatFS partially implements the Inode interface, where the client 742 // filesystem doesn't support statfs(2). 743 // 744 // +stateify savable 745 type InodeNoStatFS struct{} 746 747 // StatFS implements Inode.StatFS. 748 func (*InodeNoStatFS) StatFS(context.Context, *vfs.Filesystem) (linux.Statfs, error) { 749 return linux.Statfs{}, syserror.ENOSYS 750 }