github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/forgetfs/forget_fs.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 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 forgetfs 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 22 "github.com/scaleoutsean/fusego" 23 "github.com/scaleoutsean/fusego/fuseops" 24 "github.com/scaleoutsean/fusego/fuseutil" 25 "github.com/jacobsa/syncutil" 26 ) 27 28 // Create a file system whose sole contents are a file named "foo" and a 29 // directory named "bar". 30 // 31 // The file "foo" may be opened for reading and/or writing, but reads and 32 // writes aren't supported. Additionally, any non-existent file or directory 33 // name may be created within any directory, but the resulting inode will 34 // appear to have been unlinked immediately. 35 // 36 // The file system maintains reference counts for the inodes involved. It will 37 // panic if a reference count becomes negative or if an inode ID is re-used 38 // after we expect it to be dead. Its Check method may be used to check that 39 // there are no inodes with unexpected reference counts remaining, after 40 // unmounting. 41 func NewFileSystem() *ForgetFS { 42 // Set up the actual file system. 43 impl := &fsImpl{ 44 inodes: map[fuseops.InodeID]*inode{ 45 cannedID_Root: &inode{ 46 attributes: fuseops.InodeAttributes{ 47 Nlink: 1, 48 Mode: 0777 | os.ModeDir, 49 }, 50 }, 51 cannedID_Foo: &inode{ 52 attributes: fuseops.InodeAttributes{ 53 Nlink: 1, 54 Mode: 0777, 55 }, 56 }, 57 cannedID_Bar: &inode{ 58 attributes: fuseops.InodeAttributes{ 59 Nlink: 1, 60 Mode: 0777 | os.ModeDir, 61 }, 62 }, 63 }, 64 nextInodeID: cannedID_Next, 65 } 66 67 // The root inode starts with a lookup count of one. 68 impl.inodes[cannedID_Root].IncrementLookupCount() 69 70 // The canned inodes are supposed to be stable from the user's point of view, 71 // so we should allow them to be looked up at any point even if the kernel 72 // has balanced its lookups with its forgets. Ensure that they never go to 73 // zero until the file system is destroyed. 74 impl.inodes[cannedID_Foo].IncrementLookupCount() 75 impl.inodes[cannedID_Bar].IncrementLookupCount() 76 77 // Set up the mutex. 78 impl.mu = syncutil.NewInvariantMutex(impl.checkInvariants) 79 80 // Set up a wrapper that exposes only certain methods. 81 return &ForgetFS{ 82 impl: impl, 83 server: fuseutil.NewFileSystemServer(impl), 84 } 85 } 86 87 //////////////////////////////////////////////////////////////////////// 88 // ForgetFS 89 //////////////////////////////////////////////////////////////////////// 90 91 type ForgetFS struct { 92 impl *fsImpl 93 server fuse.Server 94 } 95 96 func (fs *ForgetFS) ServeOps(c *fuse.Connection) { 97 fs.server.ServeOps(c) 98 } 99 100 // Panic if there are any inodes that have a non-zero reference count. For use 101 // after unmounting. 102 func (fs *ForgetFS) Check() { 103 fs.impl.Check() 104 } 105 106 //////////////////////////////////////////////////////////////////////// 107 // Actual implementation 108 //////////////////////////////////////////////////////////////////////// 109 110 const ( 111 cannedID_Root = fuseops.RootInodeID + iota 112 cannedID_Foo 113 cannedID_Bar 114 cannedID_Next 115 ) 116 117 type fsImpl struct { 118 fuseutil.NotImplementedFileSystem 119 120 ///////////////////////// 121 // Mutable state 122 ///////////////////////// 123 124 mu syncutil.InvariantMutex 125 126 // An index of inode by ID, for all IDs we have issued. 127 // 128 // GUARDED_BY(mu) 129 inodes map[fuseops.InodeID]*inode 130 131 // The next ID to issue. 132 // 133 // INVARIANT: For each k in inodes, k < nextInodeID 134 // 135 // GUARDED_BY(mu) 136 nextInodeID fuseops.InodeID 137 } 138 139 //////////////////////////////////////////////////////////////////////// 140 // inode 141 //////////////////////////////////////////////////////////////////////// 142 143 type inode struct { 144 attributes fuseops.InodeAttributes 145 146 // The current lookup count. 147 lookupCount uint64 148 149 // true if lookupCount has ever been positive. 150 lookedUp bool 151 } 152 153 func (in *inode) Forgotten() bool { 154 return in.lookedUp && in.lookupCount == 0 155 } 156 157 func (in *inode) IncrementLookupCount() { 158 in.lookupCount++ 159 in.lookedUp = true 160 } 161 162 func (in *inode) DecrementLookupCount(n uint64) { 163 if in.lookupCount < n { 164 panic(fmt.Sprintf( 165 "Overly large decrement: %v, %v", 166 in.lookupCount, 167 n)) 168 } 169 170 in.lookupCount -= n 171 } 172 173 // Decrement the lookup count to zero. 174 func (in *inode) Destroy() { 175 in.DecrementLookupCount(in.lookupCount) 176 } 177 178 //////////////////////////////////////////////////////////////////////// 179 // Helpers 180 //////////////////////////////////////////////////////////////////////// 181 182 // LOCKS_REQUIRED(fs.mu) 183 func (fs *fsImpl) checkInvariants() { 184 // INVARIANT: For each k in inodes, k < nextInodeID 185 for k, _ := range fs.inodes { 186 if !(k < fs.nextInodeID) { 187 panic("Unexpectedly large inode ID") 188 } 189 } 190 } 191 192 // LOCKS_EXCLUDED(fs.mu) 193 func (fs *fsImpl) Check() { 194 fs.mu.Lock() 195 defer fs.mu.Unlock() 196 197 for k, v := range fs.inodes { 198 if v.lookupCount != 0 { 199 panic(fmt.Sprintf("Inode %v has lookup count %v", k, v.lookupCount)) 200 } 201 } 202 } 203 204 // Look up the inode and verify it hasn't been forgotten. 205 // 206 // LOCKS_REQUIRED(fs.mu) 207 func (fs *fsImpl) findInodeByID(id fuseops.InodeID) *inode { 208 in := fs.inodes[id] 209 if in == nil { 210 panic(fmt.Sprintf("Unknown inode: %v", id)) 211 } 212 213 if in.Forgotten() { 214 panic(fmt.Sprintf("Forgotten inode: %v", id)) 215 } 216 217 return in 218 } 219 220 //////////////////////////////////////////////////////////////////////// 221 // FileSystem methods 222 //////////////////////////////////////////////////////////////////////// 223 224 func (fs *fsImpl) StatFS( 225 ctx context.Context, 226 op *fuseops.StatFSOp) error { 227 return nil 228 } 229 230 func (fs *fsImpl) LookUpInode( 231 ctx context.Context, 232 op *fuseops.LookUpInodeOp) error { 233 fs.mu.Lock() 234 defer fs.mu.Unlock() 235 236 // Make sure the parent exists and has not been forgotten. 237 _ = fs.findInodeByID(op.Parent) 238 239 // Handle the names we support. 240 var childID fuseops.InodeID 241 switch { 242 case op.Parent == cannedID_Root && op.Name == "foo": 243 childID = cannedID_Foo 244 245 case op.Parent == cannedID_Root && op.Name == "bar": 246 childID = cannedID_Bar 247 248 default: 249 return fuse.ENOENT 250 } 251 252 // Look up the child. 253 child := fs.findInodeByID(childID) 254 child.IncrementLookupCount() 255 256 // Return an appropriate entry. 257 op.Entry = fuseops.ChildInodeEntry{ 258 Child: childID, 259 Attributes: child.attributes, 260 } 261 262 return nil 263 } 264 265 func (fs *fsImpl) GetInodeAttributes( 266 ctx context.Context, 267 op *fuseops.GetInodeAttributesOp) error { 268 fs.mu.Lock() 269 defer fs.mu.Unlock() 270 271 // Find the inode, verifying that it has not been forgotten. 272 in := fs.findInodeByID(op.Inode) 273 274 // Return appropriate attributes. 275 op.Attributes = in.attributes 276 277 return nil 278 } 279 280 func (fs *fsImpl) ForgetInode( 281 ctx context.Context, 282 op *fuseops.ForgetInodeOp) error { 283 fs.mu.Lock() 284 defer fs.mu.Unlock() 285 286 // Find the inode and decrement its count. 287 in := fs.findInodeByID(op.Inode) 288 in.DecrementLookupCount(op.N) 289 290 return nil 291 } 292 293 func (fs *fsImpl) MkDir( 294 ctx context.Context, 295 op *fuseops.MkDirOp) error { 296 fs.mu.Lock() 297 defer fs.mu.Unlock() 298 299 // Make sure the parent exists and has not been forgotten. 300 _ = fs.findInodeByID(op.Parent) 301 302 // Mint a child inode. 303 childID := fs.nextInodeID 304 fs.nextInodeID++ 305 306 child := &inode{ 307 attributes: fuseops.InodeAttributes{ 308 Nlink: 0, 309 Mode: 0777 | os.ModeDir, 310 }, 311 } 312 313 fs.inodes[childID] = child 314 child.IncrementLookupCount() 315 316 // Return an appropriate entry. 317 op.Entry = fuseops.ChildInodeEntry{ 318 Child: childID, 319 Attributes: child.attributes, 320 } 321 322 return nil 323 } 324 325 func (fs *fsImpl) CreateFile( 326 ctx context.Context, 327 op *fuseops.CreateFileOp) error { 328 fs.mu.Lock() 329 defer fs.mu.Unlock() 330 331 // Make sure the parent exists and has not been forgotten. 332 _ = fs.findInodeByID(op.Parent) 333 334 // Mint a child inode. 335 childID := fs.nextInodeID 336 fs.nextInodeID++ 337 338 child := &inode{ 339 attributes: fuseops.InodeAttributes{ 340 Nlink: 0, 341 Mode: 0777, 342 }, 343 } 344 345 fs.inodes[childID] = child 346 child.IncrementLookupCount() 347 348 // Return an appropriate entry. 349 op.Entry = fuseops.ChildInodeEntry{ 350 Child: childID, 351 Attributes: child.attributes, 352 } 353 354 return nil 355 } 356 357 func (fs *fsImpl) OpenFile( 358 ctx context.Context, 359 op *fuseops.OpenFileOp) error { 360 fs.mu.Lock() 361 defer fs.mu.Unlock() 362 363 // Verify that the inode has not been forgotten. 364 _ = fs.findInodeByID(op.Inode) 365 366 return nil 367 } 368 369 func (fs *fsImpl) OpenDir( 370 ctx context.Context, 371 op *fuseops.OpenDirOp) error { 372 fs.mu.Lock() 373 defer fs.mu.Unlock() 374 375 // Verify that the inode has not been forgotten. 376 _ = fs.findInodeByID(op.Inode) 377 378 return nil 379 } 380 381 func (fs *fsImpl) Destroy() { 382 for _, in := range fs.inodes { 383 in.Destroy() 384 } 385 }