1/* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "Directory.h" 8 9#include <algorithm> 10#include <new> 11 12#include <AutoDeleter.h> 13 14#include "Block.h" 15#include "BlockAllocator.h" 16#include "DebugSupport.h" 17#include "Transaction.h" 18#include "Volume.h" 19 20 21class DirEntryBlock { 22public: 23 DirEntryBlock(); 24 DirEntryBlock( 25 checksumfs_dir_entry_block* entryBlock, 26 size_t entryBlockSize); 27 28 void SetTo(checksumfs_dir_entry_block* entryBlock, 29 size_t entryBlockSize); 30 31 inline int32 EntryCount() const; 32 inline size_t BytesUsedFor(int32 entryCount) const; 33 inline size_t BytesUsed() const; 34 inline size_t FreeSpace() const; 35 36 inline uint64 BlockIndexAt(int32 index) const; 37 const char* NameAt(int32 index, size_t& _nameLength) const; 38 39 int32 FindInsertionIndex(const char* name, 40 size_t nameLength, bool& _exactMatch) const; 41 42 int32 FindSplitIndex(int32 index, 43 size_t bytesNeeded) const; 44 45 void InsertEntry(int32 index, const char* name, 46 size_t nameLength, uint64 blockIndex); 47 void ReplaceEntryName(int32 index, const char* name, 48 size_t nameLength); 49 void RemoveEntry(int32 index); 50 51 void SplitBlock(int32 splitIndex, 52 DirEntryBlock& other); 53 54 bool Check() const; 55 56private: 57 checksumfs_dir_entry_block* fBlock; 58 size_t fBlockSize; 59}; 60 61 62class DirEntryTree { 63public: 64 DirEntryTree(Directory* directory); 65 66 status_t LookupEntry(const char* name, 67 uint64& _blockIndex); 68 status_t LookupNextEntry(const char* name, 69 char* foundName, size_t& _foundNameLength, 70 uint64& _blockIndex); 71 72 status_t InsertEntry(const char* name, uint64 blockIndex, 73 Transaction& transaction); 74 status_t RemoveEntry(const char* name, 75 Transaction& transaction); 76 77 status_t FreeTree(Transaction& transaction); 78 79 bool IsEmpty() const; 80 81 bool Check(); 82 83private: 84 struct LevelInfo { 85 Block block; 86 DirEntryBlock entryBlock; 87 int32 index; 88 bool exactMatch; 89 }; 90 91private: 92 status_t _InitReadOnly(); 93 status_t _InitWritable(Transaction& transaction); 94 status_t _InitCommon(); 95 96 status_t _UpdateOrInsertKey(LevelInfo* infos, 97 int32 level, const char* name, 98 size_t nameLength, uint64 blockIndex, 99 bool insertKey, Transaction& transaction); 100 101 status_t _InsertEntryIncrementDepth(LevelInfo* infos, 102 Transaction& transaction); 103 status_t _InsertEntrySplitBlock(int32 level, 104 LevelInfo& info, size_t needed, 105 Transaction& transaction, Block& newBlock, 106 int32& _splitIndex); 107 108 bool _Check(int32 level, uint64 blockIndex, 109 const char* key, size_t keyLength, 110 DirEntryBlock& entryBlock); 111 112 inline uint16 _Depth() const { return fTree->depth; } 113 114private: 115 Directory* fDirectory; 116 Block fRootBlock; 117 checksumfs_dir_entry_tree* fTree; 118 checksumfs_dir_entry_block* fRootEntryBlock; 119 size_t fRootEntryBlockSize; 120}; 121 122 123// #pragma mark - 124 125 126static int 127compare_names(const char* a, size_t lengthA, const char* b, size_t lengthB) 128{ 129 int cmp = strncmp(a, b, std::min(lengthA, lengthB)); 130 if (cmp != 0) 131 return cmp; 132 133 return (int)lengthA - (int)lengthB; 134 // assumes we don't overflow 31 bits 135} 136 137 138// #pragma mark - DirEntryBlock 139 140 141DirEntryBlock::DirEntryBlock() 142 : 143 fBlock(NULL), 144 fBlockSize(0) 145{ 146} 147 148 149DirEntryBlock::DirEntryBlock(checksumfs_dir_entry_block* entryBlock, 150 size_t entryBlockSize) 151 : 152 fBlock(entryBlock), 153 fBlockSize(entryBlockSize) 154{ 155} 156 157 158void 159DirEntryBlock::SetTo(checksumfs_dir_entry_block* entryBlock, 160 size_t entryBlockSize) 161{ 162 fBlock = entryBlock; 163 fBlockSize = entryBlockSize; 164} 165 166 167int32 168DirEntryBlock::EntryCount() const 169{ 170 return fBlock->entryCount; 171} 172 173 174size_t 175DirEntryBlock::BytesUsedFor(int32 entryCount) const 176{ 177 if (entryCount == 0) 178 return sizeof(*fBlock); 179 return sizeof(*fBlock) + 10 * entryCount 180 + fBlock->nameEnds[entryCount - 1]; 181} 182 183 184size_t 185DirEntryBlock::BytesUsed() const 186{ 187 return BytesUsedFor(EntryCount()); 188} 189 190 191size_t 192DirEntryBlock::FreeSpace() const 193{ 194 return fBlockSize - BytesUsed(); 195} 196 197 198uint64 199DirEntryBlock::BlockIndexAt(int32 index) const 200{ 201 uint64* blockIndices 202 = (uint64*)((uint8*)fBlock + fBlockSize) - 1; 203 return blockIndices[-index]; 204} 205 206 207const char* 208DirEntryBlock::NameAt(int32 index, size_t& _nameLength) const 209{ 210 int32 entryCount = EntryCount(); 211 if (index < 0 || index >= entryCount) 212 return NULL; 213 214 uint32 nameOffset = index > 0 ? fBlock->nameEnds[index - 1] : 0; 215 _nameLength = fBlock->nameEnds[index] - nameOffset; 216 return (const char*)(fBlock->nameEnds + entryCount) + nameOffset; 217} 218 219 220int32 221DirEntryBlock::FindInsertionIndex(const char* name, size_t nameLength, 222 bool& _exactMatch) const 223{ 224 int32 entryCount = EntryCount(); 225 if (entryCount == 0) { 226 _exactMatch = false; 227 return 0; 228 } 229 230 const char* entryNames = (char*)(fBlock->nameEnds + entryCount); 231 uint32 nameOffset = 0; 232 233 int32 index = 0; 234 int cmp = -1; 235 236 // TODO: Binary search! 237 for (; index < entryCount; index++) { 238 const char* entryName = entryNames + nameOffset; 239 size_t entryNameLength = fBlock->nameEnds[index] - nameOffset; 240 241 cmp = compare_names(entryName, entryNameLength, name, nameLength); 242 if (cmp >= 0) 243 break; 244 245 nameOffset += entryNameLength; 246 } 247 248 _exactMatch = cmp == 0; 249 return index; 250} 251 252 253/*! Finds a good split index for an insertion of \a bytesNeeded bytes at 254 index \a index. 255*/ 256int32 257DirEntryBlock::FindSplitIndex(int32 index, size_t bytesNeeded) const 258{ 259 size_t splitSize = (BytesUsed() + bytesNeeded) / 2; 260 261 int32 entryCount = EntryCount(); 262 for (int32 i = 0; i < entryCount; i++) { 263 size_t bytesUsed = BytesUsedFor(i + 1); 264 if (i == index) 265 bytesUsed += bytesNeeded; 266 if (bytesUsed > splitSize) 267 return i; 268 } 269 270 // This should never happen. 271 return entryCount; 272} 273 274 275void 276DirEntryBlock::InsertEntry(int32 index, const char* name, size_t nameLength, 277 uint64 blockIndex) 278{ 279 uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize) - 1; 280 int32 entryCount = fBlock->entryCount; 281 char* entryNames = (char*)(fBlock->nameEnds + entryCount); 282 283 uint32 nameOffset = index == 0 ? 0 : fBlock->nameEnds[index - 1]; 284 uint32 lastNameEnd = entryCount == 0 285 ? 0 : fBlock->nameEnds[entryCount - 1]; 286 287 if (index < entryCount) { 288 // make room in the block indices array 289 memmove(&blockIndices[-entryCount], &blockIndices[1 - entryCount], 290 8 * (entryCount - index)); 291 292 // make room in the name array -- we also move 2 bytes more for the 293 // new entry in the nameEnds array 294 memmove(entryNames + nameOffset + nameLength + 2, 295 entryNames + nameOffset, lastNameEnd - nameOffset); 296 297 // move the names < index by 2 bytes 298 if (index > 0) 299 memmove(entryNames + 2, entryNames, nameOffset); 300 301 // move and update the nameEnds entries > index 302 for (int32 i = entryCount; i > index; i--) 303 fBlock->nameEnds[i] = fBlock->nameEnds[i - 1] + nameLength; 304 } else if (entryCount > 0) { 305 // the nameEnds array grows -- move all names 306 memmove(entryNames + 2, entryNames, lastNameEnd); 307 } 308 309 // we have made room -- insert the entry 310 entryNames += 2; 311 memcpy(entryNames + nameOffset, name, nameLength); 312 fBlock->nameEnds[index] = nameOffset + nameLength; 313 blockIndices[-index] = blockIndex; 314 fBlock->entryCount++; 315ASSERT(Check()); 316} 317 318 319void 320DirEntryBlock::ReplaceEntryName(int32 index, const char* name, 321 size_t nameLength) 322{ 323 int32 entryCount = fBlock->entryCount; 324 char* entryNames = (char*)(fBlock->nameEnds + entryCount); 325 326 ASSERT(index >= 0 && index < entryCount); 327 328 uint32 nameOffset = index == 0 ? 0 : fBlock->nameEnds[index - 1]; 329 uint32 oldNameLength = fBlock->nameEnds[index] - nameOffset; 330 uint32 lastNameEnd = fBlock->nameEnds[entryCount - 1]; 331 332 if (oldNameLength != nameLength) { 333 int32 lengthDiff = (int32)nameLength - (int32)oldNameLength; 334 335 ASSERT(lengthDiff <= (int32)FreeSpace()); 336 337 // move names after the changing name 338 if (index + 1 < entryCount) { 339 memmove(entryNames + nameOffset + nameLength, 340 entryNames + nameOffset + oldNameLength, 341 lastNameEnd - nameOffset - oldNameLength); 342 } 343 344 // update the name ends 345 for (int32 i = index; i < entryCount; i++) 346 fBlock->nameEnds[i] = (int32)fBlock->nameEnds[i] + lengthDiff; 347 } 348 349 // copy the name 350 memcpy(entryNames + nameOffset, name, nameLength); 351ASSERT(Check()); 352} 353 354 355void 356DirEntryBlock::RemoveEntry(int32 index) 357{ 358 ASSERT(index >= 0 && index < EntryCount()); 359 360 int32 entryCount = EntryCount(); 361 if (entryCount == 1) { 362 // simple case -- removing the last entry 363 fBlock->entryCount = 0; 364 return; 365 } 366 367 uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize) - 1; 368 char* entryNames = (char*)(fBlock->nameEnds + entryCount); 369 370 uint32 nameOffset = index == 0 ? 0 : fBlock->nameEnds[index - 1]; 371 uint32 nameEnd = fBlock->nameEnds[index]; 372 uint32 lastNameEnd = entryCount == 0 373 ? 0 : fBlock->nameEnds[entryCount - 1]; 374 375 if (index < entryCount - 1) { 376 uint32 nameLength = nameEnd - nameOffset; 377 378 // remove the element from the block indices array 379 memmove(&blockIndices[-entryCount + 2], &blockIndices[-entryCount + 1], 380 8 * (entryCount - index - 1)); 381 382 // move and update the nameEnds entries > index 383 for (int32 i = index + 1; i < entryCount; i++) 384 fBlock->nameEnds[i - 1] = fBlock->nameEnds[i] - nameLength; 385 386 // move the names < index by 2 bytes 387 if (index > 0) 388 memmove(entryNames - 2, entryNames, nameOffset); 389 390 // move the names > index 391 memmove(entryNames - 2 + nameOffset, entryNames + nameEnd, 392 lastNameEnd - nameEnd); 393 } else { 394 // the nameEnds array shrinks -- move all names 395 memmove(entryNames - 2, entryNames, nameOffset); 396 } 397 398 // we have removed the entry 399 fBlock->entryCount--; 400ASSERT(Check()); 401} 402 403 404/*! Moves all entries beyond \a splitIndex (inclusively) to the empty block 405 \a other. 406*/ 407void 408DirEntryBlock::SplitBlock(int32 splitIndex, DirEntryBlock& other) 409{ 410 ASSERT(other.EntryCount() == 0); 411 ASSERT(splitIndex <= EntryCount()); 412 413 int32 entryCount = EntryCount(); 414 if (splitIndex == entryCount) 415 return; 416 int32 otherEntryCount = entryCount - splitIndex; 417 418 // copy block indices 419 uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize); 420 uint64* otherBlockIndices 421 = (uint64*)((uint8*)other.fBlock + other.fBlockSize); 422 // note: both point after the last entry, unlike in other methods 423 memcpy(otherBlockIndices - otherEntryCount, blockIndices - entryCount, 424 8 * otherEntryCount); 425 426 // copy the name end offsets 427 uint32 namesOffset = splitIndex > 0 428 ? fBlock->nameEnds[splitIndex - 1] : 0; 429 for (int32 i = splitIndex; i < entryCount; i++) { 430 other.fBlock->nameEnds[i - splitIndex] = fBlock->nameEnds[i] 431 - namesOffset; 432 } 433 434 // copy the names 435 char* entryNames = (char*)(fBlock->nameEnds + entryCount); 436 char* otherEntryNames 437 = (char*)(other.fBlock->nameEnds + otherEntryCount); 438 memcpy(otherEntryNames, entryNames + namesOffset, 439 fBlock->nameEnds[entryCount - 1] - namesOffset); 440 441 // the name ends array shrinks -- move the names 442 if (splitIndex > 0) { 443 char* newEntryNames = (char*)(fBlock->nameEnds + splitIndex); 444 memmove(newEntryNames, entryNames, namesOffset); 445 } 446 447 // update the entry counts 448 fBlock->entryCount = splitIndex; 449 other.fBlock->entryCount = otherEntryCount; 450ASSERT(Check()); 451ASSERT(other.Check()); 452} 453 454 455bool 456DirEntryBlock::Check() const 457{ 458 int32 entryCount = EntryCount(); 459 if (entryCount == 0) 460 return true; 461 462 // Check size: Both name ends and block index arrays must fit and we need 463 // at least one byte per name. 464 size_t size = sizeof(*fBlock) + entryCount * 10; 465 if (size + entryCount > fBlockSize) { 466 ERROR("Invalid dir entry block: entry count %d requires minimum size " 467 "of %" B_PRIuSIZE " + %d bytes, but block size is %" B_PRIuSIZE 468 "\n", (int)entryCount, size, (int)entryCount, fBlockSize); 469 return false; 470 } 471 472 // check the name ends and block indices arrays and the names 473 const char* entryNames = (char*)(fBlock->nameEnds + entryCount); 474 const uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize) - 1; 475 const char* previousName = NULL; 476 uint16 previousNameLength = 0; 477 uint16 previousEnd = 0; 478 479 for (int32 i = 0; i < entryCount; i++) { 480 // check name end 481 uint16 nameEnd = fBlock->nameEnds[i]; 482 if (nameEnd <= previousEnd || nameEnd > fBlockSize - size) { 483 ERROR("Invalid dir entry block: name end offset of entry %" B_PRId32 484 ": %u, previous: %u\n", i, nameEnd, previousEnd); 485 return false; 486 } 487 488 // check name length 489 uint16 nameLength = nameEnd - previousEnd; 490 if (nameLength > kCheckSumFSNameLength) { 491 ERROR("Invalid dir entry block: name of entry %" B_PRId32 " too " 492 "long: %u\n", i, nameLength); 493 return false; 494 } 495 496 // verify that the name doesn't contain a null char 497 const char* name = entryNames + previousEnd; 498 if (strnlen(name, nameLength) != nameLength) { 499 ERROR("Invalid dir entry block: name of entry %" B_PRId32 500 " contains a null char\n", i); 501 return false; 502 } 503 504 // compare the name with the previous name 505 if (i > 0) { 506 int cmp = compare_names(previousName, previousNameLength, name, 507 nameLength); 508 if (cmp == 0) { 509 ERROR("Invalid dir entry block: entries %" B_PRId32 "/%" 510 B_PRId32 " have the same name: \"%.*s\"\n", i - 1, i, 511 (int)nameLength, name); 512 return false; 513 } else if (cmp > 0) { 514 ERROR("Invalid dir entry block: entries %" B_PRId32 "/%" 515 B_PRId32 " out of order: \"%.*s\" > \"%.*s\"\n", i - 1, i, 516 (int)previousNameLength, previousName, (int)nameLength, 517 name); 518 return false; 519 } 520 } 521 522 // check the block index 523 if (blockIndices[-i] < kCheckSumFSSuperBlockOffset / B_PAGE_SIZE) { 524 ERROR("Invalid dir entry block: entry %" B_PRId32 525 " has invalid block index: %" B_PRIu64, i, blockIndices[-i]); 526 return false; 527 } 528 529 previousName = name; 530 previousNameLength = nameLength; 531 previousEnd = nameEnd; 532 } 533 534 return true; 535} 536 537 538// #pragma mark - DirEntryTree 539 540 541DirEntryTree::DirEntryTree(Directory* directory) 542 : 543 fDirectory(directory) 544{ 545} 546 547 548status_t 549DirEntryTree::LookupEntry(const char* name, uint64& _blockIndex) 550{ 551 FUNCTION("name: \"%s\"\n", name); 552 553 status_t error = _InitReadOnly(); 554 if (error != B_OK) 555 RETURN_ERROR(error); 556 557 size_t nameLength = strlen(name); 558 if (nameLength > kCheckSumFSNameLength) 559 RETURN_ERROR(B_ENTRY_NOT_FOUND); 560 561 uint32 depth = _Depth(); 562 563 DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize); 564ASSERT(entryBlock.Check()); 565 566 Block block; 567 568 for (uint32 level = 0; level <= depth; level++) { 569 if (entryBlock.EntryCount() == 0) 570 RETURN_ERROR(level == 0 ? B_ENTRY_NOT_FOUND : B_BAD_DATA); 571 572 bool exactMatch; 573 int32 index = entryBlock.FindInsertionIndex(name, nameLength, 574 exactMatch); 575 576 // If we haven't found an exact match, the index points to the first 577 // entry that is greater or after the last entry. 578 if (!exactMatch) { 579 if (index == 0) { 580 // The first entry is already greater, so the branch doesn't 581 // contain the entry we're looking for. 582 RETURN_ERROR(B_ENTRY_NOT_FOUND); 583 } 584 585 index--; 586 } 587 588 PRINT(" level %" B_PRId32 " -> index: %" B_PRId32 " %sexact\n", level, 589 index, exactMatch ? "" : " not "); 590 591 uint64 blockIndex = entryBlock.BlockIndexAt(index); 592 593 if (level == depth) { 594 // final level -- here we should have an exact match 595 if (!exactMatch) 596 RETURN_ERROR(B_ENTRY_NOT_FOUND); 597 598 _blockIndex = blockIndex; 599 return B_OK; 600 } 601 602 // not the final level -- load the block and descend to the next 603 // level 604 if (!block.GetReadable(fDirectory->GetVolume(), blockIndex)) 605 RETURN_ERROR(B_ERROR); 606 607 entryBlock.SetTo((checksumfs_dir_entry_block*)block.Data(), 608 B_PAGE_SIZE); 609ASSERT(entryBlock.Check()); 610 } 611 612 // cannot get here, but keep the compiler happy 613 RETURN_ERROR(B_ENTRY_NOT_FOUND); 614} 615 616 617status_t 618DirEntryTree::LookupNextEntry(const char* name, char* foundName, 619 size_t& _foundNameLength, uint64& _blockIndex) 620{ 621 FUNCTION("name: \"%s\"\n", name); 622 623 status_t error = _InitReadOnly(); 624 if (error != B_OK) 625 RETURN_ERROR(error); 626 627 size_t nameLength = strlen(name); 628 if (nameLength > kCheckSumFSNameLength) 629 RETURN_ERROR(B_ENTRY_NOT_FOUND); 630 631 int32 depth = _Depth(); 632 633 LevelInfo* infos = new(std::nothrow) LevelInfo[ 634 kCheckSumFSMaxDirEntryTreeDepth + 1]; 635 if (infos == NULL) 636 RETURN_ERROR(B_NO_MEMORY); 637 ArrayDeleter<LevelInfo> infosDeleter(infos); 638 639 infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); 640ASSERT(infos[0].entryBlock.Check()); 641 642 // descend the tree 643 for (int32 level = 0; level <= depth; level++) { 644 LevelInfo& info = infos[level]; 645 646 if (info.entryBlock.EntryCount() == 0) { 647 if (level == 0) { 648 // directory is empty 649 return B_ENTRY_NOT_FOUND; 650 } 651 652 RETURN_ERROR(B_BAD_DATA); 653 } 654 655 info.index = info.entryBlock.FindInsertionIndex(name, nameLength, 656 info.exactMatch); 657 658 PRINT(" level %" B_PRId32 " -> index: %" B_PRId32 " %sexact\n", level, 659 info.index, info.exactMatch ? "" : " not "); 660 661 if (level == depth) 662 break; 663 664 // If we haven't found an exact match, the index points to the first 665 // entry that is greater or after the last entry. 666 if (!info.exactMatch && info.index > 0) 667 info.index--; 668 669 uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); 670 671 // not the final level -- load the block and descend to the next 672 // level 673 LevelInfo& nextInfo = infos[level + 1]; 674 if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), 675 nextBlockIndex)) { 676 RETURN_ERROR(B_ERROR); 677 } 678 679 nextInfo.entryBlock.SetTo( 680 (checksumfs_dir_entry_block*)nextInfo.block.Data(), 681 B_PAGE_SIZE); 682ASSERT(nextInfo.entryBlock.Check()); 683 } 684 685 if (infos[depth].exactMatch) 686 infos[depth].index++; 687 688 if (infos[depth].index >= infos[depth].entryBlock.EntryCount()) { 689 // We're at the end of the last block -- we need to track back to find a 690 // greater branch. 691 PRINT(" searching for greater branch\n"); 692 693 int32 level; 694 for (level = depth - 1; level >= 0; level--) { 695 LevelInfo& info = infos[level]; 696 if (++info.index < info.entryBlock.EntryCount()) { 697 PRINT(" found greater branch: level: %" B_PRId32 " -> index: %" 698 B_PRId32 "\n", level, info.index); 699 break; 700 } 701 } 702 703 if (level < 0) 704 return B_ENTRY_NOT_FOUND; 705 706 // We've found a greater branch -- get the first entry in that branch. 707 for (level++; level <= depth; level++) { 708 LevelInfo& previousInfo = infos[level - 1]; 709 LevelInfo& info = infos[level]; 710 711 uint64 nextBlockIndex = previousInfo.entryBlock.BlockIndexAt( 712 previousInfo.index); 713 714 // load the block 715 if (!info.block.GetReadable(fDirectory->GetVolume(), 716 nextBlockIndex)) { 717 RETURN_ERROR(B_ERROR); 718 } 719 720 info.entryBlock.SetTo( 721 (checksumfs_dir_entry_block*)info.block.Data(), B_PAGE_SIZE); 722ASSERT(info.entryBlock.Check()); 723 724 info.index = 0; 725 if (info.entryBlock.EntryCount() == 0) 726 RETURN_ERROR(B_BAD_DATA); 727 } 728 } 729 730 // get and check the name 731 LevelInfo& info = infos[depth]; 732 733 name = info.entryBlock.NameAt(info.index, nameLength); 734 if (nameLength > kCheckSumFSNameLength 735 || strnlen(name, nameLength) != nameLength) { 736 RETURN_ERROR(B_BAD_DATA); 737 } 738 739 // set the return values 740 memcpy(foundName, name, nameLength); 741 foundName[nameLength] = '\0'; 742 _foundNameLength = nameLength; 743 _blockIndex = info.entryBlock.BlockIndexAt(info.index); 744 745 PRINT(" found entry: \"%s\" -> %" B_PRIu64 "\n", foundName, _blockIndex); 746 747 return B_OK; 748} 749 750 751status_t 752DirEntryTree::InsertEntry(const char* name, uint64 blockIndex, 753 Transaction& transaction) 754{ 755 FUNCTION("name: \"%s\", blockIndex: %" B_PRIu64 "\n", name, blockIndex); 756 757 status_t error = _InitWritable(transaction); 758 if (error != B_OK) 759 RETURN_ERROR(error); 760 761 size_t nameLength = strlen(name); 762 if (nameLength == 0) 763 RETURN_ERROR(B_BAD_VALUE); 764 if (nameLength > kCheckSumFSNameLength) 765 RETURN_ERROR(B_NAME_TOO_LONG); 766 767 int32 depth = _Depth(); 768 769 LevelInfo* infos = new(std::nothrow) LevelInfo[ 770 kCheckSumFSMaxDirEntryTreeDepth + 1]; 771 if (infos == NULL) 772 RETURN_ERROR(B_NO_MEMORY); 773 ArrayDeleter<LevelInfo> infosDeleter(infos); 774 775 infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); 776 777 for (int32 level = 0; level <= depth; level++) { 778 LevelInfo& info = infos[level]; 779 780 if (info.entryBlock.EntryCount() == 0) { 781 if (level == 0) { 782 PRINT(" directory is empty\n"); 783 // directory is empty 784 info.index = 0; 785 break; 786 } 787 788 RETURN_ERROR(B_BAD_DATA); 789 } 790 791 info.index = info.entryBlock.FindInsertionIndex(name, nameLength, 792 info.exactMatch); 793 794 PRINT(" level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32 795 " %sexact\n", level, 796 level == 0 ? fDirectory->BlockIndex() : info.block.Index(), 797 info.index, info.exactMatch ? "" : " not "); 798 799 // Finding an exact match -- even in the non-final level -- means 800 // that there's an entry with that name. 801 if (info.exactMatch) 802 RETURN_ERROR(B_FILE_EXISTS); 803 804 if (level == depth) { 805 // final level -- here we need to insert the entry 806 break; 807 } 808 809 // Since we haven't found an exact match, the index points to the 810 // first entry that is greater or after the last entry. 811 info.index--; 812 813 uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); 814 815 // not the final level -- load the block and descend to the next 816 // level 817 LevelInfo& nextInfo = infos[level + 1]; 818 if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), 819 nextBlockIndex)) { 820 RETURN_ERROR(B_ERROR); 821 } 822 823 nextInfo.entryBlock.SetTo( 824 (checksumfs_dir_entry_block*)nextInfo.block.Data(), 825 B_PAGE_SIZE); 826ASSERT(nextInfo.entryBlock.Check()); 827 } 828 829 // We've found the insertion point. Insert the key and iterate backwards 830 // to perform the potentially necessary updates. Insertion at index 0 of 831 // the block changes the block's key, requiring an update in the parent 832 // block. Insertion or key update can cause the block to be split (if 833 // there's not enough space left in it), requiring an insertion in the 834 // parent block. So we start with a pending insertion in the leaf block 835 // and work our way upwards, performing key updates and insertions as 836 // necessary. 837 838 return _UpdateOrInsertKey(infos, depth, name, nameLength, blockIndex, true, 839 transaction); 840} 841 842 843status_t 844DirEntryTree::RemoveEntry(const char* name, Transaction& transaction) 845{ 846 FUNCTION("name: \"%s\"\n", name); 847 848 status_t error = _InitWritable(transaction); 849 if (error != B_OK) 850 RETURN_ERROR(error); 851 852 size_t nameLength = strlen(name); 853 if (nameLength == 0) 854 RETURN_ERROR(B_BAD_VALUE); 855 if (nameLength > kCheckSumFSNameLength) 856 RETURN_ERROR(B_ENTRY_NOT_FOUND); 857 858 int32 depth = _Depth(); 859 860 LevelInfo* infos = new(std::nothrow) LevelInfo[ 861 kCheckSumFSMaxDirEntryTreeDepth + 1]; 862 if (infos == NULL) 863 RETURN_ERROR(B_NO_MEMORY); 864 ArrayDeleter<LevelInfo> infosDeleter(infos); 865 866 infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); 867 868 for (int32 level = 0; level <= depth; level++) { 869 LevelInfo& info = infos[level]; 870 871 if (info.entryBlock.EntryCount() == 0) { 872 if (level == 0) { 873 // directory is empty 874 PRINT(" directory is empty\n"); 875 RETURN_ERROR(B_ENTRY_NOT_FOUND); 876 } 877 878 RETURN_ERROR(B_BAD_DATA); 879 } 880 881 info.index = info.entryBlock.FindInsertionIndex(name, nameLength, 882 info.exactMatch); 883 884 PRINT(" level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32 885 " %sexact\n", level, 886 level == 0 ? fDirectory->BlockIndex() : info.block.Index(), 887 info.index, info.exactMatch ? "" : " not "); 888 889 if (level == depth) { 890 // final level -- here the entry should be found 891 if (!info.exactMatch) 892 RETURN_ERROR(B_ENTRY_NOT_FOUND); 893 break; 894 } 895 896 // If we haven't found an exact match, the index points to the first 897 // entry that is greater or after the last entry. 898 if (!info.exactMatch) { 899 if (info.index == 0) { 900 // The first entry is already greater, so the branch doesn't 901 // contain the entry we're looking for. 902 RETURN_ERROR(B_ENTRY_NOT_FOUND); 903 } 904 905 info.index--; 906 } 907 908 uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); 909 910 // not the final level -- load the block and descend to the next 911 // level 912 LevelInfo& nextInfo = infos[level + 1]; 913 if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), 914 nextBlockIndex)) { 915 RETURN_ERROR(B_ERROR); 916 } 917 918 nextInfo.entryBlock.SetTo( 919 (checksumfs_dir_entry_block*)nextInfo.block.Data(), 920 B_PAGE_SIZE); 921ASSERT(nextInfo.entryBlock.Check()); 922 } 923 924 // We've found the entry. Insert the key and iterate backwards to perform 925 // the potentially necessary updates. Removal at index 0 of the block 926 // changes the block's key, requiring an update in the parent block. 927 // Removal of the last entry will require removal of the block from its 928 // parent. Key update can cause the block to be split (if there's not 929 // enough space left in it), requiring an insertion in the parent block. 930 // We start with a pending removal in the leaf block and work our way 931 // upwards as long as the blocks become empty. As soon as a key update is 932 // required, we delegate the remaining to the update/insert backwards loop. 933 934 for (int32 level = depth; level >= 0; level--) { 935 LevelInfo& info = infos[level]; 936 937 // make the block writable 938 if (level > 0) { 939 error = info.block.MakeWritable(transaction); 940 if (error != B_OK) 941 RETURN_ERROR(error); 942 } 943 944 PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": removing key " 945 "\"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index, (int)nameLength, 946 name, nameLength); 947 948 if (info.entryBlock.EntryCount() == 1) { 949 // That's the last key in the block. Unless that's the root level, 950 // we remove the block completely. 951 PRINT(" -> block is empty\n"); 952 if (level == 0) { 953 info.entryBlock.RemoveEntry(info.index); 954 return B_OK; 955 } 956 957 error = fDirectory->GetVolume()->GetBlockAllocator()->Free( 958 info.block.Index(), 1, transaction); 959 if (error != B_OK) 960 RETURN_ERROR(error); 961 fDirectory->SetSize(fDirectory->Size() - B_PAGE_SIZE); 962 963 // remove the key (the same one) from the parent block 964 continue; 965 } 966 967 // There are more entries, so just remove the entry in question. If it 968 // is not the first one, we're done, otherwise we have to update the 969 // block's key in the parent block. 970 info.entryBlock.RemoveEntry(info.index); 971 972 if (info.index > 0 || level == 0) 973 return B_OK; 974 975 name = info.entryBlock.NameAt(0, nameLength); 976 return _UpdateOrInsertKey(infos, level - 1, name, nameLength, 0, false, 977 transaction); 978 } 979 980 return B_OK; 981} 982 983 984status_t 985DirEntryTree::FreeTree(Transaction& transaction) 986{ 987 status_t error = _InitReadOnly(); 988 if (error != B_OK) 989 RETURN_ERROR(error); 990 991 int32 depth = _Depth(); 992 if (depth == 0) 993 return B_OK; 994 995 LevelInfo* infos = new(std::nothrow) LevelInfo[ 996 kCheckSumFSMaxDirEntryTreeDepth + 1]; 997 if (infos == NULL) 998 RETURN_ERROR(B_NO_MEMORY); 999 ArrayDeleter<LevelInfo> infosDeleter(infos); 1000 1001 infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); 1002 infos[0].index = 0; 1003 1004 // Iterate through the tree in post order. We don't touch the content of 1005 // any block, we only free the blocks. 1006 int32 level = 0; 1007 while (true) { 1008 LevelInfo& info = infos[level]; 1009 1010 if (level == depth || info.index >= info.entryBlock.EntryCount()) { 1011 // we're through with the block 1012 if (level == 0) 1013 break; 1014 1015 // free it 1016 error = fDirectory->GetVolume()->GetBlockAllocator()->Free( 1017 info.block.Index(), 1, transaction); 1018 1019 // continue with the next sibling branch 1020 infos[--level].index++; 1021 } 1022 1023 // descend to next level 1024 uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); 1025 1026 LevelInfo& nextInfo = infos[++level]; 1027 if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), 1028 nextBlockIndex)) { 1029 RETURN_ERROR(B_ERROR); 1030 } 1031 1032 nextInfo.entryBlock.SetTo( 1033 (checksumfs_dir_entry_block*)nextInfo.block.Data(), 1034 B_PAGE_SIZE); 1035 } 1036 1037 return B_OK; 1038} 1039 1040 1041bool 1042DirEntryTree::IsEmpty() const 1043{ 1044 DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize); 1045 return entryBlock.EntryCount() == 0; 1046} 1047 1048 1049bool 1050DirEntryTree::Check() 1051{ 1052 if (_InitReadOnly() != B_OK) { 1053 ERROR("DirEntryTree::Check(): Init failed!\n"); 1054 return false; 1055 } 1056 1057 DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize); 1058 return _Check(0, fDirectory->BlockIndex(), NULL, 0, entryBlock); 1059} 1060 1061 1062status_t 1063DirEntryTree::_InitReadOnly() 1064{ 1065 if (!fRootBlock.GetReadable(fDirectory->GetVolume(), 1066 fDirectory->BlockIndex())) { 1067 RETURN_ERROR(B_ERROR); 1068 } 1069 1070 return _InitCommon(); 1071} 1072 1073 1074status_t 1075DirEntryTree::_InitWritable(Transaction& transaction) 1076{ 1077 if (!fRootBlock.GetWritable(fDirectory->GetVolume(), 1078 fDirectory->BlockIndex(), transaction)) { 1079 RETURN_ERROR(B_ERROR); 1080 } 1081 1082 return _InitCommon(); 1083} 1084 1085 1086status_t 1087DirEntryTree::_InitCommon() 1088{ 1089 fTree = (checksumfs_dir_entry_tree*) 1090 ((uint8*)fRootBlock.Data() + sizeof(checksumfs_node)); 1091 1092 fRootEntryBlock = (checksumfs_dir_entry_block*)(fTree + 1); 1093 fRootEntryBlockSize = B_PAGE_SIZE 1094 - ((addr_t)fRootEntryBlock - (addr_t)fRootBlock.Data()); 1095 1096 if (_Depth() > kCheckSumFSMaxDirEntryTreeDepth) 1097 RETURN_ERROR(B_BAD_DATA); 1098 1099 return B_OK; 1100} 1101 1102 1103status_t 1104DirEntryTree::_UpdateOrInsertKey(LevelInfo* infos, int32 level, 1105 const char* name, size_t nameLength, uint64 blockIndex, bool insertKey, 1106 Transaction& transaction) 1107{ 1108 FUNCTION("level: %" B_PRId32 ": %s name: \"%.*s\" (%" B_PRIuSIZE "), " 1109 "blockIndex: %" B_PRIu64 "\n", level, insertKey ? "insert" : "update", 1110 (int)nameLength, name, nameLength, blockIndex); 1111 1112 // Some temporary blocks: newBlock is used when a block is split. The 1113 // other three are used when a key update respectively insertion in the 1114 // parent block becomes necessary. We only need them, since the name 1115 // we update/insert is potentially from a block and instead of cloning 1116 // the name, we simple postpone putting the block until we don't need 1117 // the name anymore. 1118 Block newBlock; 1119 Block tempBlockUpdate; 1120 Block tempBlockUpdateInsert; 1121 Block tempBlockInsert; 1122 1123 int32 depth = _Depth(); 1124 status_t error; 1125 1126 bool updateNextKey = !insertKey; 1127 bool insertNextKey = insertKey; 1128 const char* nameToUpdate = name; 1129 size_t nameToUpdateLength = nameLength; 1130 const char* nextNameToInsert = name; 1131 size_t nextNameToInsertLength = nameLength; 1132 uint64 nextBlockIndexToInsert = blockIndex; 1133 1134 for (; level >= 0; level--) { 1135 LevelInfo& info = infos[level]; 1136 1137 bool updateThisKey = updateNextKey; 1138 bool insertThisKey = insertNextKey; 1139 1140 if (!updateThisKey && !insertThisKey) 1141 return B_OK; 1142 1143 updateNextKey = false; 1144 insertNextKey = false; 1145 1146 blockIndex = nextBlockIndexToInsert; 1147 name = nextNameToInsert; 1148 nameLength = nextNameToInsertLength; 1149 1150 // make the block writable 1151 if (level > 0) { 1152 error = info.block.MakeWritable(transaction); 1153 if (error != B_OK) 1154 RETURN_ERROR(error); 1155 } 1156 1157 if (updateThisKey) { 1158 PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": updating key " 1159 "to \"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index, 1160 (int)nameToUpdateLength, nameToUpdate, nameToUpdateLength); 1161 1162 size_t oldNameLength; 1163 info.entryBlock.NameAt(info.index, oldNameLength); 1164 size_t spaceNeeded = oldNameLength < nameToUpdateLength 1165 ? nameToUpdateLength - oldNameLength : 0; 1166 1167 if (spaceNeeded <= info.entryBlock.FreeSpace()) { 1168 info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, 1169 nameToUpdateLength); 1170 if (info.index == 0) { 1171 // we updated at index 0, so we need to update this 1172 // block's key in the parent block 1173 updateNextKey = true; 1174 nameToUpdate = info.entryBlock.NameAt(0, 1175 nameToUpdateLength); 1176 1177 // make sure the new block is kept until we no longer 1178 // use the name in the next iteration 1179 tempBlockUpdate.TransferFrom(info.block); 1180 } 1181 } else if (level == 0) { 1182 // We need to split the root block -- clone it first. 1183 error = _InsertEntryIncrementDepth(infos, transaction); 1184 if (error != B_OK) 1185 RETURN_ERROR(error); 1186 1187 level = 2; 1188 // _InsertEntryIncrementDepth() moved the root block 1189 // content to level 1, where we want to continue. 1190 updateNextKey = true; 1191 insertNextKey = insertThisKey; 1192 continue; 1193 } else { 1194 // We need to split this non-root block. 1195 int32 splitIndex; 1196 error = _InsertEntrySplitBlock(level, info, spaceNeeded, 1197 transaction, newBlock, splitIndex); 1198 if (error != B_OK) 1199 RETURN_ERROR(error); 1200 1201 nextBlockIndexToInsert = newBlock.Index(); 1202 1203 DirEntryBlock newEntryBlock( 1204 (checksumfs_dir_entry_block*)newBlock.Data(), 1205 B_PAGE_SIZE); 1206ASSERT(newEntryBlock.Check()); 1207 1208 if (info.index < splitIndex) { 1209 ASSERT(info.entryBlock.FreeSpace() >= spaceNeeded); 1210 1211 info.entryBlock.ReplaceEntryName(info.index, 1212 nameToUpdate, nameToUpdateLength); 1213 if (info.index == 0) { 1214 // we updated at index 0, so we need to update this 1215 // block's key in the parent block 1216 updateNextKey = true; 1217 nameToUpdate = info.entryBlock.NameAt(0, 1218 nameToUpdateLength); 1219 1220 // make sure the new block is kept until we no 1221 // longer use the name in the next iteration 1222 tempBlockUpdate.TransferFrom(info.block); 1223 } 1224 } else { 1225 ASSERT(newEntryBlock.FreeSpace() >= spaceNeeded); 1226 1227 // we need to transfer the block to the info, in case we 1228 // also need to insert a key below 1229 info.block.TransferFrom(newBlock); 1230 info.entryBlock.SetTo( 1231 (checksumfs_dir_entry_block*)info.block.Data(), 1232 B_PAGE_SIZE); 1233ASSERT(info.entryBlock.Check()); 1234 1235 info.index -= splitIndex; 1236 1237 info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, 1238 nameToUpdateLength); 1239 } 1240 1241 // the newly created block needs to be inserted in the 1242 // parent 1243 insertNextKey = true; 1244 nextNameToInsert = newEntryBlock.NameAt(0, 1245 nextNameToInsertLength); 1246 1247 // make sure the new block is kept until we no longer use 1248 // the name in the next iteration (might already have been 1249 // transferred to entry.block) 1250 tempBlockUpdateInsert.TransferFrom(newBlock); 1251 } 1252 } 1253 1254 if (insertThisKey) { 1255 // insert after the block we descended 1256 if (level < depth) 1257 info.index++; 1258 1259 PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": inserting key " 1260 "\"%.*s\" (%" B_PRIuSIZE "), blockIndex: %" B_PRIu64 "\n", 1261 level, info.index, (int)nameLength, name, nameLength, 1262 blockIndex); 1263 1264 if (info.entryBlock.FreeSpace() >= nameLength + 10) { 1265 info.entryBlock.InsertEntry(info.index, name, 1266 nameLength, blockIndex); 1267 if (info.index == 0) { 1268 // we inserted at index 0, so we need to update this 1269 // block's key in the parent block 1270 updateNextKey = true; 1271 nameToUpdate = info.entryBlock.NameAt(0, 1272 nameToUpdateLength); 1273 1274 // make sure the new block is kept until we no longer 1275 // use the name in the next iteration 1276 tempBlockUpdate.TransferFrom(info.block); 1277 } 1278 continue; 1279 } 1280 1281 // Not enough space left in the block -- we need to split it. 1282 ASSERT(!insertNextKey); 1283 1284 // for level == 0 we need to clone the block first 1285 if (level == 0) { 1286 error = _InsertEntryIncrementDepth(infos, transaction); 1287 if (error != B_OK) 1288 RETURN_ERROR(error); 1289 1290 level = 2; 1291 // _InsertEntryIncrementDepth() moved the root block 1292 // content to level 1, where we want to continue. 1293 updateNextKey = false; 1294 insertNextKey = true; 1295 continue; 1296 } 1297 1298 int32 splitIndex; 1299 error = _InsertEntrySplitBlock(level, info, nameLength + 10, 1300 transaction, newBlock, splitIndex); 1301 if (error != B_OK) 1302 RETURN_ERROR(error); 1303 1304 DirEntryBlock newEntryBlock( 1305 (checksumfs_dir_entry_block*)newBlock.Data(), 1306 B_PAGE_SIZE); 1307ASSERT(newEntryBlock.Check()); 1308 1309 if (info.index < splitIndex) { 1310 ASSERT(info.entryBlock.FreeSpace() >= nameLength + 10); 1311 1312 info.entryBlock.InsertEntry(info.index, name, 1313 nameLength, blockIndex); 1314 if (info.index == 0) { 1315 // we inserted at index 0, so we need to update this 1316 // block's key in the parent block 1317 updateNextKey = true; 1318 nameToUpdate = info.entryBlock.NameAt(0, 1319 nameToUpdateLength); 1320 1321 // make sure the new block is kept until we no longer 1322 // use the name in the next iteration 1323 tempBlockUpdate.TransferFrom(info.block); 1324 } 1325 } else { 1326 ASSERT(newEntryBlock.FreeSpace() >= nameLength + 10); 1327 1328 info.index -= splitIndex; 1329 1330 newEntryBlock.InsertEntry(info.index, name, nameLength, 1331 blockIndex); 1332 } 1333 1334 // the newly created block needs to be inserted in the parent 1335 insertNextKey = true; 1336 nextNameToInsert = newEntryBlock.NameAt(0, nextNameToInsertLength); 1337 nextBlockIndexToInsert = newBlock.Index(); 1338 1339 // make sure the new block is kept until we no longer use 1340 // the name in the next iteration 1341 tempBlockInsert.TransferFrom(newBlock); 1342 } 1343 } 1344 1345 return B_OK; 1346} 1347 1348 1349status_t 1350DirEntryTree::_InsertEntryIncrementDepth(LevelInfo* infos, 1351 Transaction& transaction) 1352{ 1353 FUNCTION("depth: %u -> %u\n", _Depth(), _Depth() + 1); 1354 1355 if (_Depth() >= kCheckSumFSMaxDirEntryTreeDepth) 1356 RETURN_ERROR(B_DEVICE_FULL); 1357 1358 // allocate a new block 1359 AllocatedBlock allocatedBlock( 1360 fDirectory->GetVolume()->GetBlockAllocator(), transaction); 1361 1362 status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex()); 1363 if (error != B_OK) 1364 RETURN_ERROR(error); 1365 fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE); 1366 1367 LevelInfo& newInfo = infos[1]; 1368 if (!newInfo.block.GetZero(fDirectory->GetVolume(), 1369 allocatedBlock.Index(), transaction)) { 1370 RETURN_ERROR(B_ERROR); 1371 } 1372 1373 allocatedBlock.Detach(); 1374 1375 newInfo.entryBlock.SetTo( 1376 (checksumfs_dir_entry_block*)newInfo.block.Data(), B_PAGE_SIZE); 1377ASSERT(newInfo.entryBlock.Check()); 1378 1379 // move the old root block contents to the new block 1380 LevelInfo& rootInfo = infos[0]; 1381 rootInfo.entryBlock.SplitBlock(0, newInfo.entryBlock); 1382 1383 // add an entry for the new block to the root block 1384 size_t nameLength; 1385 const char* name = newInfo.entryBlock.NameAt(0, nameLength); 1386 rootInfo.entryBlock.InsertEntry(0, name, nameLength, 1387 newInfo.block.Index()); 1388 1389 PRINT(" -> new block: %" B_PRIu64 "\n", newInfo.block.Index()); 1390 1391 newInfo.index = rootInfo.index; 1392 rootInfo.index = 0; 1393 fTree->depth++; 1394 1395 return B_OK; 1396} 1397 1398 1399status_t 1400DirEntryTree::_InsertEntrySplitBlock(int32 level, LevelInfo& info, 1401 size_t needed, Transaction& transaction, Block& newBlock, 1402 int32& _splitIndex) 1403{ 1404 int32 splitIndex = info.entryBlock.FindSplitIndex(info.index, 1405 needed); 1406 1407 FUNCTION("level: %" B_PRId32 ", size needed: %" B_PRIuSIZE ", split index: " 1408 "%" B_PRId32 "/%" B_PRId32 "\n", level, needed, splitIndex, 1409 info.entryBlock.EntryCount()); 1410 1411 // allocate a new block 1412 AllocatedBlock allocatedBlock( 1413 fDirectory->GetVolume()->GetBlockAllocator(), transaction); 1414 1415 status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex()); 1416 if (error != B_OK) 1417 RETURN_ERROR(error); 1418 fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE); 1419 1420 if (!newBlock.GetZero(fDirectory->GetVolume(), allocatedBlock.Index(), 1421 transaction)) { 1422 RETURN_ERROR(B_ERROR); 1423 } 1424 1425 allocatedBlock.Detach(); 1426 1427 // split the old block 1428 DirEntryBlock newEntryBlock( 1429 (checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE); 1430ASSERT(newEntryBlock.Check()); 1431 info.entryBlock.SplitBlock(splitIndex, newEntryBlock); 1432 1433 PRINT(" -> new block: %" B_PRIu64 "\n", newBlock.Index()); 1434 1435 _splitIndex = splitIndex; 1436 return B_OK; 1437} 1438 1439 1440bool 1441DirEntryTree::_Check(int32 level, uint64 blockIndex, const char* key, 1442 size_t keyLength, DirEntryBlock& entryBlock) 1443{ 1444 // check block for validity 1445 if (!entryBlock.Check()) { 1446 ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" 1447 B_PRIu64 " not valid!\n", level, blockIndex); 1448 return false; 1449 } 1450 1451 // The root block is allowed to be empty. For all other blocks that is an 1452 // error. 1453 uint32 entryCount = entryBlock.EntryCount(); 1454 if (entryCount == 0) { 1455 if (level == 0) 1456 return true; 1457 1458 ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" 1459 B_PRIu64 " empty!\n", level, blockIndex); 1460 return false; 1461 } 1462 1463 // Verify that the block's first entry matches the key with which the 1464 // parent block refers to it. 1465 if (level > 0) { 1466 size_t nameLength; 1467 const char* name = entryBlock.NameAt(0, nameLength); 1468 if (nameLength != keyLength || strncmp(name, key, keyLength) != 0) { 1469 ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" 1470 B_PRIu64 " key mismatch: is \"%.*s\", should be \"%.*s\"\n", 1471 level, blockIndex, (int)keyLength, key, (int)nameLength, name); 1472 return false; 1473 } 1474 } 1475 1476 if (level == _Depth()) 1477 return true; 1478 1479 // not the final level -- recurse 1480 for (uint32 i = 0; i < entryCount; i++) { 1481 size_t nameLength; 1482 const char* name = entryBlock.NameAt(i, nameLength); 1483 uint64 childBlockIndex = entryBlock.BlockIndexAt(i); 1484 1485 Block childBlock; 1486 if (!childBlock.GetReadable(fDirectory->GetVolume(), childBlockIndex)) { 1487 ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" 1488 B_PRIu64 " failed to get child block %" B_PRIu64 " (entry %" 1489 B_PRIu32 ")\n", level, blockIndex, childBlockIndex, i); 1490 } 1491 1492 DirEntryBlock childEntryBlock( 1493 (checksumfs_dir_entry_block*)childBlock.Data(), B_PAGE_SIZE); 1494 1495 if (!_Check(level + 1, childBlockIndex, name, nameLength, 1496 childEntryBlock)) { 1497 return false; 1498 } 1499 } 1500 1501 return true; 1502} 1503 1504 1505// #pragma mark - Directory 1506 1507 1508Directory::Directory(Volume* volume, uint64 blockIndex, 1509 const checksumfs_node& nodeData) 1510 : 1511 Node(volume, blockIndex, nodeData) 1512{ 1513} 1514 1515 1516Directory::Directory(Volume* volume, mode_t mode) 1517 : 1518 Node(volume, mode) 1519{ 1520} 1521 1522 1523Directory::~Directory() 1524{ 1525} 1526 1527 1528void 1529Directory::DeletingNode() 1530{ 1531 Node::DeletingNode(); 1532 1533 // iterate through the directory and remove references to all entries' nodes 1534 char* name = (char*)malloc(kCheckSumFSNameLength + 1); 1535 if (name != NULL) { 1536 name[0] = '\0'; 1537 1538 DirEntryTree entryTree(this); 1539 size_t nameLength; 1540 uint64 blockIndex; 1541 while (entryTree.LookupNextEntry(name, name, nameLength, 1542 blockIndex) == B_OK) { 1543 Node* node; 1544 if (GetVolume()->GetNode(blockIndex, node) == B_OK) { 1545 Transaction transaction(GetVolume()); 1546 if (transaction.StartAndAddNode(node) == B_OK) { 1547 node->SetHardLinks(node->HardLinks() - 1); 1548 if (node->HardLinks() == 0) 1549 GetVolume()->RemoveNode(node); 1550 1551 if (transaction.Commit() != B_OK) { 1552 ERROR("Failed to commit transaction for dereferencing " 1553 "entry node of deleted directory at %" B_PRIu64 1554 "\n", BlockIndex()); 1555 } 1556 } else { 1557 ERROR("Failed to start transaction for dereferencing " 1558 "entry node of deleted directory at %" B_PRIu64 "\n", 1559 BlockIndex()); 1560 } 1561 1562 GetVolume()->PutNode(node); 1563 } else { 1564 ERROR("Failed to get node %" B_PRIu64 " referenced by deleted " 1565 "directory at %" B_PRIu64 "\n", blockIndex, BlockIndex()); 1566 } 1567 } 1568 1569 free(name); 1570 } 1571 1572 // free the directory entry block tree 1573 Transaction transaction(GetVolume()); 1574 if (transaction.Start() != B_OK) { 1575 ERROR("Failed to start transaction for freeing entry tree of deleted " 1576 "directory at %" B_PRIu64 "\n", BlockIndex()); 1577 return; 1578 } 1579 1580 DirEntryTree entryTree(this); 1581 if (entryTree.FreeTree(transaction) != B_OK) { 1582 ERROR("Failed to freeing entry tree of deleted directory at %" B_PRIu64 1583 "\n", BlockIndex()); 1584 return; 1585 } 1586 1587 if (transaction.Commit() != B_OK) { 1588 ERROR("Failed to commit transaction for freeing entry tree of deleted " 1589 "directory at %" B_PRIu64 "\n", BlockIndex()); 1590 } 1591} 1592 1593 1594status_t 1595Directory::LookupEntry(const char* name, uint64& _blockIndex) 1596{ 1597 DirEntryTree entryTree(this); 1598 return entryTree.LookupEntry(name, _blockIndex); 1599} 1600 1601 1602status_t 1603Directory::LookupNextEntry(const char* name, char* foundName, 1604 size_t& _foundNameLength, uint64& _blockIndex) 1605{ 1606 DirEntryTree entryTree(this); 1607 return entryTree.LookupNextEntry(name, foundName, _foundNameLength, 1608 _blockIndex); 1609} 1610 1611 1612status_t 1613Directory::InsertEntry(const char* name, uint64 blockIndex, 1614 Transaction& transaction) 1615{ 1616 DirEntryTree entryTree(this); 1617 1618 status_t error = entryTree.InsertEntry(name, blockIndex, transaction); 1619 if (error == B_OK) 1620 ASSERT(entryTree.Check()); 1621 return error; 1622} 1623 1624 1625status_t 1626Directory::RemoveEntry(const char* name, Transaction& transaction, 1627 bool* _lastEntryRemoved) 1628{ 1629 DirEntryTree entryTree(this); 1630 1631 status_t error = entryTree.RemoveEntry(name, transaction); 1632 if (error != B_OK) 1633 return error; 1634 1635 ASSERT(entryTree.Check()); 1636 1637 if (_lastEntryRemoved != NULL) 1638 *_lastEntryRemoved = entryTree.IsEmpty(); 1639 1640 return B_OK; 1641} 1642