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