github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/mount.go (about) 1 // Copyright 2018 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 fs 16 17 import ( 18 "bytes" 19 "fmt" 20 "sync/atomic" 21 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/refs" 24 ) 25 26 // DirentOperations provide file systems greater control over how long a Dirent 27 // stays pinned in core. Implementations must not take Dirent.mu. 28 type DirentOperations interface { 29 // Revalidate is called during lookup each time we encounter a Dirent 30 // in the cache. Implementations may update stale properties of the 31 // child Inode. If Revalidate returns true, then the entire Inode will 32 // be reloaded. 33 // 34 // Revalidate will never be called on a Inode that is mounted. 35 Revalidate(ctx context.Context, name string, parent, child *Inode) bool 36 37 // Keep returns true if the Dirent should be kept in memory for as long 38 // as possible beyond any active references. 39 Keep(dirent *Dirent) bool 40 41 // CacheReaddir returns true if directory entries returned by 42 // FileOperations.Readdir may be cached for future use. 43 // 44 // Postconditions: This method must always return the same value. 45 CacheReaddir() bool 46 } 47 48 // MountSourceOperations contains filesystem specific operations. 49 type MountSourceOperations interface { 50 // DirentOperations provide optional extra management of Dirents. 51 DirentOperations 52 53 // Destroy destroys the MountSource. 54 Destroy(ctx context.Context) 55 56 // Below are MountSourceOperations that do not conform to Linux. 57 58 // ResetInodeMappings clears all mappings of Inodes before SaveInodeMapping 59 // is called. 60 ResetInodeMappings() 61 62 // SaveInodeMappings is called during saving to store, for each reachable 63 // Inode in the mounted filesystem, a mapping of Inode.StableAttr.InodeID 64 // to the Inode's path relative to its mount point. If an Inode is 65 // reachable at more than one path due to hard links, it is unspecified 66 // which path is mapped. Filesystems that do not use this information to 67 // restore inodes can make SaveInodeMappings a no-op. 68 SaveInodeMapping(inode *Inode, path string) 69 } 70 71 // InodeMappings defines a fmt.Stringer MountSource Inode mappings. 72 type InodeMappings map[uint64]string 73 74 // String implements fmt.Stringer.String. 75 func (i InodeMappings) String() string { 76 var mappingsBuf bytes.Buffer 77 mappingsBuf.WriteString("\n") 78 for ino, name := range i { 79 mappingsBuf.WriteString(fmt.Sprintf("\t%q\t\tinode number %d\n", name, ino)) 80 } 81 return mappingsBuf.String() 82 } 83 84 // MountSource represents a source of file objects. 85 // 86 // MountSource corresponds to struct super_block in Linux. 87 // 88 // A mount source may represent a physical device (or a partition of a physical 89 // device) or a virtual source of files such as procfs for a specific PID 90 // namespace. There should be only one mount source per logical device. E.g. 91 // there should be only procfs mount source for a given PID namespace. 92 // 93 // A mount source represents files as inodes. Every inode belongs to exactly 94 // one mount source. Each file object may only be represented using one inode 95 // object in a sentry instance. 96 // 97 // TODO(b/63601033): Move Flags out of MountSource to Mount. 98 // 99 // +stateify savable 100 type MountSource struct { 101 refs.AtomicRefCount 102 103 // MountSourceOperations defines filesystem specific behavior. 104 MountSourceOperations 105 106 // FilesystemType is the type of the filesystem backing this mount. 107 FilesystemType string 108 109 // Flags are the flags that this filesystem was mounted with. 110 Flags MountSourceFlags 111 112 // fscache keeps Dirents pinned beyond application references to them. 113 // It must be flushed before kernel.SaveTo. 114 fscache *DirentCache 115 116 // direntRefs is the sum of references on all Dirents in this MountSource. 117 // 118 // direntRefs is increased when a Dirent in MountSource is IncRef'd, and 119 // decreased when a Dirent in MountSource is DecRef'd. 120 // 121 // To cleanly unmount a MountSource, one must check that no direntRefs are 122 // held anymore. To check, one must hold root.parent.dirMu of the 123 // MountSource's root Dirent before reading direntRefs to prevent further 124 // walks to Dirents in this MountSource. 125 // 126 // direntRefs must be atomically changed. 127 direntRefs uint64 128 } 129 130 // DefaultDirentCacheSize is the number of Dirents that the VFS can hold an 131 // extra reference on. 132 const DefaultDirentCacheSize uint64 = 1000 133 134 // NewMountSource returns a new MountSource. Filesystem may be nil if there is no 135 // filesystem backing the mount. 136 func NewMountSource(ctx context.Context, mops MountSourceOperations, filesystem Filesystem, flags MountSourceFlags) *MountSource { 137 fsType := "none" 138 if filesystem != nil { 139 fsType = filesystem.Name() 140 } 141 msrc := MountSource{ 142 MountSourceOperations: mops, 143 Flags: flags, 144 FilesystemType: fsType, 145 fscache: NewDirentCache(DefaultDirentCacheSize), 146 } 147 msrc.EnableLeakCheck("fs.MountSource") 148 return &msrc 149 } 150 151 // DirentRefs returns the current mount direntRefs. 152 func (msrc *MountSource) DirentRefs() uint64 { 153 return atomic.LoadUint64(&msrc.direntRefs) 154 } 155 156 // IncDirentRefs increases direntRefs. 157 func (msrc *MountSource) IncDirentRefs() { 158 atomic.AddUint64(&msrc.direntRefs, 1) 159 } 160 161 // DecDirentRefs decrements direntRefs. 162 func (msrc *MountSource) DecDirentRefs() { 163 if atomic.AddUint64(&msrc.direntRefs, ^uint64(0)) == ^uint64(0) { 164 panic("Decremented zero mount reference direntRefs") 165 } 166 } 167 168 func (msrc *MountSource) destroy(ctx context.Context) { 169 if c := msrc.DirentRefs(); c != 0 { 170 panic(fmt.Sprintf("MountSource with non-zero direntRefs is being destroyed: %d", c)) 171 } 172 msrc.MountSourceOperations.Destroy(ctx) 173 } 174 175 // DecRef drops a reference on the MountSource. 176 func (msrc *MountSource) DecRef(ctx context.Context) { 177 msrc.DecRefWithDestructor(ctx, msrc.destroy) 178 } 179 180 // FlushDirentRefs drops all references held by the MountSource on Dirents. 181 func (msrc *MountSource) FlushDirentRefs() { 182 msrc.fscache.Invalidate() 183 } 184 185 // SetDirentCacheMaxSize sets the max size to the dirent cache associated with 186 // this mount source. 187 func (msrc *MountSource) SetDirentCacheMaxSize(max uint64) { 188 msrc.fscache.setMaxSize(max) 189 } 190 191 // SetDirentCacheLimiter sets the limiter objcet to the dirent cache associated 192 // with this mount source. 193 func (msrc *MountSource) SetDirentCacheLimiter(l *DirentCacheLimiter) { 194 msrc.fscache.limit = l 195 } 196 197 // NewCachingMountSource returns a generic mount that will cache dirents 198 // aggressively. 199 func NewCachingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource { 200 return NewMountSource(ctx, &SimpleMountSourceOperations{ 201 keep: true, 202 revalidate: false, 203 cacheReaddir: true, 204 }, filesystem, flags) 205 } 206 207 // NewNonCachingMountSource returns a generic mount that will never cache dirents. 208 func NewNonCachingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource { 209 return NewMountSource(ctx, &SimpleMountSourceOperations{ 210 keep: false, 211 revalidate: false, 212 cacheReaddir: false, 213 }, filesystem, flags) 214 } 215 216 // NewRevalidatingMountSource returns a generic mount that will cache dirents, 217 // but will revalidate them on each lookup and always perform uncached readdir. 218 func NewRevalidatingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource { 219 return NewMountSource(ctx, &SimpleMountSourceOperations{ 220 keep: true, 221 revalidate: true, 222 cacheReaddir: false, 223 }, filesystem, flags) 224 } 225 226 // NewPseudoMountSource returns a "pseudo" mount source that is not backed by 227 // an actual filesystem. It is always non-caching. 228 func NewPseudoMountSource(ctx context.Context) *MountSource { 229 return NewMountSource(ctx, &SimpleMountSourceOperations{ 230 keep: false, 231 revalidate: false, 232 cacheReaddir: false, 233 }, nil, MountSourceFlags{}) 234 } 235 236 // SimpleMountSourceOperations implements MountSourceOperations. 237 // 238 // +stateify savable 239 type SimpleMountSourceOperations struct { 240 keep bool 241 revalidate bool 242 cacheReaddir bool 243 } 244 245 // Revalidate implements MountSourceOperations.Revalidate. 246 func (smo *SimpleMountSourceOperations) Revalidate(context.Context, string, *Inode, *Inode) bool { 247 return smo.revalidate 248 } 249 250 // Keep implements MountSourceOperations.Keep. 251 func (smo *SimpleMountSourceOperations) Keep(*Dirent) bool { 252 return smo.keep 253 } 254 255 // CacheReaddir implements MountSourceOperations.CacheReaddir. 256 func (smo *SimpleMountSourceOperations) CacheReaddir() bool { 257 return smo.cacheReaddir 258 } 259 260 // ResetInodeMappings implements MountSourceOperations.ResetInodeMappings. 261 func (*SimpleMountSourceOperations) ResetInodeMappings() {} 262 263 // SaveInodeMapping implements MountSourceOperations.SaveInodeMapping. 264 func (*SimpleMountSourceOperations) SaveInodeMapping(*Inode, string) {} 265 266 // Destroy implements MountSourceOperations.Destroy. 267 func (*SimpleMountSourceOperations) Destroy(context.Context) {} 268 269 // Info defines attributes of a filesystem. 270 type Info struct { 271 // Type is the filesystem type magic value. 272 Type uint64 273 274 // TotalBlocks is the total data blocks in the filesystem. 275 TotalBlocks uint64 276 277 // FreeBlocks is the number of free blocks available. 278 FreeBlocks uint64 279 280 // TotalFiles is the total file nodes in the filesystem. 281 TotalFiles uint64 282 283 // FreeFiles is the number of free file nodes. 284 FreeFiles uint64 285 }