github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/inode/dir.go (about) 1 // Copyright (c) 2015-2021, NVIDIA CORPORATION. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package inode 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/swiftstack/sortedmap" 11 12 "github.com/swiftstack/ProxyFS/blunder" 13 "github.com/swiftstack/ProxyFS/headhunter" 14 "github.com/swiftstack/ProxyFS/logger" 15 "github.com/swiftstack/ProxyFS/stats" 16 "github.com/swiftstack/ProxyFS/utils" 17 ) 18 19 func (vS *volumeStruct) createRootOrSubDir(filePerm InodeMode, userID InodeUserID, groupID InodeGroupID, isRootDir bool) (dirInodeNumber InodeNumber, err error) { 20 // Create file mode out of file permissions plus inode type 21 fileMode, err := determineMode(filePerm, DirType) 22 if nil != err { 23 return 24 } 25 26 var dirInode *inMemoryInodeStruct 27 28 if isRootDir { 29 dirInode = vS.makeInMemoryInodeWithThisInodeNumber(DirType, fileMode, userID, groupID, RootDirInodeNumber, true) 30 dirInodeNumber = RootDirInodeNumber 31 } else { 32 dirInode, err = vS.makeInMemoryInode(DirType, fileMode, userID, groupID) 33 if nil != err { 34 return 35 } 36 dirInodeNumber = dirInode.InodeNumber 37 } 38 39 dirInode.dirty = true 40 41 // sorted map from directory entry name (a string) to InodeNumber 42 43 dirMapping := 44 sortedmap.NewBPlusTree( 45 vS.maxEntriesPerDirNode, 46 sortedmap.CompareString, 47 &dirInodeCallbacks{treeNodeLoadable{inode: dirInode}}, 48 globals.dirEntryCache) 49 50 ok, err := dirMapping.Put(".", dirInode.InodeNumber) 51 if (nil != err) || (!ok) { 52 panic(err) 53 } 54 55 if isRootDir { 56 ok, err = dirMapping.Put("..", dirInode.InodeNumber) 57 if (nil != err) || (!ok) { 58 panic(err) 59 } 60 61 dirInode.LinkCount = 2 62 } else { 63 dirInode.LinkCount = 1 64 } 65 66 dirInode.payload = dirMapping 67 68 if isRootDir { 69 // If creating RootDir, since this must be atomic, caller already holds vS.Mutex 70 ok, err = vS.inodeCacheInsertWhileLocked(dirInode) 71 } else { 72 ok, err = vS.inodeCacheInsert(dirInode) 73 } 74 if nil != err { 75 return 76 } 77 if !ok { 78 err = fmt.Errorf("inodeCacheInsert(dirInode) failed") 79 return 80 } 81 82 // If creating RootDir, force an immediate flush to ensure it is atomically created as well 83 if isRootDir { 84 err = vS.flushInode(dirInode) 85 if nil != err { 86 logger.ErrorfWithError(err, "createRootOrSubDir() call to flushInode() failed") 87 return 88 } 89 } 90 91 err = nil 92 return 93 } 94 95 func (vS *volumeStruct) CreateDir(filePerm InodeMode, userID InodeUserID, groupID InodeGroupID) (dirInodeNumber InodeNumber, err error) { 96 err = enforceRWMode(false) 97 if nil != err { 98 return 99 } 100 101 stats.IncrementOperations(&stats.DirCreateOps) 102 103 dirInodeNumber, err = vS.createRootOrSubDir(filePerm, userID, groupID, false) 104 105 if err == nil { 106 stats.IncrementOperations(&stats.DirCreateSuccessOps) 107 } 108 109 return 110 } 111 112 func linkInMemory(dirInode *inMemoryInodeStruct, targetInode *inMemoryInodeStruct, basename string) error { 113 dirInode.dirty = true 114 targetInode.dirty = true 115 116 dirMapping := dirInode.payload.(sortedmap.BPlusTree) 117 118 ok, err := dirMapping.Put(basename, targetInode.InodeNumber) 119 if nil != err { 120 panic(err) 121 } 122 if !ok { 123 err = fmt.Errorf("%s: failed to create link '%v' to inode %v in directory inode %v: entry exists", 124 utils.GetFnName(), basename, targetInode.InodeNumber, dirInode.InodeNumber) 125 return blunder.AddError(err, blunder.FileExistsError) 126 } 127 128 updateTime := time.Now() 129 130 targetInode.LinkCount++ 131 targetInode.AttrChangeTime = updateTime 132 133 if targetInode.InodeType == DirType && targetInode.InodeNumber != RootDirInodeNumber { 134 subdirMapping := targetInode.payload.(sortedmap.BPlusTree) 135 subdirMapping.Put("..", dirInode.InodeNumber) 136 dirInode.LinkCount++ 137 } 138 139 dirInode.AttrChangeTime = updateTime 140 dirInode.ModificationTime = updateTime 141 142 return nil 143 } 144 145 // Insert-only version of linkInMemory() 146 func linkInMemoryInsertOnly(dirInode *inMemoryInodeStruct, basename string, targetInodeNumber InodeNumber) (err error) { 147 dirInode.dirty = true 148 149 dirMapping := dirInode.payload.(sortedmap.BPlusTree) 150 151 ok, err := dirMapping.Put(basename, targetInodeNumber) 152 if nil != err { 153 panic(err) 154 } 155 if !ok { 156 err = fmt.Errorf("%s: failed to create link '%v' to inode %v in directory inode %v: entry exists", 157 utils.GetFnName(), basename, targetInodeNumber, dirInode.InodeNumber) 158 return blunder.AddError(err, blunder.FileExistsError) 159 } 160 161 updateTime := time.Now() 162 163 dirInode.AttrChangeTime = updateTime 164 dirInode.ModificationTime = updateTime 165 166 return nil 167 } 168 169 // This is used by the link(2), create(2), and mkdir(2) operations 170 // (mountstruct.Link(), mountstruct.Create(), and mountstruct.Mkdir()) 171 func (vS *volumeStruct) Link(dirInodeNumber InodeNumber, basename string, targetInodeNumber InodeNumber, insertOnly bool) (err error) { 172 var ( 173 dirInode *inMemoryInodeStruct 174 flushInodeList []*inMemoryInodeStruct 175 ok bool 176 snapShotIDType headhunter.SnapShotIDType 177 targetInode *inMemoryInodeStruct 178 ) 179 180 err = enforceRWMode(false) 181 if nil != err { 182 return 183 } 184 185 if (RootDirInodeNumber == dirInodeNumber) && (SnapShotDirName == basename) { 186 err = blunder.NewError(blunder.InvalidArgError, "Link() to /%v not allowed", SnapShotDirName) 187 return 188 } 189 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber)) 190 if headhunter.SnapShotIDTypeLive != snapShotIDType { 191 err = blunder.NewError(blunder.InvalidArgError, "Link() on non-LiveView dirInodeNumber not allowed") 192 return 193 } 194 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(targetInodeNumber)) 195 if headhunter.SnapShotIDTypeLive != snapShotIDType { 196 err = blunder.NewError(blunder.InvalidArgError, "Link() on non-LiveView targetInodeNumber not allowed") 197 return 198 } 199 200 stats.IncrementOperations(&stats.DirLinkOps) 201 202 dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType) 203 if err != nil { 204 logger.ErrorfWithError(err, "dirInode error") 205 return err 206 } 207 208 if insertOnly { 209 err = linkInMemoryInsertOnly(dirInode, basename, targetInodeNumber) 210 if err != nil { 211 return err 212 } 213 214 flushInodeList = []*inMemoryInodeStruct{dirInode} 215 } else { 216 targetInode, ok, err = vS.fetchInode(targetInodeNumber) 217 if err != nil { 218 // the inode is locked so this should never happen (unless the inode 219 // was evicted from the cache and it was corrupt when re-read from disk) 220 // (err includes volume name and inode number) 221 logger.ErrorfWithError(err, "%s: targetInode fetch error", utils.GetFnName()) 222 return err 223 } 224 if !ok { 225 // this should never happen (see above) 226 err = fmt.Errorf("%s: Link failing request to link to inode %d volume '%s' because it is unallocated", 227 utils.GetFnName(), targetInode.InodeNumber, vS.volumeName) 228 err = blunder.AddError(err, blunder.NotFoundError) 229 logger.ErrorWithError(err) 230 return err 231 } 232 233 err = linkInMemory(dirInode, targetInode, basename) 234 if err != nil { 235 return err 236 } 237 238 flushInodeList = []*inMemoryInodeStruct{dirInode, targetInode} 239 } 240 241 // REVIEW TODO: We think we need to do something more than just return an error here :-) 242 243 err = vS.flushInodes(flushInodeList) 244 if err != nil { 245 logger.ErrorWithError(err) 246 return err 247 } 248 249 stats.IncrementOperations(&stats.DirLinkSuccessOps) 250 return nil 251 } 252 253 // Manipulate a directory to remove an an entry. Like Unlink(), but without any inode loading or flushing. 254 func unlinkInMemory(dirInode *inMemoryInodeStruct, untargetInode *inMemoryInodeStruct, basename string) (toDestroyInodeNumber InodeNumber) { 255 var ( 256 dirMapping sortedmap.BPlusTree 257 err error 258 ok bool 259 updateTime time.Time 260 ) 261 262 dirMapping = dirInode.payload.(sortedmap.BPlusTree) 263 264 dirInode.dirty = true 265 untargetInode.dirty = true 266 267 ok, err = dirMapping.DeleteByKey(basename) 268 if nil != err { 269 panic(err) 270 } 271 if !ok { 272 err = fmt.Errorf("Unlink(): dirInode DeleteByKey of \"%v\" should have returned ok == true", basename) 273 panic(err) 274 } 275 276 untargetInode.LinkCount-- 277 278 if DirType == untargetInode.InodeType { 279 untargetDirMapping := untargetInode.payload.(sortedmap.BPlusTree) 280 281 ok, err = untargetDirMapping.DeleteByKey("..") 282 if nil != err { 283 panic(err) 284 } 285 if !ok { 286 err = fmt.Errorf("Unlink(): untargetInode DeleteByKey of \"..\" should have returned ok == true") 287 panic(err) 288 } 289 290 dirInode.LinkCount-- 291 292 if 1 == untargetInode.LinkCount { 293 toDestroyInodeNumber = untargetInode.InodeNumber 294 } else { 295 toDestroyInodeNumber = InodeNumber(0) 296 } 297 } else { 298 if 0 == untargetInode.LinkCount { 299 toDestroyInodeNumber = untargetInode.InodeNumber 300 } else { 301 toDestroyInodeNumber = InodeNumber(0) 302 } 303 } 304 305 updateTime = time.Now() 306 307 dirInode.AttrChangeTime = updateTime 308 dirInode.ModificationTime = updateTime 309 310 untargetInode.AttrChangeTime = updateTime 311 312 return 313 } 314 315 // Remove-only version of unlinkInMemory() 316 func unlinkInMemoryRemoveOnly(dirInode *inMemoryInodeStruct, basename string) { 317 var ( 318 dirMapping sortedmap.BPlusTree 319 err error 320 ok bool 321 updateTime time.Time 322 ) 323 324 dirMapping = dirInode.payload.(sortedmap.BPlusTree) 325 326 dirInode.dirty = true 327 328 ok, err = dirMapping.DeleteByKey(basename) 329 if nil != err { 330 panic(err) 331 } 332 if !ok { 333 err = fmt.Errorf("Unlink(): dirInode DeleteByKey of \"%v\" should have returned ok == true", basename) 334 panic(err) 335 } 336 337 updateTime = time.Now() 338 339 dirInode.AttrChangeTime = updateTime 340 dirInode.ModificationTime = updateTime 341 } 342 343 func (vS *volumeStruct) Unlink(dirInodeNumber InodeNumber, basename string, removeOnly bool) (toDestroyInodeNumber InodeNumber, err error) { 344 var ( 345 dirInode *inMemoryInodeStruct 346 flushInodeList []*inMemoryInodeStruct 347 ok bool 348 snapShotIDType headhunter.SnapShotIDType 349 untargetInode *inMemoryInodeStruct 350 untargetInodeNumber InodeNumber 351 ) 352 353 err = enforceRWMode(false) 354 if nil != err { 355 return 356 } 357 358 if (RootDirInodeNumber == dirInodeNumber) && (SnapShotDirName == basename) { 359 err = blunder.NewError(blunder.InvalidArgError, "Unlink() of /%v not allowed", SnapShotDirName) 360 return 361 } 362 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber)) 363 if headhunter.SnapShotIDTypeLive != snapShotIDType { 364 err = blunder.NewError(blunder.InvalidArgError, "Unlink() on non-LiveView dirInodeNumber not allowed") 365 return 366 } 367 368 stats.IncrementOperations(&stats.DirUnlinkOps) 369 370 dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType) 371 if nil != err { 372 return 373 } 374 375 untargetInodeNumber, err = vS.lookupByDirInodeNumber(dirInodeNumber, basename) 376 if nil != err { 377 err = blunder.AddError(err, blunder.NotFoundError) 378 return 379 } 380 381 if removeOnly { 382 unlinkInMemoryRemoveOnly(dirInode, basename) 383 384 toDestroyInodeNumber = InodeNumber(0) 385 386 flushInodeList = []*inMemoryInodeStruct{dirInode} 387 } else { 388 untargetInode, ok, err = vS.fetchInode(untargetInodeNumber) 389 if nil != err { 390 // the inode is locked so this should never happen (unless the inode 391 // was evicted from the cache and it was corrupt when re-read from disk) 392 // (err includes volume name and inode number) 393 logger.ErrorfWithError(err, "%s: fetch of target inode failed", utils.GetFnName()) 394 return 395 } 396 if !ok { 397 // this should never happen (see above) 398 err = fmt.Errorf("%s: failing request to Unlink inode %d volume '%s' because it is unallocated", 399 utils.GetFnName(), untargetInode.InodeNumber, vS.volumeName) 400 err = blunder.AddError(err, blunder.NotFoundError) 401 logger.ErrorWithError(err) 402 return 403 } 404 405 // Pre-flush untargetInode so that no time-based (implicit) flushes will occur during this transaction 406 err = vS.flushInode(untargetInode) 407 if err != nil { 408 logger.ErrorfWithError(err, "Unlink(): untargetInode flush error") 409 panic(err) 410 } 411 412 toDestroyInodeNumber = unlinkInMemory(dirInode, untargetInode, basename) 413 414 flushInodeList = []*inMemoryInodeStruct{dirInode, untargetInode} 415 } 416 417 err = vS.flushInodes(flushInodeList) 418 if err != nil { 419 logger.ErrorWithError(err) 420 return 421 } 422 423 stats.IncrementOperations(&stats.DirUnlinkSuccessOps) 424 return 425 } 426 427 func (vS *volumeStruct) Move(srcDirInodeNumber InodeNumber, srcBasename string, dstDirInodeNumber InodeNumber, dstBasename string) (toDestroyInodeNumber InodeNumber, err error) { 428 err = enforceRWMode(false) 429 if nil != err { 430 return 431 } 432 433 if (RootDirInodeNumber == srcDirInodeNumber) && (SnapShotDirName == srcBasename) { 434 err = blunder.NewError(blunder.InvalidArgError, "Move() from /%v not allowed", SnapShotDirName) 435 return 436 } 437 snapShotIDType, _, _ := vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(srcDirInodeNumber)) 438 if headhunter.SnapShotIDTypeLive != snapShotIDType { 439 err = blunder.NewError(blunder.InvalidArgError, "Move() on non-LiveView srcDirInodeNumber not allowed") 440 return 441 } 442 if (RootDirInodeNumber == dstDirInodeNumber) && (SnapShotDirName == dstBasename) { 443 err = blunder.NewError(blunder.InvalidArgError, "Move() into /%v not allowed", SnapShotDirName) 444 return 445 } 446 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dstDirInodeNumber)) 447 if headhunter.SnapShotIDTypeLive != snapShotIDType { 448 err = blunder.NewError(blunder.InvalidArgError, "Move() on non-LiveView dstDirInodeNumber not allowed") 449 return 450 } 451 452 stats.IncrementOperations(&stats.DirRenameOps) 453 454 srcDirInode, err := vS.fetchInodeType(srcDirInodeNumber, DirType) 455 if nil != err { 456 logger.ErrorfWithError(err, "Move(): srcDirInode fetch error") 457 panic(err) 458 } 459 srcDirMapping := srcDirInode.payload.(sortedmap.BPlusTree) 460 461 var dstDirInode *inMemoryInodeStruct 462 var dstDirMapping sortedmap.BPlusTree 463 if srcDirInodeNumber == dstDirInodeNumber { 464 if srcBasename == dstBasename { 465 err = fmt.Errorf("%v: Source & Target of Move() cannot be identical: %v/%v", utils.GetFnName(), srcDirInodeNumber, srcBasename) 466 logger.ErrorWithError(err) 467 err = blunder.AddError(err, blunder.FileExistsError) 468 return 469 } 470 dstDirInode = srcDirInode 471 dstDirMapping = srcDirMapping 472 } else { 473 dstDirInode, err = vS.fetchInodeType(dstDirInodeNumber, DirType) 474 if nil != err { 475 logger.ErrorfWithError(err, "Move(): dstDirInode fetch error") 476 panic(err) 477 } 478 dstDirMapping = dstDirInode.payload.(sortedmap.BPlusTree) 479 } 480 481 srcInodeNumberAsValue, ok, err := srcDirMapping.GetByKey(srcBasename) 482 if nil != err { 483 panic(err) 484 } 485 if !ok { 486 err = fmt.Errorf("%v: unable to find basename %v in dirInode %v", utils.GetFnName(), srcBasename, srcDirInodeNumber) 487 logger.ErrorWithError(err) 488 err = blunder.AddError(err, blunder.NotFoundError) 489 return 490 } 491 srcInodeNumber := srcInodeNumberAsValue.(InodeNumber) 492 493 srcInode, ok, err := vS.fetchInode(srcInodeNumber) 494 if nil != err { 495 // the inode is locked so this should never happen (unless the inode 496 // was evicted from the cache and it was corrupt when re-read from disk) 497 // (err includes volume name and inode number) 498 logger.ErrorfWithError(err, "%s: fetch of src inode failed", utils.GetFnName()) 499 return 500 } 501 if !ok { 502 // this should never happen (see above) 503 err = fmt.Errorf("%s: failing request because src inode %d volume '%s' is unallocated", 504 utils.GetFnName(), srcInode.InodeNumber, vS.volumeName) 505 err = blunder.AddError(err, blunder.NotDirError) 506 logger.ErrorWithError(err) 507 return 508 } 509 510 var dstInodeNumber InodeNumber 511 var dstInode *inMemoryInodeStruct 512 dstInodeNumberAsValue, ok, err := dstDirMapping.GetByKey(dstBasename) 513 if nil != err { 514 // this indicates disk corruption or software bug 515 logger.ErrorfWithError(err, "%s: dstDirInode GetByKey(%s) error inode %d volume '%s'", 516 utils.GetFnName(), dstBasename, dstDirInode.InodeNumber, vS.volumeName) 517 panic(err) 518 } 519 if ok { 520 dstInodeNumber = dstInodeNumberAsValue.(InodeNumber) 521 522 dstInode, ok, err = vS.fetchInode(dstInodeNumber) 523 if nil != err { 524 // this indicates disk corruption or software bug 525 // (err includes volume name and inode number) 526 logger.ErrorfWithError(err, "%s: dstInode fetch error", utils.GetFnName()) 527 panic(err) 528 } 529 if !ok { 530 // disk corruption or software bug 531 err = fmt.Errorf("%s: dstInode inode %d volume '%s' is unallocated", 532 utils.GetFnName(), dstInode.InodeNumber, vS.volumeName) 533 err = blunder.AddError(err, blunder.NotFoundError) 534 logger.ErrorWithError(err) 535 panic(err) 536 } 537 } else { 538 dstInodeNumber = InodeNumber(0) 539 dstInode = nil 540 } 541 542 // I believe this is allowed so long at the dstInode is empty --craig 543 if (nil != dstInode) && (DirType == dstInode.InodeType) { 544 err = fmt.Errorf("%v: Target of Move() is an existing directory: %v/%v", utils.GetFnName(), dstDirInodeNumber, dstBasename) 545 logger.ErrorWithError(err) 546 err = blunder.AddError(err, blunder.FileExistsError) 547 return 548 } 549 550 // All set to proceed 551 552 if FileType == srcInode.InodeType { 553 // Pre-flush srcInode so that no time-based (implicit) flushes will occur during this transaction 554 err = vS.flushInode(srcInode) 555 if err != nil { 556 logger.ErrorfWithError(err, "Move(): srcInode flush error") 557 panic(err) 558 } 559 } 560 if (nil != dstInode) && (FileType == dstInode.InodeType) { 561 // Pre-flush dstInode so that no time-based (implicit) flushes will occur during this transaction 562 err = vS.flushInode(dstInode) 563 if err != nil { 564 logger.ErrorfWithError(err, "Move(): dstInode flush error") 565 panic(err) 566 } 567 } 568 569 updateTime := time.Now() 570 571 inodes := make([]*inMemoryInodeStruct, 0, 4) 572 573 srcDirInode.dirty = true 574 srcDirInode.AttrChangeTime = updateTime 575 srcDirInode.ModificationTime = updateTime 576 inodes = append(inodes, srcDirInode) 577 578 if srcDirInodeNumber != dstDirInodeNumber { 579 dstDirInode.dirty = true 580 dstDirInode.AttrChangeTime = updateTime 581 dstDirInode.ModificationTime = updateTime 582 inodes = append(inodes, dstDirInode) 583 584 if DirType == srcInode.InodeType { 585 srcDirInode.LinkCount-- 586 dstDirInode.LinkCount++ 587 588 srcInodeAsDirMapping := srcInode.payload.(sortedmap.BPlusTree) 589 ok, err = srcInodeAsDirMapping.PatchByKey("..", dstDirInodeNumber) 590 if nil != err { 591 logger.ErrorfWithError(err, "Move(): srcInode PatchByKey error") 592 panic(err) 593 } 594 if !ok { 595 err = fmt.Errorf("Should have found \"..\" entry") 596 logger.ErrorfWithError(err, "Move(): srcInode PatchByKey error") 597 panic(err) 598 } 599 } 600 } 601 602 srcInode.dirty = true 603 srcInode.AttrChangeTime = updateTime 604 inodes = append(inodes, srcInode) 605 606 ok, err = srcDirMapping.DeleteByKey(srcBasename) 607 if nil != err { 608 logger.ErrorfWithError(err, "Move(): srcDirInode DeleteByKey error") 609 panic(err) 610 } 611 if !ok { 612 err = fmt.Errorf("Should have found \"%v\" entry", srcBasename) 613 logger.ErrorfWithError(err, "Move(): srcDirInode DeleteByKey error") 614 panic(err) 615 } 616 617 if nil == dstInode { 618 ok, err = dstDirMapping.Put(dstBasename, srcInodeNumber) 619 if nil != err { 620 logger.ErrorfWithError(err, "Move(): dstDirInode Put error") 621 panic(err) 622 } 623 if !ok { 624 err = fmt.Errorf("Should have been able to PUT \"%v\" entry", dstBasename) 625 logger.ErrorfWithError(err, "Move(): dstDirInode Put error") 626 panic(err) 627 } 628 } else { 629 dstInode.dirty = true 630 dstInode.AttrChangeTime = updateTime 631 inodes = append(inodes, dstInode) 632 633 dstInode.LinkCount-- 634 635 ok, err = dstDirMapping.PatchByKey(dstBasename, srcInodeNumber) 636 if nil != err { 637 logger.ErrorfWithError(err, "Move(): dstDirInode PatchByKey error") 638 panic(err) 639 } 640 if !ok { 641 err = fmt.Errorf("Should have been able to PatchByKey \"%v\" entry", dstBasename) 642 logger.ErrorfWithError(err, "Move(): dstDirInode PatchByKey error") 643 panic(err) 644 } 645 } 646 647 // Flush the multi-inode transaction 648 649 err = vS.flushInodes(inodes) 650 if err != nil { 651 logger.ErrorfWithError(err, "flushInodes(%v) error", inodes) 652 panic(err) 653 } 654 655 // Finally, if we decremented dstInode.LinkCount to zero, indicate dstInode should be destroyed as well 656 657 if (nil != dstInode) && (0 == dstInode.LinkCount) { 658 toDestroyInodeNumber = dstInode.InodeNumber 659 } else { 660 toDestroyInodeNumber = InodeNumber(0) 661 } 662 663 stats.IncrementOperations(&stats.DirRenameSuccessOps) 664 665 err = nil 666 return 667 } 668 669 func (vS *volumeStruct) lookupByDirInode(dirInode *inMemoryInodeStruct, basename string) (targetInodeNumber InodeNumber, err error) { 670 var ( 671 dirInodeSnapShotID uint64 672 dirMapping sortedmap.BPlusTree 673 ok bool 674 value sortedmap.Value 675 ) 676 677 _, dirInodeSnapShotID, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInode.InodeNumber)) 678 679 dirMapping = dirInode.payload.(sortedmap.BPlusTree) 680 value, ok, err = dirMapping.GetByKey(basename) 681 if nil != err { 682 panic(err) 683 } 684 if !ok { 685 err = fmt.Errorf("unable to find basename %v in dirInode @ %p", basename, dirInode) 686 // There are cases where failing to find an inode is not an error. 687 // Not logging any errors here; let the caller decide if this is log-worthy 688 err = blunder.AddError(err, blunder.NotFoundError) 689 return 690 } 691 targetInodeNumber, ok = value.(InodeNumber) 692 if !ok { 693 err = fmt.Errorf("dirMapping for basename %v in dirInode @ %p not an InodeNumber", basename, dirInode) 694 panic(err) 695 } 696 targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(dirInodeSnapShotID, uint64(targetInodeNumber))) 697 698 return 699 } 700 701 func (vS *volumeStruct) lookupByDirInodeNumber(dirInodeNumber InodeNumber, basename string) (targetInodeNumber InodeNumber, err error) { 702 var ( 703 dirInode *inMemoryInodeStruct 704 dirInodeNonce uint64 705 dirInodeSnapShotID uint64 706 dirInodeSnapShotIDType headhunter.SnapShotIDType 707 dirMapping sortedmap.BPlusTree 708 ok bool 709 value sortedmap.Value 710 snapShot headhunter.SnapShotStruct 711 ) 712 713 dirInodeSnapShotIDType, dirInodeSnapShotID, dirInodeNonce = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber)) 714 715 switch dirInodeSnapShotIDType { 716 case headhunter.SnapShotIDTypeLive: 717 if (uint64(RootDirInodeNumber) == dirInodeNonce) && 718 (SnapShotDirName == basename) && 719 (0 < vS.headhunterVolumeHandle.SnapShotCount()) { 720 // Lookup should only succeed there are any active SnapShot's 721 722 targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotTypeDotSnapShotAndNonceEncode(dirInodeNonce)) 723 err = nil 724 725 return 726 } 727 // Let normal processing perform the lookup below (SnapShotID == 0, so InodeNumber "adornment" is a no-op) 728 case headhunter.SnapShotIDTypeSnapShot: 729 // Let normal processing perform the lookup below but "adorn" resultant InodeNumber's with the dirSnapShotID 730 case headhunter.SnapShotIDTypeDotSnapShot: 731 // Currently, this is only supported in RootDirInodeNumber 732 733 if uint64(RootDirInodeNumber) != dirInodeNonce { 734 err = blunder.AddError(fmt.Errorf("%v other than in '/' not supported", SnapShotDirName), blunder.NotFoundError) 735 return 736 } 737 738 switch basename { 739 case ".": 740 targetInodeNumber = dirInodeNumber 741 err = nil 742 case "..": 743 targetInodeNumber = RootDirInodeNumber 744 err = nil 745 default: 746 // See if basename is the name for an active SnapShot 747 748 snapShot, ok = vS.headhunterVolumeHandle.SnapShotLookupByName(basename) 749 750 if ok { 751 targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShot.ID, dirInodeNonce)) 752 err = nil 753 } else { 754 err = blunder.AddError(fmt.Errorf("/%v/%v SnapShot not found", SnapShotDirName, basename), blunder.NotFoundError) 755 } 756 } 757 758 return 759 } 760 761 dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType) 762 if nil != err { 763 logger.ErrorWithError(err) 764 return 765 } 766 767 dirMapping = dirInode.payload.(sortedmap.BPlusTree) 768 value, ok, err = dirMapping.GetByKey(basename) 769 if nil != err { 770 panic(err) 771 } 772 if !ok { 773 err = fmt.Errorf("unable to find basename %v in dirInode %v", basename, dirInodeNumber) 774 // There are cases where failing to find an inode is not an error. 775 // Not logging any errors here; let the caller decide if this is log-worthy 776 err = blunder.AddError(err, blunder.NotFoundError) 777 return 778 } 779 targetInodeNumber, ok = value.(InodeNumber) 780 if !ok { 781 err = fmt.Errorf("dirMapping for basename %v in dirInode %v not an InodeNumber", basename, dirInodeNumber) 782 panic(err) 783 } 784 targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(dirInodeSnapShotID, uint64(targetInodeNumber))) 785 786 return 787 } 788 789 func (vS *volumeStruct) Lookup(dirInodeNumber InodeNumber, basename string) (targetInodeNumber InodeNumber, err error) { 790 stats.IncrementOperations(&stats.DirLookupOps) 791 792 targetInodeNumber, err = vS.lookupByDirInodeNumber(dirInodeNumber, basename) 793 794 return 795 } 796 797 func (vS *volumeStruct) NumDirEntries(dirInodeNumber InodeNumber) (numEntries uint64, err error) { 798 var ( 799 adjustNumEntriesForSnapShotSubDirInRootDirInode bool 800 dirMapping sortedmap.BPlusTree 801 dirMappingLen int 802 inode *inMemoryInodeStruct 803 snapShotCount uint64 804 snapShotIDType headhunter.SnapShotIDType 805 ) 806 807 if RootDirInodeNumber == dirInodeNumber { 808 // Account for .. in /<SnapShotDirName> if any SnapShot's exist 809 snapShotCount = vS.headhunterVolumeHandle.SnapShotCount() 810 adjustNumEntriesForSnapShotSubDirInRootDirInode = (0 != snapShotCount) 811 } else { 812 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber)) 813 if headhunter.SnapShotIDTypeDotSnapShot == snapShotIDType { 814 // numEntries == 1 ('.') + 1 ('..') + # SnapShot's 815 snapShotCount = vS.headhunterVolumeHandle.SnapShotCount() 816 numEntries = 1 + 1 + snapShotCount 817 err = nil 818 return 819 } 820 adjustNumEntriesForSnapShotSubDirInRootDirInode = false 821 } 822 823 inode, err = vS.fetchInodeType(dirInodeNumber, DirType) 824 if nil != err { 825 return 826 } 827 828 dirMapping = inode.payload.(sortedmap.BPlusTree) 829 830 dirMappingLen, err = dirMapping.Len() 831 if nil != err { 832 err = blunder.AddError(err, blunder.IOError) 833 return 834 } 835 836 if adjustNumEntriesForSnapShotSubDirInRootDirInode { 837 numEntries = uint64(dirMappingLen) + 1 838 } else { 839 numEntries = uint64(dirMappingLen) 840 } 841 842 return 843 } 844 845 // A maxEntries or maxBufSize argument of zero is interpreted to mean "no maximum". 846 func (vS *volumeStruct) ReadDir(dirInodeNumber InodeNumber, maxEntries uint64, maxBufSize uint64, prevReturned ...interface{}) (dirEntries []DirEntry, moreEntries bool, err error) { 847 var ( 848 bufSize uint64 849 dirEntryBasename string 850 dirEntryBasenameAsKey sortedmap.Key 851 dirEntryInodeNumber InodeNumber 852 dirEntryInodeNumberAsValue sortedmap.Value 853 dirIndex int 854 dirMapping sortedmap.BPlusTree 855 dirMappingLen int // If snapShotDirToBeInserted, this is 1 + dirMapping.Len() 856 dotDotInodeNumberReplacement InodeNumber // If == 0, do not replace ..'s InodeNumber 857 foundPrevReturned bool 858 inode *inMemoryInodeStruct 859 key sortedmap.Key 860 nextEntry DirEntry 861 nonce uint64 862 okGetByIndex bool 863 okKeyAsString bool 864 okPayloadBPlusTree bool 865 okPutToSnapShotListSorted bool 866 okValueAsInodeNumber bool 867 snapShotDirFound bool 868 snapShotDirIndex int // If snapShotDirToBeInserted, this is the index where it goes 869 snapShotDirToBeInserted bool // Only true in /<SnapShotDirName> 870 snapShotID uint64 871 snapShotIDType headhunter.SnapShotIDType 872 snapShotList []headhunter.SnapShotStruct 873 snapShotListElement headhunter.SnapShotStruct 874 snapShotListSorted sortedmap.LLRBTree // Will also include '.' & '..' 875 snapShotListSortedLen int 876 value sortedmap.Value 877 ) 878 879 // The following defer'd func() is useful for debugging... so leaving it in here as a comment 880 /* 881 defer func() { 882 logger.Errorf("Executed inode.ReadDir()...") 883 logger.Errorf(" dirInodeNumber: 0x%016X", dirInodeNumber) 884 logger.Errorf(" maxEntries: 0x%016X", maxEntries) 885 logger.Errorf(" maxBufSize: 0x%016X", maxBufSize) 886 switch len(prevReturned) { 887 case 0: 888 // Nothing 889 case 1: 890 logger.Errorf(" prevReturned: %v", prevReturned[0]) 891 default: 892 logger.Errorf(" len(prevReturned) [%v] should have been 0 or 1", len(prevReturned)) 893 } 894 if nil == err { 895 for dirEntriesIndex, dirEntry := range dirEntries { 896 logger.Errorf(" dirEntries[%v]:", dirEntriesIndex) 897 logger.Errorf(" InodeNumber: 0x%016X", dirEntry.InodeNumber) 898 logger.Errorf(" Basename: %s", dirEntry.Basename) 899 logger.Errorf(" InodeType: %v", dirEntry.Type) 900 logger.Errorf(" NextDirLocation: %v", dirEntry.NextDirLocation) 901 } 902 } 903 logger.Errorf(" moreEntries: %v", moreEntries) 904 logger.Errorf(" err: %v", err) 905 }() 906 */ 907 908 stats.IncrementOperations(&stats.DirReaddirOps) 909 910 dirEntries = make([]DirEntry, 0, int(maxEntries)) 911 moreEntries = false 912 913 snapShotIDType, snapShotID, nonce = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber)) 914 915 if headhunter.SnapShotIDTypeDotSnapShot == snapShotIDType { 916 if uint64(RootDirInodeNumber) != nonce { 917 err = fmt.Errorf("ReadDir() to %v not in '/' not supported", SnapShotDirName) 918 err = blunder.AddError(err, blunder.NotSupportedError) 919 return 920 } 921 922 snapShotList = vS.headhunterVolumeHandle.SnapShotListByName(false) 923 924 snapShotListSorted = sortedmap.NewLLRBTree(sortedmap.CompareString, nil) 925 926 okPutToSnapShotListSorted, err = snapShotListSorted.Put(".", dirInodeNumber) 927 if nil != err { 928 err = fmt.Errorf("ReadDir() encountered error populating SnapShotListSorted: %v", err) 929 err = blunder.AddError(err, blunder.IOError) 930 return 931 } 932 if !okPutToSnapShotListSorted { 933 err = fmt.Errorf("ReadDir() encountered !ok populating SnapShotListSorted") 934 err = blunder.AddError(err, blunder.IOError) 935 return 936 } 937 938 okPutToSnapShotListSorted, err = snapShotListSorted.Put("..", RootDirInodeNumber) 939 if nil != err { 940 err = fmt.Errorf("ReadDir() encountered error populating SnapShotListSorted: %v", err) 941 err = blunder.AddError(err, blunder.IOError) 942 return 943 } 944 if !okPutToSnapShotListSorted { 945 err = fmt.Errorf("ReadDir() encountered !ok populating SnapShotListSorted") 946 err = blunder.AddError(err, blunder.IOError) 947 return 948 } 949 950 for _, snapShotListElement = range snapShotList { 951 okPutToSnapShotListSorted, err = snapShotListSorted.Put(snapShotListElement.Name, InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShotListElement.ID, uint64(RootDirInodeNumber)))) 952 if nil != err { 953 err = fmt.Errorf("ReadDir() encountered error populating SnapShotListSorted: %v", err) 954 err = blunder.AddError(err, blunder.IOError) 955 return 956 } 957 if !okPutToSnapShotListSorted { 958 err = fmt.Errorf("ReadDir() encountered !ok populating SnapShotListSorted") 959 err = blunder.AddError(err, blunder.IOError) 960 return 961 } 962 } 963 964 switch len(prevReturned) { 965 case 0: 966 dirIndex = int(0) 967 case 1: 968 var ( 969 okAsInodeDirLocation bool 970 okAsString bool 971 prevReturnedAsInodeDirLocation InodeDirLocation 972 prevReturnedAsString string 973 ) 974 975 prevReturnedAsInodeDirLocation, okAsInodeDirLocation = prevReturned[0].(InodeDirLocation) 976 prevReturnedAsString, okAsString = prevReturned[0].(string) 977 978 if okAsInodeDirLocation { 979 if 0 > prevReturnedAsInodeDirLocation { 980 dirIndex = int(0) 981 } else { 982 dirIndex = int(prevReturnedAsInodeDirLocation + 1) 983 } 984 } else if okAsString { 985 dirIndex, foundPrevReturned, err = snapShotListSorted.BisectRight(prevReturnedAsString) 986 if nil != err { 987 err = fmt.Errorf("ReadDir() encountered error bisecting SnapShotListSorted: %v", err) 988 } 989 if foundPrevReturned { 990 dirIndex++ 991 } 992 } else { 993 err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument") 994 err = blunder.AddError(err, blunder.NotSupportedError) 995 return 996 } 997 default: 998 err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument") 999 err = blunder.AddError(err, blunder.NotSupportedError) 1000 return 1001 } 1002 1003 bufSize = 0 1004 1005 snapShotListSortedLen, err = snapShotListSorted.Len() 1006 if nil != err { 1007 err = fmt.Errorf("ReadDir() encountered error accessing SnapShotListSorted: %v", err) 1008 err = blunder.AddError(err, blunder.IOError) 1009 return 1010 } 1011 1012 for { 1013 if snapShotListSortedLen <= dirIndex { 1014 break 1015 } 1016 1017 dirEntryBasenameAsKey, dirEntryInodeNumberAsValue, okGetByIndex, err = snapShotListSorted.GetByIndex(dirIndex) 1018 if nil != err { 1019 err = fmt.Errorf("ReadDir() encountered error accessing SnapShotListSorted: %v", err) 1020 err = blunder.AddError(err, blunder.IOError) 1021 return 1022 } 1023 if !okGetByIndex { 1024 err = fmt.Errorf("ReadDir() encountered !ok accessing SnapShotListSorted: %v", err) 1025 err = blunder.AddError(err, blunder.IOError) 1026 return 1027 } 1028 1029 dirEntryBasename, okKeyAsString = dirEntryBasenameAsKey.(string) 1030 if !okKeyAsString { 1031 err = fmt.Errorf("ReadDir() encountered !ok accessing SnapShotListSorted: %v", err) 1032 err = blunder.AddError(err, blunder.IOError) 1033 return 1034 } 1035 1036 dirEntryInodeNumber, okValueAsInodeNumber = dirEntryInodeNumberAsValue.(InodeNumber) 1037 if !okValueAsInodeNumber { 1038 err = fmt.Errorf("ReadDir() encountered !ok accessing SnapShotListSorted: %v", err) 1039 err = blunder.AddError(err, blunder.IOError) 1040 return 1041 } 1042 1043 nextEntry = DirEntry{ 1044 InodeNumber: dirEntryInodeNumber, 1045 Basename: dirEntryBasename, 1046 NextDirLocation: InodeDirLocation(dirIndex) + 1, 1047 } 1048 1049 if (0 != maxEntries) && (uint64(len(dirEntries)+1) > maxEntries) { 1050 break 1051 } 1052 if (0 != maxBufSize) && ((bufSize + uint64(nextEntry.Size())) > maxBufSize) { 1053 break 1054 } 1055 1056 dirEntries = append(dirEntries, nextEntry) 1057 bufSize += uint64(nextEntry.Size()) 1058 dirIndex++ 1059 } 1060 1061 moreEntries = dirIndex < dirMappingLen 1062 1063 stats.IncrementOperationsEntriesAndBytes(stats.DirRead, uint64(len(dirEntries)), bufSize) 1064 1065 err = nil 1066 return 1067 } 1068 1069 // If we reach here, snapShotIDType is one of headhunter.SnapShotIDType{Live|SnapShotIDTypeSnapShot} 1070 1071 inode, err = vS.fetchInodeType(dirInodeNumber, DirType) 1072 if nil != err { 1073 return 1074 } 1075 1076 dirMapping, okPayloadBPlusTree = inode.payload.(sortedmap.BPlusTree) 1077 if !okPayloadBPlusTree { 1078 err = fmt.Errorf("ReadDir() found unexpected Inode Payload") 1079 err = blunder.AddError(err, blunder.IOError) 1080 return 1081 } 1082 1083 dirMappingLen, err = dirMapping.Len() 1084 if nil != err { 1085 err = blunder.AddError(err, blunder.IOError) 1086 return 1087 } 1088 1089 snapShotDirToBeInserted = false // By default, SnapShotDirName not to be inserted 1090 dotDotInodeNumberReplacement = InodeNumber(0) // By default, do not replace ..'s InodeNumber 1091 1092 if headhunter.SnapShotIDTypeLive == snapShotIDType { 1093 if RootDirInodeNumber == dirInodeNumber { 1094 if uint64(0) < vS.headhunterVolumeHandle.SnapShotCount() { 1095 // Need to find a spot to insert SnapShotDirName 1096 1097 snapShotDirIndex, snapShotDirFound, err = dirMapping.BisectRight(SnapShotDirName) 1098 if nil != err { 1099 err = blunder.AddError(err, blunder.IOError) 1100 return 1101 } 1102 if snapShotDirFound { 1103 err = fmt.Errorf("ReadDir() encountered pre-existing /%v dirEntry", SnapShotDirName) 1104 err = blunder.AddError(err, blunder.IOError) 1105 return 1106 } 1107 1108 snapShotDirToBeInserted = true 1109 dirMappingLen++ 1110 } 1111 } 1112 } else { 1113 if uint64(RootDirInodeNumber) == nonce { 1114 dotDotInodeNumberReplacement = InodeNumber(vS.headhunterVolumeHandle.SnapShotTypeDotSnapShotAndNonceEncode(uint64(RootDirInodeNumber))) 1115 } 1116 } 1117 1118 switch len(prevReturned) { 1119 case 0: 1120 dirIndex = int(0) 1121 case 1: 1122 var ( 1123 foundDoingBisectRight bool 1124 okAsInodeDirLocation bool 1125 okAsString bool 1126 prevReturnedAsInodeDirLocation InodeDirLocation 1127 prevReturnedAsString string 1128 ) 1129 1130 prevReturnedAsInodeDirLocation, okAsInodeDirLocation = prevReturned[0].(InodeDirLocation) 1131 prevReturnedAsString, okAsString = prevReturned[0].(string) 1132 1133 if okAsInodeDirLocation { 1134 if 0 > prevReturnedAsInodeDirLocation { 1135 dirIndex = int(0) 1136 } else { 1137 dirIndex = int(prevReturnedAsInodeDirLocation + 1) 1138 } 1139 } else if okAsString { 1140 if snapShotDirToBeInserted { 1141 if SnapShotDirName == prevReturnedAsString { 1142 dirIndex = snapShotDirIndex + 1 1143 } else { 1144 dirIndex, foundDoingBisectRight, err = dirMapping.BisectRight(prevReturnedAsString) 1145 if nil != err { 1146 err = blunder.AddError(err, blunder.IOError) 1147 return 1148 } 1149 if dirIndex >= snapShotDirIndex { 1150 dirIndex++ 1151 } 1152 if foundDoingBisectRight { 1153 dirIndex++ 1154 } 1155 } 1156 } else { 1157 dirIndex, foundDoingBisectRight, err = dirMapping.BisectRight(prevReturnedAsString) 1158 if nil != err { 1159 err = blunder.AddError(err, blunder.IOError) 1160 return 1161 } 1162 if foundDoingBisectRight { 1163 dirIndex++ 1164 } 1165 } 1166 } else { 1167 err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument") 1168 err = blunder.AddError(err, blunder.NotSupportedError) 1169 return 1170 } 1171 default: 1172 err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument") 1173 err = blunder.AddError(err, blunder.NotSupportedError) 1174 return 1175 } 1176 1177 bufSize = 0 1178 1179 for { 1180 if snapShotDirToBeInserted && (dirIndex == snapShotDirIndex) { 1181 dirEntryBasename = SnapShotDirName 1182 dirEntryInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotTypeDotSnapShotAndNonceEncode(uint64(RootDirInodeNumber))) 1183 } else { 1184 if snapShotDirToBeInserted { 1185 if dirIndex < snapShotDirIndex { 1186 key, value, okGetByIndex, err = dirMapping.GetByIndex(dirIndex) 1187 } else { 1188 key, value, okGetByIndex, err = dirMapping.GetByIndex(dirIndex - 1) 1189 } 1190 } else { 1191 key, value, okGetByIndex, err = dirMapping.GetByIndex(dirIndex) 1192 } 1193 if nil != err { 1194 err = blunder.AddError(err, blunder.IOError) 1195 return 1196 } 1197 if !okGetByIndex { 1198 break 1199 } 1200 1201 dirEntryBasename, okKeyAsString = key.(string) 1202 if !okKeyAsString { 1203 err = fmt.Errorf("ReadDir() encountered dirEntry with non-string Key") 1204 err = blunder.AddError(err, blunder.IOError) 1205 return 1206 } 1207 1208 if (InodeNumber(0) != dotDotInodeNumberReplacement) && (".." == dirEntryBasename) { 1209 dirEntryInodeNumber = dotDotInodeNumberReplacement 1210 } else { 1211 dirEntryInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShotID, uint64(value.(InodeNumber)))) 1212 } 1213 } 1214 1215 nextEntry = DirEntry{ 1216 InodeNumber: dirEntryInodeNumber, 1217 Basename: dirEntryBasename, 1218 NextDirLocation: InodeDirLocation(dirIndex) + 1, 1219 } 1220 1221 if (0 != maxEntries) && (uint64(len(dirEntries)+1) > maxEntries) { 1222 break 1223 } 1224 if (0 != maxBufSize) && ((bufSize + uint64(nextEntry.Size())) > maxBufSize) { 1225 break 1226 } 1227 1228 dirEntries = append(dirEntries, nextEntry) 1229 bufSize += uint64(nextEntry.Size()) 1230 1231 dirIndex++ // Consumed one dirEntry either manufactured (/<SnapShotDirName>) or from dirMapping 1232 } 1233 1234 moreEntries = dirIndex < dirMappingLen 1235 1236 stats.IncrementOperationsEntriesAndBytes(stats.DirRead, uint64(len(dirEntries)), bufSize) 1237 1238 err = nil 1239 return 1240 } 1241 1242 func (vS *volumeStruct) AddDirEntry(dirInodeNumber InodeNumber, dirEntryName string, dirEntryInodeNumber InodeNumber, skipDirLinkCountIncrementOnSubDirEntry bool, skipSettingDotDotOnSubDirEntry bool, skipDirEntryLinkCountIncrementOnNonSubDirEntry bool) (err error) { 1243 var ( 1244 dirEntryInode *inMemoryInodeStruct 1245 dirInode *inMemoryInodeStruct 1246 dirMapping sortedmap.BPlusTree 1247 ok bool 1248 snapShotIDType headhunter.SnapShotIDType 1249 ) 1250 1251 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber)) 1252 if headhunter.SnapShotIDTypeLive != snapShotIDType { 1253 err = fmt.Errorf("AddDirEntry(dirInodeNumber==0x%016X,,,,,) not allowed for snapShotIDType == %v", dirInodeNumber, snapShotIDType) 1254 return 1255 } 1256 if "" == dirEntryName { 1257 err = fmt.Errorf("AddDirEntry(,dirEntryName==\"\",,,,) not allowed") 1258 return 1259 } 1260 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirEntryInodeNumber)) 1261 if headhunter.SnapShotIDTypeLive != snapShotIDType { 1262 err = fmt.Errorf("AddDirEntry(,,dirEntryInodeNumber==0x%016X,,,) not allowed for snapShotIDType == %v", dirEntryInodeNumber, snapShotIDType) 1263 return 1264 } 1265 1266 dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType) 1267 if nil != err { 1268 err = fmt.Errorf("AddDirEntry(dirInodeNumber==0x%016X,,,,,) failed to fetch dirInode: %v", dirInodeNumber, err) 1269 return 1270 } 1271 dirEntryInode, _, err = vS.fetchInode(dirEntryInodeNumber) 1272 if nil != err { 1273 err = fmt.Errorf("AddDirEntry(,,dirEntryInodeNumber==0x%016X,,,) failed to fetch dirEntryInode: %v", dirEntryInodeNumber, err) 1274 return 1275 } 1276 1277 dirMapping = dirInode.payload.(sortedmap.BPlusTree) 1278 1279 _, ok, err = dirMapping.GetByKey(dirEntryName) 1280 if nil != err { 1281 panic(err) 1282 } 1283 if ok { 1284 err = fmt.Errorf("AddDirEntry(,dirEntryName==\"%s\",,,,) collided with pre-existing dirEntryName (case 1)", dirEntryName) 1285 return 1286 } 1287 1288 ok, err = dirMapping.Put(dirEntryName, dirEntryInodeNumber) 1289 if nil != err { 1290 panic(err) 1291 } 1292 if !ok { 1293 err = fmt.Errorf("AddDirEntry(,dirEntryName==\"%s\",,,,) collided with pre-existing dirEntryName (case 2)", dirEntryName) 1294 return 1295 } 1296 1297 if !skipDirLinkCountIncrementOnSubDirEntry && (DirType == dirEntryInode.InodeType) { 1298 dirInode.onDiskInodeV1Struct.LinkCount++ 1299 } 1300 1301 if !skipSettingDotDotOnSubDirEntry && (DirType == dirEntryInode.InodeType) { 1302 dirMapping = dirEntryInode.payload.(sortedmap.BPlusTree) 1303 1304 ok, err = dirMapping.PatchByKey("..", dirInodeNumber) 1305 if nil != err { 1306 panic(err) 1307 } 1308 if !ok { 1309 _, err = dirMapping.Put("..", dirInodeNumber) 1310 if nil != err { 1311 panic(err) 1312 } 1313 } 1314 } 1315 1316 if !skipDirEntryLinkCountIncrementOnNonSubDirEntry && (DirType != dirEntryInode.InodeType) { 1317 dirEntryInode.onDiskInodeV1Struct.LinkCount++ 1318 } 1319 1320 err = nil 1321 return 1322 } 1323 1324 func (vS *volumeStruct) ReplaceDirEntries(parentDirInodeNumber InodeNumber, parentDirEntryBasename string, dirInodeNumber InodeNumber, dirEntryInodeNumbers []InodeNumber) (err error) { 1325 var ( 1326 dirEntryBasename string 1327 dirEntryInodeNumber InodeNumber 1328 dirEntryIndex int 1329 dirEntryInode *inMemoryInodeStruct 1330 dirEntryInodes []*inMemoryInodeStruct 1331 dirInode *inMemoryInodeStruct 1332 dirLinkCount uint64 1333 dirMapping sortedmap.BPlusTree 1334 ok bool 1335 parentDirEntryInodeNumber InodeNumber 1336 parentDirInode *inMemoryInodeStruct 1337 snapShotIDType headhunter.SnapShotIDType 1338 ) 1339 1340 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(parentDirInodeNumber)) 1341 if headhunter.SnapShotIDTypeLive != snapShotIDType { 1342 err = fmt.Errorf("ReplaceDirEntries(parentDirInodeNumber==0x%016X,,) not allowed for snapShotIDType == %v", parentDirInodeNumber, snapShotIDType) 1343 return 1344 } 1345 1346 parentDirInode, err = vS.fetchInodeType(parentDirInodeNumber, DirType) 1347 if nil != err { 1348 err = fmt.Errorf("ReplaceDirEntries() cannot find parentDirInodeNumber==0x%016X: %v", parentDirInodeNumber, err) 1349 return 1350 } 1351 1352 parentDirEntryInodeNumber, err = vS.lookupByDirInode(parentDirInode, parentDirEntryBasename) 1353 if nil != err { 1354 err = fmt.Errorf("ReplaceDirEntries() lookup in parentDirInodeNumber==0x%016X at basename=\"%s\" failed: %v", parentDirInodeNumber, parentDirEntryBasename, err) 1355 return 1356 } 1357 if parentDirEntryInodeNumber != dirInodeNumber { 1358 err = fmt.Errorf("ReplaceDirEntries() lookup in parentDirInodeNumber==0x%016X at basename=\"%s\" returned unexpected parentDirEntryInodeNumber (0x%016X)... expected dirInodeNumber (0x%016X)", parentDirInodeNumber, parentDirEntryBasename, parentDirEntryInodeNumber, dirInodeNumber) 1359 return 1360 } 1361 1362 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber)) 1363 if headhunter.SnapShotIDTypeLive != snapShotIDType { 1364 err = fmt.Errorf("ReplaceDirEntries(,dirInodeNumber==0x%016X,) not allowed for snapShotIDType == %v", dirInodeNumber, snapShotIDType) 1365 return 1366 } 1367 1368 dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType) 1369 if nil != err { 1370 err = fmt.Errorf("ReplaceDirEntries() cannot find dirInodeNumber==0x%016X: %v", dirInodeNumber, err) 1371 return 1372 } 1373 1374 dirEntryInodes = make([]*inMemoryInodeStruct, len(dirEntryInodeNumbers)) 1375 1376 for dirEntryIndex, dirEntryInodeNumber = range dirEntryInodeNumbers { 1377 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirEntryInodeNumber)) 1378 if headhunter.SnapShotIDTypeLive != snapShotIDType { 1379 err = fmt.Errorf("ReplaceDirEntries(,,dirEntryInodeNumbers[%v]==0x%016X) not allowed for snapShotIDType == %v", dirEntryIndex, dirEntryInodeNumber, snapShotIDType) 1380 return 1381 } 1382 1383 dirEntryInodes[dirEntryIndex], ok, err = vS.fetchInode(dirEntryInodeNumber) 1384 if (nil != err) || !ok { 1385 err = fmt.Errorf("ReplaceDirEntries() cannot find dirEntryInodeNumbers[%v]==0x%016X: %v", dirEntryIndex, dirInodeNumber, err) 1386 return 1387 } 1388 } 1389 1390 dirMapping = sortedmap.NewBPlusTree(vS.maxEntriesPerDirNode, sortedmap.CompareString, &dirInodeCallbacks{treeNodeLoadable{inode: dirInode}}, globals.dirEntryCache) 1391 1392 ok, err = dirMapping.Put(".", dirInodeNumber) 1393 if (nil != err) || !ok { 1394 panic(err) 1395 } 1396 1397 ok, err = dirMapping.Put("..", parentDirInodeNumber) 1398 if (nil != err) || !ok { 1399 panic(err) 1400 } 1401 1402 dirLinkCount = 2 1403 1404 for _, dirEntryInode = range dirEntryInodes { 1405 dirEntryBasename = fmt.Sprintf("%016X", dirEntryInode.InodeNumber) 1406 1407 ok, err = dirMapping.Put(dirEntryBasename, dirEntryInode.InodeNumber) 1408 if (nil != err) || !ok { 1409 panic(err) 1410 } 1411 1412 if DirType == dirEntryInode.InodeType { 1413 dirLinkCount++ 1414 } 1415 } 1416 1417 dirInode.payload = dirMapping 1418 dirInode.onDiskInodeV1Struct.LinkCount = dirLinkCount 1419 1420 dirInode.dirty = true 1421 1422 err = vS.flushInode(dirInode) 1423 1424 return 1425 }