static status_t checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name, ino_t* _id) { Volume* volume = (Volume*)fsVolume->private_volume; Node* node = (Node*)fsDir->private_node; Directory* directory = dynamic_cast<Directory*>(node); if (directory == NULL) return B_NOT_A_DIRECTORY; uint64 blockIndex; if (strcmp(name, ".") == 0) { blockIndex = directory->BlockIndex(); } else if (strcmp(name, "..") == 0) { blockIndex = directory->ParentDirectory(); } else { // TODO: Implement! return B_ENTRY_NOT_FOUND; } // get the node Node* childNode; status_t error = volume->GetNode(blockIndex, childNode); if (error != B_OK) return error; *_id = blockIndex; return B_OK; }
status_t DirEntryTree::_InitWritable(Transaction& transaction) { if (!fRootBlock.GetWritable(fDirectory->GetVolume(), fDirectory->BlockIndex(), transaction)) { RETURN_ERROR(B_ERROR); } return _InitCommon(); }
status_t DirEntryTree::_InitReadOnly() { if (!fRootBlock.GetReadable(fDirectory->GetVolume(), fDirectory->BlockIndex())) { RETURN_ERROR(B_ERROR); } return _InitCommon(); }
bool DirEntryTree::Check() { if (_InitReadOnly() != B_OK) { ERROR("DirEntryTree::Check(): Init failed!\n"); return false; } DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize); return _Check(0, fDirectory->BlockIndex(), NULL, 0, entryBlock); }
status_t DirEntryTree::_InsertEntryIncrementDepth(LevelInfo* infos, Transaction& transaction) { FUNCTION("depth: %u -> %u\n", _Depth(), _Depth() + 1); if (_Depth() >= kCheckSumFSMaxDirEntryTreeDepth) RETURN_ERROR(B_DEVICE_FULL); // allocate a new block AllocatedBlock allocatedBlock( fDirectory->GetVolume()->GetBlockAllocator(), transaction); status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex()); if (error != B_OK) RETURN_ERROR(error); fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE); LevelInfo& newInfo = infos[1]; if (!newInfo.block.GetZero(fDirectory->GetVolume(), allocatedBlock.Index(), transaction)) { RETURN_ERROR(B_ERROR); } allocatedBlock.Detach(); newInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)newInfo.block.Data(), B_PAGE_SIZE); ASSERT(newInfo.entryBlock.Check()); // move the old root block contents to the new block LevelInfo& rootInfo = infos[0]; rootInfo.entryBlock.SplitBlock(0, newInfo.entryBlock); // add an entry for the new block to the root block size_t nameLength; const char* name = newInfo.entryBlock.NameAt(0, nameLength); rootInfo.entryBlock.InsertEntry(0, name, nameLength, newInfo.block.Index()); PRINT(" -> new block: %" B_PRIu64 "\n", newInfo.block.Index()); newInfo.index = rootInfo.index; rootInfo.index = 0; fTree->depth++; return B_OK; }
status_t DirEntryTree::_InsertEntrySplitBlock(int32 level, LevelInfo& info, size_t needed, Transaction& transaction, Block& newBlock, int32& _splitIndex) { int32 splitIndex = info.entryBlock.FindSplitIndex(info.index, needed); FUNCTION("level: %" B_PRId32 ", size needed: %" B_PRIuSIZE ", split index: " "%" B_PRId32 "/%" B_PRId32 "\n", level, needed, splitIndex, info.entryBlock.EntryCount()); // allocate a new block AllocatedBlock allocatedBlock( fDirectory->GetVolume()->GetBlockAllocator(), transaction); status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex()); if (error != B_OK) RETURN_ERROR(error); fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE); if (!newBlock.GetZero(fDirectory->GetVolume(), allocatedBlock.Index(), transaction)) { RETURN_ERROR(B_ERROR); } allocatedBlock.Detach(); // split the old block DirEntryBlock newEntryBlock( (checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE); ASSERT(newEntryBlock.Check()); info.entryBlock.SplitBlock(splitIndex, newEntryBlock); PRINT(" -> new block: %" B_PRIu64 "\n", newBlock.Index()); _splitIndex = splitIndex; return B_OK; }
status_t ReadNextEntry(struct dirent* buffer, size_t size, uint32& _countRead) { const char* name; uint64 blockIndex; int nextIterationState = OTHERS; switch (iterationState) { case DOT: name = "."; blockIndex = directory->BlockIndex(); nextIterationState = DOT_DOT; break; case DOT_DOT: name = ".."; blockIndex = directory->ParentDirectory(); break; default: // TODO: Implement! _countRead = 0; return B_OK; } size_t entrySize = sizeof(dirent) + strlen(name); if (entrySize > size) return B_BUFFER_OVERFLOW; buffer->d_dev = directory->GetVolume()->ID(); buffer->d_ino = blockIndex; buffer->d_reclen = entrySize; strcpy(buffer->d_name, name); iterationState = nextIterationState; _countRead = 1; return B_OK; }
status_t DirEntryTree::RemoveEntry(const char* name, Transaction& transaction) { FUNCTION("name: \"%s\"\n", name); status_t error = _InitWritable(transaction); if (error != B_OK) RETURN_ERROR(error); size_t nameLength = strlen(name); if (nameLength == 0) RETURN_ERROR(B_BAD_VALUE); if (nameLength > kCheckSumFSNameLength) RETURN_ERROR(B_ENTRY_NOT_FOUND); int32 depth = _Depth(); LevelInfo* infos = new(std::nothrow) LevelInfo[ kCheckSumFSMaxDirEntryTreeDepth + 1]; if (infos == NULL) RETURN_ERROR(B_NO_MEMORY); ArrayDeleter<LevelInfo> infosDeleter(infos); infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); for (int32 level = 0; level <= depth; level++) { LevelInfo& info = infos[level]; if (info.entryBlock.EntryCount() == 0) { if (level == 0) { // directory is empty PRINT(" directory is empty\n"); RETURN_ERROR(B_ENTRY_NOT_FOUND); } RETURN_ERROR(B_BAD_DATA); } info.index = info.entryBlock.FindInsertionIndex(name, nameLength, info.exactMatch); PRINT(" level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32 " %sexact\n", level, level == 0 ? fDirectory->BlockIndex() : info.block.Index(), info.index, info.exactMatch ? "" : " not "); if (level == depth) { // final level -- here the entry should be found if (!info.exactMatch) RETURN_ERROR(B_ENTRY_NOT_FOUND); break; } // If we haven't found an exact match, the index points to the first // entry that is greater or after the last entry. if (!info.exactMatch) { if (info.index == 0) { // The first entry is already greater, so the branch doesn't // contain the entry we're looking for. RETURN_ERROR(B_ENTRY_NOT_FOUND); } info.index--; } uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); // not the final level -- load the block and descend to the next // level LevelInfo& nextInfo = infos[level + 1]; if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), nextBlockIndex)) { RETURN_ERROR(B_ERROR); } nextInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)nextInfo.block.Data(), B_PAGE_SIZE); ASSERT(nextInfo.entryBlock.Check()); } // We've found the entry. Insert the key and iterate backwards to perform // the potentially necessary updates. Removal at index 0 of the block // changes the block's key, requiring an update in the parent block. // Removal of the last entry will require removal of the block from its // parent. Key update can cause the block to be split (if there's not // enough space left in it), requiring an insertion in the parent block. // We start with a pending removal in the leaf block and work our way // upwards as long as the blocks become empty. As soon as a key update is // required, we delegate the remaining to the update/insert backwards loop. for (int32 level = depth; level >= 0; level--) { LevelInfo& info = infos[level]; // make the block writable if (level > 0) { error = info.block.MakeWritable(transaction); if (error != B_OK) RETURN_ERROR(error); } PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": removing key " "\"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index, (int)nameLength, name, nameLength); if (info.entryBlock.EntryCount() == 1) { // That's the last key in the block. Unless that's the root level, // we remove the block completely. PRINT(" -> block is empty\n"); if (level == 0) { info.entryBlock.RemoveEntry(info.index); return B_OK; } error = fDirectory->GetVolume()->GetBlockAllocator()->Free( info.block.Index(), 1, transaction); if (error != B_OK) RETURN_ERROR(error); fDirectory->SetSize(fDirectory->Size() - B_PAGE_SIZE); // remove the key (the same one) from the parent block continue; } // There are more entries, so just remove the entry in question. If it // is not the first one, we're done, otherwise we have to update the // block's key in the parent block. info.entryBlock.RemoveEntry(info.index); if (info.index > 0 || level == 0) return B_OK; name = info.entryBlock.NameAt(0, nameLength); return _UpdateOrInsertKey(infos, level - 1, name, nameLength, 0, false, transaction); } return B_OK; }
status_t DirEntryTree::InsertEntry(const char* name, uint64 blockIndex, Transaction& transaction) { FUNCTION("name: \"%s\", blockIndex: %" B_PRIu64 "\n", name, blockIndex); status_t error = _InitWritable(transaction); if (error != B_OK) RETURN_ERROR(error); size_t nameLength = strlen(name); if (nameLength == 0) RETURN_ERROR(B_BAD_VALUE); if (nameLength > kCheckSumFSNameLength) RETURN_ERROR(B_NAME_TOO_LONG); int32 depth = _Depth(); LevelInfo* infos = new(std::nothrow) LevelInfo[ kCheckSumFSMaxDirEntryTreeDepth + 1]; if (infos == NULL) RETURN_ERROR(B_NO_MEMORY); ArrayDeleter<LevelInfo> infosDeleter(infos); infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize); for (int32 level = 0; level <= depth; level++) { LevelInfo& info = infos[level]; if (info.entryBlock.EntryCount() == 0) { if (level == 0) { PRINT(" directory is empty\n"); // directory is empty info.index = 0; break; } RETURN_ERROR(B_BAD_DATA); } info.index = info.entryBlock.FindInsertionIndex(name, nameLength, info.exactMatch); PRINT(" level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32 " %sexact\n", level, level == 0 ? fDirectory->BlockIndex() : info.block.Index(), info.index, info.exactMatch ? "" : " not "); // Finding an exact match -- even in the non-final level -- means // that there's an entry with that name. if (info.exactMatch) RETURN_ERROR(B_FILE_EXISTS); if (level == depth) { // final level -- here we need to insert the entry break; } // Since we haven't found an exact match, the index points to the // first entry that is greater or after the last entry. info.index--; uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index); // not the final level -- load the block and descend to the next // level LevelInfo& nextInfo = infos[level + 1]; if (!nextInfo.block.GetReadable(fDirectory->GetVolume(), nextBlockIndex)) { RETURN_ERROR(B_ERROR); } nextInfo.entryBlock.SetTo( (checksumfs_dir_entry_block*)nextInfo.block.Data(), B_PAGE_SIZE); ASSERT(nextInfo.entryBlock.Check()); } // We've found the insertion point. Insert the key and iterate backwards // to perform the potentially necessary updates. Insertion at index 0 of // the block changes the block's key, requiring an update in the parent // block. Insertion or key update can cause the block to be split (if // there's not enough space left in it), requiring an insertion in the // parent block. So we start with a pending insertion in the leaf block // and work our way upwards, performing key updates and insertions as // necessary. return _UpdateOrInsertKey(infos, depth, name, nameLength, blockIndex, true, transaction); }