github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/memfs/memfs.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 memfs 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "os" 22 "syscall" 23 "time" 24 25 "github.com/scaleoutsean/fusego" 26 "github.com/scaleoutsean/fusego/fuseops" 27 "github.com/scaleoutsean/fusego/fuseutil" 28 "github.com/jacobsa/syncutil" 29 "golang.org/x/sys/unix" 30 ) 31 32 type memFS struct { 33 fuseutil.NotImplementedFileSystem 34 35 // The UID and GID that every inode receives. 36 uid uint32 37 gid uint32 38 39 ///////////////////////// 40 // Mutable state 41 ///////////////////////// 42 43 mu syncutil.InvariantMutex 44 45 // The collection of live inodes, indexed by ID. IDs of free inodes that may 46 // be re-used have nil entries. No ID less than fuseops.RootInodeID is ever 47 // used. 48 // 49 // All inodes are protected by the file system mutex. 50 // 51 // INVARIANT: For each inode in, in.CheckInvariants() does not panic. 52 // INVARIANT: len(inodes) > fuseops.RootInodeID 53 // INVARIANT: For all i < fuseops.RootInodeID, inodes[i] == nil 54 // INVARIANT: inodes[fuseops.RootInodeID] != nil 55 // INVARIANT: inodes[fuseops.RootInodeID].isDir() 56 inodes []*inode // GUARDED_BY(mu) 57 58 // A list of inode IDs within inodes available for reuse, not including the 59 // reserved IDs less than fuseops.RootInodeID. 60 // 61 // INVARIANT: This is all and only indices i of 'inodes' such that i > 62 // fuseops.RootInodeID and inodes[i] == nil 63 freeInodes []fuseops.InodeID // GUARDED_BY(mu) 64 } 65 66 // Create a file system that stores data and metadata in memory. 67 // 68 // The supplied UID/GID pair will own the root inode. This file system does no 69 // permissions checking, and should therefore be mounted with the 70 // default_permissions option. 71 func NewMemFS( 72 uid uint32, 73 gid uint32) fuse.Server { 74 // Set up the basic struct. 75 fs := &memFS{ 76 inodes: make([]*inode, fuseops.RootInodeID+1), 77 uid: uid, 78 gid: gid, 79 } 80 81 // Set up the root inode. 82 rootAttrs := fuseops.InodeAttributes{ 83 Mode: 0700 | os.ModeDir, 84 Uid: uid, 85 Gid: gid, 86 } 87 88 fs.inodes[fuseops.RootInodeID] = newInode(rootAttrs) 89 90 // Set up invariant checking. 91 fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants) 92 93 return fuseutil.NewFileSystemServer(fs) 94 } 95 96 //////////////////////////////////////////////////////////////////////// 97 // Helpers 98 //////////////////////////////////////////////////////////////////////// 99 100 func (fs *memFS) checkInvariants() { 101 // Check reserved inodes. 102 for i := 0; i < fuseops.RootInodeID; i++ { 103 if fs.inodes[i] != nil { 104 panic(fmt.Sprintf("Non-nil inode for ID: %v", i)) 105 } 106 } 107 108 // Check the root inode. 109 if !fs.inodes[fuseops.RootInodeID].isDir() { 110 panic("Expected root to be a directory.") 111 } 112 113 // Build our own list of free IDs. 114 freeIDsEncountered := make(map[fuseops.InodeID]struct{}) 115 for i := fuseops.RootInodeID + 1; i < len(fs.inodes); i++ { 116 inode := fs.inodes[i] 117 if inode == nil { 118 freeIDsEncountered[fuseops.InodeID(i)] = struct{}{} 119 continue 120 } 121 } 122 123 // Check fs.freeInodes. 124 if len(fs.freeInodes) != len(freeIDsEncountered) { 125 panic( 126 fmt.Sprintf( 127 "Length mismatch: %v vs. %v", 128 len(fs.freeInodes), 129 len(freeIDsEncountered))) 130 } 131 132 for _, id := range fs.freeInodes { 133 if _, ok := freeIDsEncountered[id]; !ok { 134 panic(fmt.Sprintf("Unexected free inode ID: %v", id)) 135 } 136 } 137 138 // INVARIANT: For each inode in, in.CheckInvariants() does not panic. 139 for _, in := range fs.inodes { 140 in.CheckInvariants() 141 } 142 } 143 144 // Find the given inode. Panic if it doesn't exist. 145 // 146 // LOCKS_REQUIRED(fs.mu) 147 func (fs *memFS) getInodeOrDie(id fuseops.InodeID) *inode { 148 inode := fs.inodes[id] 149 if inode == nil { 150 panic(fmt.Sprintf("Unknown inode: %v", id)) 151 } 152 153 return inode 154 } 155 156 // Allocate a new inode, assigning it an ID that is not in use. 157 // 158 // LOCKS_REQUIRED(fs.mu) 159 func (fs *memFS) allocateInode( 160 attrs fuseops.InodeAttributes) (id fuseops.InodeID, inode *inode) { 161 // Create the inode. 162 inode = newInode(attrs) 163 164 // Re-use a free ID if possible. Otherwise mint a new one. 165 numFree := len(fs.freeInodes) 166 if numFree != 0 { 167 id = fs.freeInodes[numFree-1] 168 fs.freeInodes = fs.freeInodes[:numFree-1] 169 fs.inodes[id] = inode 170 } else { 171 id = fuseops.InodeID(len(fs.inodes)) 172 fs.inodes = append(fs.inodes, inode) 173 } 174 175 return id, inode 176 } 177 178 // LOCKS_REQUIRED(fs.mu) 179 func (fs *memFS) deallocateInode(id fuseops.InodeID) { 180 fs.freeInodes = append(fs.freeInodes, id) 181 fs.inodes[id] = nil 182 } 183 184 //////////////////////////////////////////////////////////////////////// 185 // FileSystem methods 186 //////////////////////////////////////////////////////////////////////// 187 188 func (fs *memFS) StatFS( 189 ctx context.Context, 190 op *fuseops.StatFSOp) error { 191 return nil 192 } 193 194 func (fs *memFS) LookUpInode( 195 ctx context.Context, 196 op *fuseops.LookUpInodeOp) error { 197 fs.mu.Lock() 198 defer fs.mu.Unlock() 199 200 // Grab the parent directory. 201 inode := fs.getInodeOrDie(op.Parent) 202 203 // Does the directory have an entry with the given name? 204 childID, _, ok := inode.LookUpChild(op.Name) 205 if !ok { 206 return fuse.ENOENT 207 } 208 209 // Grab the child. 210 child := fs.getInodeOrDie(childID) 211 212 // Fill in the response. 213 op.Entry.Child = childID 214 op.Entry.Attributes = child.attrs 215 216 // We don't spontaneously mutate, so the kernel can cache as long as it wants 217 // (since it also handles invalidation). 218 op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour) 219 op.Entry.EntryExpiration = op.Entry.AttributesExpiration 220 221 return nil 222 } 223 224 func (fs *memFS) GetInodeAttributes( 225 ctx context.Context, 226 op *fuseops.GetInodeAttributesOp) error { 227 fs.mu.Lock() 228 defer fs.mu.Unlock() 229 230 // Grab the inode. 231 inode := fs.getInodeOrDie(op.Inode) 232 233 // Fill in the response. 234 op.Attributes = inode.attrs 235 236 // We don't spontaneously mutate, so the kernel can cache as long as it wants 237 // (since it also handles invalidation). 238 op.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour) 239 240 return nil 241 } 242 243 func (fs *memFS) SetInodeAttributes( 244 ctx context.Context, 245 op *fuseops.SetInodeAttributesOp) error { 246 fs.mu.Lock() 247 defer fs.mu.Unlock() 248 249 var err error 250 if op.Size != nil && op.Handle == nil && *op.Size != 0 { 251 // require that truncate to non-zero has to be ftruncate() 252 // but allow open(O_TRUNC) 253 err = syscall.EBADF 254 } 255 256 // Grab the inode. 257 inode := fs.getInodeOrDie(op.Inode) 258 259 // Handle the request. 260 inode.SetAttributes(op.Size, op.Mode, op.Mtime) 261 262 // Fill in the response. 263 op.Attributes = inode.attrs 264 265 // We don't spontaneously mutate, so the kernel can cache as long as it wants 266 // (since it also handles invalidation). 267 op.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour) 268 269 return err 270 } 271 272 func (fs *memFS) MkDir( 273 ctx context.Context, 274 op *fuseops.MkDirOp) error { 275 fs.mu.Lock() 276 defer fs.mu.Unlock() 277 278 // Grab the parent, which we will update shortly. 279 parent := fs.getInodeOrDie(op.Parent) 280 281 // Ensure that the name doesn't already exist, so we don't wind up with a 282 // duplicate. 283 _, _, exists := parent.LookUpChild(op.Name) 284 if exists { 285 return fuse.EEXIST 286 } 287 288 // Set up attributes from the child. 289 childAttrs := fuseops.InodeAttributes{ 290 Nlink: 1, 291 Mode: op.Mode, 292 Uid: fs.uid, 293 Gid: fs.gid, 294 } 295 296 // Allocate a child. 297 childID, child := fs.allocateInode(childAttrs) 298 299 // Add an entry in the parent. 300 parent.AddChild(childID, op.Name, fuseutil.DT_Directory) 301 302 // Fill in the response. 303 op.Entry.Child = childID 304 op.Entry.Attributes = child.attrs 305 306 // We don't spontaneously mutate, so the kernel can cache as long as it wants 307 // (since it also handles invalidation). 308 op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour) 309 op.Entry.EntryExpiration = op.Entry.AttributesExpiration 310 311 return nil 312 } 313 314 func (fs *memFS) MkNode( 315 ctx context.Context, 316 op *fuseops.MkNodeOp) error { 317 fs.mu.Lock() 318 defer fs.mu.Unlock() 319 320 var err error 321 op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode) 322 return err 323 } 324 325 // LOCKS_REQUIRED(fs.mu) 326 func (fs *memFS) createFile( 327 parentID fuseops.InodeID, 328 name string, 329 mode os.FileMode) (fuseops.ChildInodeEntry, error) { 330 // Grab the parent, which we will update shortly. 331 parent := fs.getInodeOrDie(parentID) 332 333 // Ensure that the name doesn't already exist, so we don't wind up with a 334 // duplicate. 335 _, _, exists := parent.LookUpChild(name) 336 if exists { 337 return fuseops.ChildInodeEntry{}, fuse.EEXIST 338 } 339 340 // Set up attributes for the child. 341 now := time.Now() 342 childAttrs := fuseops.InodeAttributes{ 343 Nlink: 1, 344 Mode: mode, 345 Atime: now, 346 Mtime: now, 347 Ctime: now, 348 Crtime: now, 349 Uid: fs.uid, 350 Gid: fs.gid, 351 } 352 353 // Allocate a child. 354 childID, child := fs.allocateInode(childAttrs) 355 356 // Add an entry in the parent. 357 parent.AddChild(childID, name, fuseutil.DT_File) 358 359 // Fill in the response entry. 360 var entry fuseops.ChildInodeEntry 361 entry.Child = childID 362 entry.Attributes = child.attrs 363 364 // We don't spontaneously mutate, so the kernel can cache as long as it wants 365 // (since it also handles invalidation). 366 entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour) 367 entry.EntryExpiration = entry.AttributesExpiration 368 369 return entry, nil 370 } 371 372 func (fs *memFS) CreateFile( 373 ctx context.Context, 374 op *fuseops.CreateFileOp) (err error) { 375 if op.Metadata.Pid == 0 { 376 // CreateFileOp should have a valid pid in metadata. 377 return fuse.EINVAL 378 } 379 fs.mu.Lock() 380 defer fs.mu.Unlock() 381 382 var err error 383 op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode) 384 return err 385 } 386 387 func (fs *memFS) CreateSymlink( 388 ctx context.Context, 389 op *fuseops.CreateSymlinkOp) error { 390 fs.mu.Lock() 391 defer fs.mu.Unlock() 392 393 // Grab the parent, which we will update shortly. 394 parent := fs.getInodeOrDie(op.Parent) 395 396 // Ensure that the name doesn't already exist, so we don't wind up with a 397 // duplicate. 398 _, _, exists := parent.LookUpChild(op.Name) 399 if exists { 400 return fuse.EEXIST 401 } 402 403 // Set up attributes from the child. 404 now := time.Now() 405 childAttrs := fuseops.InodeAttributes{ 406 Nlink: 1, 407 Mode: 0444 | os.ModeSymlink, 408 Atime: now, 409 Mtime: now, 410 Ctime: now, 411 Crtime: now, 412 Uid: fs.uid, 413 Gid: fs.gid, 414 } 415 416 // Allocate a child. 417 childID, child := fs.allocateInode(childAttrs) 418 419 // Set up its target. 420 child.target = op.Target 421 422 // Add an entry in the parent. 423 parent.AddChild(childID, op.Name, fuseutil.DT_Link) 424 425 // Fill in the response entry. 426 op.Entry.Child = childID 427 op.Entry.Attributes = child.attrs 428 429 // We don't spontaneously mutate, so the kernel can cache as long as it wants 430 // (since it also handles invalidation). 431 op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour) 432 op.Entry.EntryExpiration = op.Entry.AttributesExpiration 433 434 return nil 435 } 436 437 func (fs *memFS) CreateLink( 438 ctx context.Context, 439 op *fuseops.CreateLinkOp) error { 440 fs.mu.Lock() 441 defer fs.mu.Unlock() 442 443 // Grab the parent, which we will update shortly. 444 parent := fs.getInodeOrDie(op.Parent) 445 446 // Ensure that the name doesn't already exist, so we don't wind up with a 447 // duplicate. 448 _, _, exists := parent.LookUpChild(op.Name) 449 if exists { 450 return fuse.EEXIST 451 } 452 453 // Get the target inode to be linked 454 target := fs.getInodeOrDie(op.Target) 455 456 // Update the attributes 457 now := time.Now() 458 target.attrs.Nlink++ 459 target.attrs.Ctime = now 460 461 // Add an entry in the parent. 462 parent.AddChild(op.Target, op.Name, fuseutil.DT_File) 463 464 // Return the response. 465 op.Entry.Child = op.Target 466 op.Entry.Attributes = target.attrs 467 468 // We don't spontaneously mutate, so the kernel can cache as long as it wants 469 // (since it also handles invalidation). 470 op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour) 471 op.Entry.EntryExpiration = op.Entry.AttributesExpiration 472 473 return nil 474 } 475 476 func (fs *memFS) Rename( 477 ctx context.Context, 478 op *fuseops.RenameOp) error { 479 fs.mu.Lock() 480 defer fs.mu.Unlock() 481 482 // Ask the old parent for the child's inode ID and type. 483 oldParent := fs.getInodeOrDie(op.OldParent) 484 childID, childType, ok := oldParent.LookUpChild(op.OldName) 485 486 if !ok { 487 return fuse.ENOENT 488 } 489 490 // If the new name exists already in the new parent, make sure it's not a 491 // non-empty directory, then delete it. 492 newParent := fs.getInodeOrDie(op.NewParent) 493 existingID, _, ok := newParent.LookUpChild(op.NewName) 494 if ok { 495 existing := fs.getInodeOrDie(existingID) 496 497 var buf [4096]byte 498 if existing.isDir() && existing.ReadDir(buf[:], 0) > 0 { 499 return fuse.ENOTEMPTY 500 } 501 502 newParent.RemoveChild(op.NewName) 503 } 504 505 // Link the new name. 506 newParent.AddChild( 507 childID, 508 op.NewName, 509 childType) 510 511 // Finally, remove the old name from the old parent. 512 oldParent.RemoveChild(op.OldName) 513 514 return nil 515 } 516 517 func (fs *memFS) RmDir( 518 ctx context.Context, 519 op *fuseops.RmDirOp) error { 520 fs.mu.Lock() 521 defer fs.mu.Unlock() 522 523 // Grab the parent, which we will update shortly. 524 parent := fs.getInodeOrDie(op.Parent) 525 526 // Find the child within the parent. 527 childID, _, ok := parent.LookUpChild(op.Name) 528 if !ok { 529 return fuse.ENOENT 530 } 531 532 // Grab the child. 533 child := fs.getInodeOrDie(childID) 534 535 // Make sure the child is empty. 536 if child.Len() != 0 { 537 return fuse.ENOTEMPTY 538 } 539 540 // Remove the entry within the parent. 541 parent.RemoveChild(op.Name) 542 543 // Mark the child as unlinked. 544 child.attrs.Nlink-- 545 546 return nil 547 } 548 549 func (fs *memFS) Unlink( 550 ctx context.Context, 551 op *fuseops.UnlinkOp) error { 552 fs.mu.Lock() 553 defer fs.mu.Unlock() 554 555 // Grab the parent, which we will update shortly. 556 parent := fs.getInodeOrDie(op.Parent) 557 558 // Find the child within the parent. 559 childID, _, ok := parent.LookUpChild(op.Name) 560 if !ok { 561 return fuse.ENOENT 562 } 563 564 // Grab the child. 565 child := fs.getInodeOrDie(childID) 566 567 // Remove the entry within the parent. 568 parent.RemoveChild(op.Name) 569 570 // Mark the child as unlinked. 571 child.attrs.Nlink-- 572 573 return nil 574 } 575 576 func (fs *memFS) OpenDir( 577 ctx context.Context, 578 op *fuseops.OpenDirOp) error { 579 fs.mu.Lock() 580 defer fs.mu.Unlock() 581 582 // We don't mutate spontaneosuly, so if the VFS layer has asked for an 583 // inode that doesn't exist, something screwed up earlier (a lookup, a 584 // cache invalidation, etc.). 585 inode := fs.getInodeOrDie(op.Inode) 586 587 if !inode.isDir() { 588 panic("Found non-dir.") 589 } 590 591 return nil 592 } 593 594 func (fs *memFS) ReadDir( 595 ctx context.Context, 596 op *fuseops.ReadDirOp) error { 597 fs.mu.Lock() 598 defer fs.mu.Unlock() 599 600 // Grab the directory. 601 inode := fs.getInodeOrDie(op.Inode) 602 603 // Serve the request. 604 op.BytesRead = inode.ReadDir(op.Dst, int(op.Offset)) 605 606 return nil 607 } 608 609 func (fs *memFS) OpenFile( 610 ctx context.Context, 611 op *fuseops.OpenFileOp) (err error) { 612 if op.Metadata.Pid == 0 { 613 // OpenFileOp should have a valid pid in metadata. 614 return fuse.EINVAL 615 } 616 617 fs.mu.Lock() 618 defer fs.mu.Unlock() 619 620 // We don't mutate spontaneosuly, so if the VFS layer has asked for an 621 // inode that doesn't exist, something screwed up earlier (a lookup, a 622 // cache invalidation, etc.). 623 inode := fs.getInodeOrDie(op.Inode) 624 625 if !inode.isFile() { 626 panic("Found non-file.") 627 } 628 629 return nil 630 } 631 632 func (fs *memFS) ReadFile( 633 ctx context.Context, 634 op *fuseops.ReadFileOp) error { 635 fs.mu.Lock() 636 defer fs.mu.Unlock() 637 638 // Find the inode in question. 639 inode := fs.getInodeOrDie(op.Inode) 640 641 // Serve the request. 642 var err error 643 op.BytesRead, err = inode.ReadAt(op.Dst, op.Offset) 644 645 // Don't return EOF errors; we just indicate EOF to fuse using a short read. 646 if err == io.EOF { 647 return nil 648 } 649 650 return err 651 } 652 653 func (fs *memFS) WriteFile( 654 ctx context.Context, 655 op *fuseops.WriteFileOp) error { 656 fs.mu.Lock() 657 defer fs.mu.Unlock() 658 659 // Find the inode in question. 660 inode := fs.getInodeOrDie(op.Inode) 661 662 // Serve the request. 663 _, err := inode.WriteAt(op.Data, op.Offset) 664 665 return err 666 } 667 668 func (fs *memFS) FlushFile( 669 ctx context.Context, 670 op *fuseops.FlushFileOp) (err error) { 671 if op.Metadata.Pid == 0 { 672 // FlushFileOp should have a valid pid in metadata. 673 return fuse.EINVAL 674 } 675 return 676 } 677 678 func (fs *memFS) ReadSymlink( 679 ctx context.Context, 680 op *fuseops.ReadSymlinkOp) error { 681 fs.mu.Lock() 682 defer fs.mu.Unlock() 683 684 // Find the inode in question. 685 inode := fs.getInodeOrDie(op.Inode) 686 687 // Serve the request. 688 op.Target = inode.target 689 690 return nil 691 } 692 693 func (fs *memFS) GetXattr(ctx context.Context, 694 op *fuseops.GetXattrOp) error { 695 fs.mu.Lock() 696 defer fs.mu.Unlock() 697 698 inode := fs.getInodeOrDie(op.Inode) 699 if value, ok := inode.xattrs[op.Name]; ok { 700 op.BytesRead = len(value) 701 if len(op.Dst) >= len(value) { 702 copy(op.Dst, value) 703 } else if len(op.Dst) != 0 { 704 return syscall.ERANGE 705 } 706 } else { 707 return fuse.ENOATTR 708 } 709 710 return nil 711 } 712 713 func (fs *memFS) ListXattr(ctx context.Context, 714 op *fuseops.ListXattrOp) error { 715 fs.mu.Lock() 716 defer fs.mu.Unlock() 717 718 inode := fs.getInodeOrDie(op.Inode) 719 720 dst := op.Dst[:] 721 for key := range inode.xattrs { 722 keyLen := len(key) + 1 723 724 if len(dst) >= keyLen { 725 copy(dst, key) 726 dst = dst[keyLen:] 727 } else if len(op.Dst) != 0 { 728 return syscall.ERANGE 729 } 730 op.BytesRead += keyLen 731 } 732 733 return nil 734 } 735 736 func (fs *memFS) RemoveXattr(ctx context.Context, 737 op *fuseops.RemoveXattrOp) error { 738 fs.mu.Lock() 739 defer fs.mu.Unlock() 740 inode := fs.getInodeOrDie(op.Inode) 741 742 if _, ok := inode.xattrs[op.Name]; ok { 743 delete(inode.xattrs, op.Name) 744 } else { 745 return fuse.ENOATTR 746 } 747 return nil 748 } 749 750 func (fs *memFS) SetXattr(ctx context.Context, 751 op *fuseops.SetXattrOp) error { 752 fs.mu.Lock() 753 defer fs.mu.Unlock() 754 inode := fs.getInodeOrDie(op.Inode) 755 756 _, ok := inode.xattrs[op.Name] 757 758 switch op.Flags { 759 case unix.XATTR_CREATE: 760 if ok { 761 return fuse.EEXIST 762 } 763 case unix.XATTR_REPLACE: 764 if !ok { 765 return fuse.ENOATTR 766 } 767 } 768 769 value := make([]byte, len(op.Value)) 770 copy(value, op.Value) 771 inode.xattrs[op.Name] = value 772 return nil 773 } 774 775 func (fs *memFS) Fallocate(ctx context.Context, 776 op *fuseops.FallocateOp) error { 777 fs.mu.Lock() 778 defer fs.mu.Unlock() 779 inode := fs.getInodeOrDie(op.Inode) 780 inode.Fallocate(op.Mode, op.Length, op.Length) 781 return nil 782 }