gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/erofs/directory.go (about) 1 // Copyright 2023 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 erofs 16 17 import ( 18 "sort" 19 "sync" 20 21 "gvisor.dev/gvisor/pkg/abi/linux" 22 "gvisor.dev/gvisor/pkg/context" 23 "gvisor.dev/gvisor/pkg/errors/linuxerr" 24 "gvisor.dev/gvisor/pkg/sentry/vfs" 25 ) 26 27 func (i *inode) getDirents() ([]vfs.Dirent, error) { 28 // Fast path. 29 i.dirMu.RLock() 30 dirents := i.dirents 31 i.dirMu.RUnlock() 32 if dirents != nil { 33 return dirents, nil 34 } 35 36 // Slow path. 37 i.dirMu.Lock() 38 defer i.dirMu.Unlock() 39 40 off := int64(1) 41 if err := i.IterDirents(func(name string, typ uint8, nid uint64) error { 42 dirents = append(dirents, vfs.Dirent{ 43 Name: name, 44 Type: linux.FileTypeToDirentType(typ), 45 Ino: nid, 46 NextOff: off, 47 }) 48 off++ 49 return nil 50 }); err != nil { 51 return nil, err 52 } 53 54 // "." and ".." should always be present. 55 if len(dirents) < 2 { 56 return nil, linuxerr.EUCLEAN 57 } 58 59 i.dirents = dirents 60 return dirents, nil 61 } 62 63 func (i *inode) lookup(name string) (uint64, error) { 64 var dirents []vfs.Dirent 65 66 // Lazily fetch dirents. 67 if i.dirMu.TryRLock() { 68 dirents = i.dirents // +checklocksforce: TryRLock. 69 i.dirMu.RUnlock() // +checklocksforce: TryRLock. 70 } 71 72 if dirents == nil { 73 // The dirents cache is not available immediately, let's do 74 // binary search on disk data directly. 75 return i.Lookup(name) 76 } 77 78 // The dirents are sorted in alphabetical order. We do binary search 79 // to find the target. 80 idx := sort.Search(len(dirents), func(i int) bool { 81 return dirents[i].Name >= name 82 }) 83 if idx >= len(dirents) || dirents[idx].Name != name { 84 return 0, linuxerr.ENOENT 85 } 86 return dirents[idx].Ino, nil 87 } 88 89 func (d *dentry) lookup(ctx context.Context, name string) (*dentry, error) { 90 // Fast path, dentry already exists. 91 d.dirMu.RLock() 92 child, ok := d.childMap[name] 93 d.dirMu.RUnlock() 94 if ok { 95 return child, nil 96 } 97 98 // Slow path, create a new dentry. 99 d.dirMu.Lock() 100 defer d.dirMu.Unlock() 101 if child, ok := d.childMap[name]; ok { 102 return child, nil 103 } 104 105 nid, err := d.inode.lookup(name) 106 if err != nil { 107 return nil, err 108 } 109 110 if d.childMap == nil { 111 d.childMap = make(map[string]*dentry) 112 } 113 114 child, err = d.inode.fs.newDentry(nid) 115 if err != nil { 116 return nil, err 117 } 118 child.parent.Store(d) 119 child.name = name 120 d.childMap[name] = child 121 return child, nil 122 } 123 124 // +stateify savable 125 type directoryFD struct { 126 fileDescription 127 vfs.DirectoryFileDescriptionDefaultImpl 128 129 // mu protects off. 130 mu sync.Mutex `state:"nosave"` 131 // +checklocks:mu 132 off int64 133 } 134 135 // IterDirents implements vfs.FileDescriptionImpl.IterDirents. 136 func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error { 137 d := fd.dentry() 138 dirents, err := d.inode.getDirents() 139 if err != nil { 140 return err 141 } 142 143 d.InotifyWithParent(ctx, linux.IN_ACCESS, 0, vfs.PathEvent) 144 145 fd.mu.Lock() 146 defer fd.mu.Unlock() 147 148 for fd.off < int64(len(dirents)) { 149 if err := cb.Handle(dirents[fd.off]); err != nil { 150 return err 151 } 152 fd.off++ 153 } 154 return nil 155 } 156 157 // Seek implements vfs.FileDescriptionImpl.Seek. 158 func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { 159 fd.mu.Lock() 160 defer fd.mu.Unlock() 161 162 switch whence { 163 case linux.SEEK_SET: 164 // use offset as specified 165 case linux.SEEK_CUR: 166 offset += fd.off 167 default: 168 return 0, linuxerr.EINVAL 169 } 170 if offset < 0 { 171 return 0, linuxerr.EINVAL 172 } 173 fd.off = offset 174 return offset, nil 175 }