gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/tmpfs/directory.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 tmpfs 16 17 import ( 18 "gvisor.dev/gvisor/pkg/abi/linux" 19 "gvisor.dev/gvisor/pkg/atomicbitops" 20 "gvisor.dev/gvisor/pkg/context" 21 "gvisor.dev/gvisor/pkg/errors/linuxerr" 22 "gvisor.dev/gvisor/pkg/sentry/kernel/auth" 23 "gvisor.dev/gvisor/pkg/sentry/vfs" 24 ) 25 26 // +stateify savable 27 type directory struct { 28 // Since directories can't be hard-linked, each directory can only be 29 // associated with a single dentry, which we can store in the directory 30 // struct. 31 dentry dentry 32 inode inode 33 34 // childMap maps the names of the directory's children to their dentries. 35 // childMap is protected by filesystem.mu. 36 childMap map[string]*dentry 37 38 // numChildren is len(childMap), but accessed using atomic memory 39 // operations to avoid locking in inode.statTo(). 40 numChildren atomicbitops.Int64 41 42 // childList is a list containing (1) child dentries and (2) fake dentries 43 // (with inode == nil) that represent the iteration position of 44 // directoryFDs. childList is used to support directoryFD.IterDirents() 45 // efficiently. childList is protected by iterMu. 46 iterMu iterMutex `state:"nosave"` 47 childList dentryList 48 } 49 50 func (fs *filesystem) newDirectory(kuid auth.KUID, kgid auth.KGID, mode linux.FileMode, parentDir *directory) *directory { 51 dir := &directory{} 52 dir.inode.init(dir, fs, kuid, kgid, linux.S_IFDIR|mode, parentDir) 53 dir.inode.nlink = atomicbitops.FromUint32(2) // from "." and parent directory or ".." for root 54 dir.dentry.inode = &dir.inode 55 dir.dentry.vfsd.Init(&dir.dentry) 56 return dir 57 } 58 59 // Preconditions: 60 // - filesystem.mu must be locked for writing. 61 // - dir must not already contain a child with the given name. 62 func (dir *directory) insertChildLocked(child *dentry, name string) { 63 child.parent.Store(&dir.dentry) 64 child.name = name 65 if dir.childMap == nil { 66 dir.childMap = make(map[string]*dentry) 67 } 68 dir.childMap[name] = child 69 dir.numChildren.Add(1) 70 dir.iterMu.Lock() 71 dir.childList.PushBack(child) 72 dir.iterMu.Unlock() 73 } 74 75 // Preconditions: filesystem.mu must be locked for writing. 76 func (dir *directory) removeChildLocked(child *dentry) { 77 delete(dir.childMap, child.name) 78 dir.numChildren.Add(-1) 79 dir.iterMu.Lock() 80 dir.childList.Remove(child) 81 dir.iterMu.Unlock() 82 } 83 84 func (dir *directory) mayDelete(creds *auth.Credentials, child *dentry) error { 85 return vfs.CheckDeleteSticky( 86 creds, 87 linux.FileMode(dir.inode.mode.Load()), 88 auth.KUID(dir.inode.uid.Load()), 89 auth.KUID(child.inode.uid.Load()), 90 auth.KGID(child.inode.gid.Load()), 91 ) 92 } 93 94 // +stateify savable 95 type directoryFD struct { 96 fileDescription 97 vfs.DirectoryFileDescriptionDefaultImpl 98 99 // Protected by directory.iterMu. 100 iter *dentry 101 off int64 102 } 103 104 // Release implements vfs.FileDescriptionImpl.Release. 105 func (fd *directoryFD) Release(ctx context.Context) { 106 if fd.iter != nil { 107 dir := fd.inode().impl.(*directory) 108 dir.iterMu.Lock() 109 dir.childList.Remove(fd.iter) 110 dir.iterMu.Unlock() 111 fd.iter = nil 112 } 113 } 114 115 // IterDirents implements vfs.FileDescriptionImpl.IterDirents. 116 func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error { 117 fs := fd.filesystem() 118 dir := fd.inode().impl.(*directory) 119 120 // fs.mu is required to read d.parent and dentry.name. 121 fs.mu.RLock() 122 defer fs.mu.RUnlock() 123 dir.iterMu.Lock() 124 defer dir.iterMu.Unlock() 125 126 fd.inode().touchAtime(fd.vfsfd.Mount()) 127 128 if fd.off == 0 { 129 if err := cb.Handle(vfs.Dirent{ 130 Name: ".", 131 Type: linux.DT_DIR, 132 Ino: dir.inode.ino, 133 NextOff: 1, 134 }); err != nil { 135 return err 136 } 137 fd.off++ 138 } 139 140 if fd.off == 1 { 141 parentInode := genericParentOrSelf(&dir.dentry).inode 142 if err := cb.Handle(vfs.Dirent{ 143 Name: "..", 144 Type: parentInode.direntType(), 145 Ino: parentInode.ino, 146 NextOff: 2, 147 }); err != nil { 148 return err 149 } 150 fd.off++ 151 } 152 153 var child *dentry 154 if fd.iter == nil { 155 // Start iteration at the beginning of dir. 156 child = dir.childList.Front() 157 fd.iter = &dentry{} 158 } else { 159 // Continue iteration from where we left off. 160 child = fd.iter.Next() 161 dir.childList.Remove(fd.iter) 162 } 163 for child != nil { 164 // Skip other directoryFD iterators. 165 if child.inode != nil { 166 if err := cb.Handle(vfs.Dirent{ 167 Name: child.name, 168 Type: child.inode.direntType(), 169 Ino: child.inode.ino, 170 NextOff: fd.off + 1, 171 }); err != nil { 172 dir.childList.InsertBefore(child, fd.iter) 173 return err 174 } 175 fd.off++ 176 } 177 child = child.Next() 178 } 179 dir.childList.PushBack(fd.iter) 180 return nil 181 } 182 183 // Seek implements vfs.FileDescriptionImpl.Seek. 184 func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { 185 dir := fd.inode().impl.(*directory) 186 dir.iterMu.Lock() 187 defer dir.iterMu.Unlock() 188 189 switch whence { 190 case linux.SEEK_SET: 191 // Use offset as given. 192 case linux.SEEK_CUR: 193 offset += fd.off 194 default: 195 return 0, linuxerr.EINVAL 196 } 197 if offset < 0 { 198 return 0, linuxerr.EINVAL 199 } 200 201 // If the offset isn't changing (e.g. due to lseek(0, SEEK_CUR)), don't 202 // seek even if doing so might reposition the iterator due to concurrent 203 // mutation of the directory. Compare fs/libfs.c:dcache_dir_lseek(). 204 if fd.off == offset { 205 return offset, nil 206 } 207 208 fd.off = offset 209 // Compensate for "." and "..". 210 remChildren := int64(0) 211 if offset >= 2 { 212 remChildren = offset - 2 213 } 214 215 // Ensure that fd.iter exists and is not linked into dir.childList. 216 if fd.iter == nil { 217 fd.iter = &dentry{} 218 } else { 219 dir.childList.Remove(fd.iter) 220 } 221 // Insert fd.iter before the remChildren'th child, or at the end of the 222 // list if remChildren >= number of children. 223 child := dir.childList.Front() 224 for child != nil { 225 // Skip other directoryFD iterators. 226 if child.inode != nil { 227 if remChildren == 0 { 228 dir.childList.InsertBefore(child, fd.iter) 229 return offset, nil 230 } 231 remChildren-- 232 } 233 child = child.Next() 234 } 235 dir.childList.PushBack(fd.iter) 236 return offset, nil 237 }