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