status_t DirEntryTree::_InitCommon() { fTree = (checksumfs_dir_entry_tree*) ((uint8*)fRootBlock.Data() + sizeof(checksumfs_node)); fRootEntryBlock = (checksumfs_dir_entry_block*)(fTree + 1); fRootEntryBlockSize = B_PAGE_SIZE - ((addr_t)fRootEntryBlock - (addr_t)fRootBlock.Data()); if (_Depth() > kCheckSumFSMaxDirEntryTreeDepth) RETURN_ERROR(B_BAD_DATA); return B_OK; }
status_t SymLink::WriteSymLink(const char* buffer, size_t toWrite, Transaction& transaction) { uint64 size = Size(); if (size > kMaxSymLinkSize) RETURN_ERROR(B_BAD_DATA); if (toWrite > kMaxSymLinkSize) RETURN_ERROR(B_NAME_TOO_LONG); if (toWrite == 0) { SetSize(0); return B_OK; } Block block; if (!block.GetWritable(GetVolume(), BlockIndex(), transaction)) return B_ERROR; char* data = (char*)block.Data() + kSymLinkDataOffset; memcpy(data, buffer, toWrite); SetSize(toWrite); return B_OK; }
status_t SymLink::ReadSymLink(char* buffer, size_t toRead, size_t& _bytesRead) { uint64 size = Size(); if (size > kMaxSymLinkSize) RETURN_ERROR(B_BAD_DATA); if (toRead > size) toRead = size; if (toRead == 0) { _bytesRead = 0; return B_OK; } // get the block Block block; if (!block.GetReadable(GetVolume(), BlockIndex())) RETURN_ERROR(B_ERROR); const char* data = (char*)block.Data() + kSymLinkDataOffset; memcpy(buffer, data, toRead); _bytesRead = toRead; return B_OK; }
status_t Volume::ReadNode(uint64 blockIndex, Node*& _node) { if (blockIndex == 0 || blockIndex >= fTotalBlocks) return B_BAD_VALUE; // load the node's block Block block; if (!block.GetReadable(this, blockIndex)) return B_ERROR; checksumfs_node* nodeData = (checksumfs_node*)block.Data(); // create the Node object Node* node; switch (nodeData->mode & S_IFMT) { // TODO: Don't directly access mode! case S_IFDIR: node = new(std::nothrow) Directory(this, blockIndex, *nodeData); break; default: node = new(std::nothrow) Node(this, blockIndex, *nodeData); break; } if (node == NULL) return B_NO_MEMORY; // TODO: Sanity check the node! _node = node; return B_OK; }
status_t Volume::Initialize(const char* name) { fName = strdup(name); if (fName == NULL) return B_NO_MEMORY; status_t error = fBlockAllocator->Initialize(); if (error != B_OK) return error; // create the root directory error = CreateDirectory(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, fRootDirectory); if (error != B_OK) return error; error = fRootDirectory->Flush(); if (error != B_OK) return error; // write the super block Block block; if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE)) return B_ERROR; SuperBlock* superBlock = (SuperBlock*)block.Data(); superBlock->Initialize(this); block.Put(); return block_cache_sync(fBlockCache); }
status_t Node::Flush() { if (!fNodeDataDirty) return B_OK; Block block; if (!block.GetWritable(fVolume, fBlockIndex)) return B_ERROR; memcpy(block.Data(), &fNode, sizeof(fNode)); fNodeDataDirty = false; return B_OK; }
status_t Volume::Mount(fs_volume* fsVolume) { fFSVolume = fsVolume; // load the superblock Block block; if (!block.GetReadable(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE)) RETURN_ERROR(B_ERROR); SuperBlock* superBlock = (SuperBlock*)block.Data(); if (!superBlock->Check(fTotalBlocks)) RETURN_ERROR(B_BAD_DATA); // copy the volume name fName = strdup(superBlock->Name()); if (fName == NULL) RETURN_ERROR(B_NO_MEMORY); // init the block allocator status_t error = fBlockAllocator->Init(superBlock->BlockBitmap(), superBlock->FreeBlocks()); if (error != B_OK) RETURN_ERROR(error); // load the root directory Node* rootNode; error = ReadNode(superBlock->RootDirectory(), rootNode); if (error != B_OK) RETURN_ERROR(error); fRootDirectory = dynamic_cast<Directory*>(rootNode); if (fRootDirectory == NULL) { delete rootNode; RETURN_ERROR(B_BAD_DATA); } error = PublishNode(fRootDirectory, 0); if (error != B_OK) { delete fRootDirectory; fRootDirectory = NULL; return error; } 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 DirEntryTree::LookupEntry(const char* name, uint64& _blockIndex) { FUNCTION("name: \"%s\"\n", name); status_t error = _InitReadOnly(); if (error != B_OK) RETURN_ERROR(error); size_t nameLength = strlen(name); if (nameLength > kCheckSumFSNameLength) RETURN_ERROR(B_ENTRY_NOT_FOUND); uint32 depth = _Depth(); DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize); ASSERT(entryBlock.Check()); Block block; for (uint32 level = 0; level <= depth; level++) { if (entryBlock.EntryCount() == 0) RETURN_ERROR(level == 0 ? B_ENTRY_NOT_FOUND : B_BAD_DATA); bool exactMatch; int32 index = entryBlock.FindInsertionIndex(name, nameLength, exactMatch); // If we haven't found an exact match, the index points to the first // entry that is greater or after the last entry. if (!exactMatch) { if (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); } index--; } PRINT(" level %" B_PRId32 " -> index: %" B_PRId32 " %sexact\n", level, index, exactMatch ? "" : " not "); uint64 blockIndex = entryBlock.BlockIndexAt(index); if (level == depth) { // final level -- here we should have an exact match if (!exactMatch) RETURN_ERROR(B_ENTRY_NOT_FOUND); _blockIndex = blockIndex; return B_OK; } // not the final level -- load the block and descend to the next // level if (!block.GetReadable(fDirectory->GetVolume(), blockIndex)) RETURN_ERROR(B_ERROR); entryBlock.SetTo((checksumfs_dir_entry_block*)block.Data(), B_PAGE_SIZE); ASSERT(entryBlock.Check()); } // cannot get here, but keep the compiler happy RETURN_ERROR(B_ENTRY_NOT_FOUND); }
bool DirEntryTree::_Check(int32 level, uint64 blockIndex, const char* key, size_t keyLength, DirEntryBlock& entryBlock) { // check block for validity if (!entryBlock.Check()) { ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " not valid!\n", level, blockIndex); return false; } // The root block is allowed to be empty. For all other blocks that is an // error. uint32 entryCount = entryBlock.EntryCount(); if (entryCount == 0) { if (level == 0) return true; ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " empty!\n", level, blockIndex); return false; } // Verify that the block's first entry matches the key with which the // parent block refers to it. if (level > 0) { size_t nameLength; const char* name = entryBlock.NameAt(0, nameLength); if (nameLength != keyLength || strncmp(name, key, keyLength) != 0) { ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " key mismatch: is \"%.*s\", should be \"%.*s\"\n", level, blockIndex, (int)keyLength, key, (int)nameLength, name); return false; } } if (level == _Depth()) return true; // not the final level -- recurse for (uint32 i = 0; i < entryCount; i++) { size_t nameLength; const char* name = entryBlock.NameAt(i, nameLength); uint64 childBlockIndex = entryBlock.BlockIndexAt(i); Block childBlock; if (!childBlock.GetReadable(fDirectory->GetVolume(), childBlockIndex)) { ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %" B_PRIu64 " failed to get child block %" B_PRIu64 " (entry %" B_PRIu32 ")\n", level, blockIndex, childBlockIndex, i); } DirEntryBlock childEntryBlock( (checksumfs_dir_entry_block*)childBlock.Data(), B_PAGE_SIZE); if (!_Check(level + 1, childBlockIndex, name, nameLength, childEntryBlock)) { return false; } } return true; }
status_t DirEntryTree::_UpdateOrInsertKey(LevelInfo* infos, int32 level, const char* name, size_t nameLength, uint64 blockIndex, bool insertKey, Transaction& transaction) { FUNCTION("level: %" B_PRId32 ": %s name: \"%.*s\" (%" B_PRIuSIZE "), " "blockIndex: %" B_PRIu64 "\n", level, insertKey ? "insert" : "update", (int)nameLength, name, nameLength, blockIndex); // Some temporary blocks: newBlock is used when a block is split. The // other three are used when a key update respectively insertion in the // parent block becomes necessary. We only need them, since the name // we update/insert is potentially from a block and instead of cloning // the name, we simple postpone putting the block until we don't need // the name anymore. Block newBlock; Block tempBlockUpdate; Block tempBlockUpdateInsert; Block tempBlockInsert; int32 depth = _Depth(); status_t error; bool updateNextKey = !insertKey; bool insertNextKey = insertKey; const char* nameToUpdate = name; size_t nameToUpdateLength = nameLength; const char* nextNameToInsert = name; size_t nextNameToInsertLength = nameLength; uint64 nextBlockIndexToInsert = blockIndex; for (; level >= 0; level--) { LevelInfo& info = infos[level]; bool updateThisKey = updateNextKey; bool insertThisKey = insertNextKey; if (!updateThisKey && !insertThisKey) return B_OK; updateNextKey = false; insertNextKey = false; blockIndex = nextBlockIndexToInsert; name = nextNameToInsert; nameLength = nextNameToInsertLength; // make the block writable if (level > 0) { error = info.block.MakeWritable(transaction); if (error != B_OK) RETURN_ERROR(error); } if (updateThisKey) { PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": updating key " "to \"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index, (int)nameToUpdateLength, nameToUpdate, nameToUpdateLength); size_t oldNameLength; info.entryBlock.NameAt(info.index, oldNameLength); size_t spaceNeeded = oldNameLength < nameToUpdateLength ? nameToUpdateLength - oldNameLength : 0; if (spaceNeeded <= info.entryBlock.FreeSpace()) { info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, nameToUpdateLength); if (info.index == 0) { // we updated at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no longer // use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } } else if (level == 0) { // We need to split the root block -- clone it first. error = _InsertEntryIncrementDepth(infos, transaction); if (error != B_OK) RETURN_ERROR(error); level = 2; // _InsertEntryIncrementDepth() moved the root block // content to level 1, where we want to continue. updateNextKey = true; insertNextKey = insertThisKey; continue; } else { // We need to split this non-root block. int32 splitIndex; error = _InsertEntrySplitBlock(level, info, spaceNeeded, transaction, newBlock, splitIndex); if (error != B_OK) RETURN_ERROR(error); nextBlockIndexToInsert = newBlock.Index(); DirEntryBlock newEntryBlock( (checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE); ASSERT(newEntryBlock.Check()); if (info.index < splitIndex) { ASSERT(info.entryBlock.FreeSpace() >= spaceNeeded); info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, nameToUpdateLength); if (info.index == 0) { // we updated at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no // longer use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } } else { ASSERT(newEntryBlock.FreeSpace() >= spaceNeeded); // we need to transfer the block to the info, in case we // also need to insert a key below info.block.TransferFrom(newBlock); info.entryBlock.SetTo( (checksumfs_dir_entry_block*)info.block.Data(), B_PAGE_SIZE); ASSERT(info.entryBlock.Check()); info.index -= splitIndex; info.entryBlock.ReplaceEntryName(info.index, nameToUpdate, nameToUpdateLength); } // the newly created block needs to be inserted in the // parent insertNextKey = true; nextNameToInsert = newEntryBlock.NameAt(0, nextNameToInsertLength); // make sure the new block is kept until we no longer use // the name in the next iteration (might already have been // transferred to entry.block) tempBlockUpdateInsert.TransferFrom(newBlock); } } if (insertThisKey) { // insert after the block we descended if (level < depth) info.index++; PRINT(" level: %" B_PRId32 ", index: %" B_PRId32 ": inserting key " "\"%.*s\" (%" B_PRIuSIZE "), blockIndex: %" B_PRIu64 "\n", level, info.index, (int)nameLength, name, nameLength, blockIndex); if (info.entryBlock.FreeSpace() >= nameLength + 10) { info.entryBlock.InsertEntry(info.index, name, nameLength, blockIndex); if (info.index == 0) { // we inserted at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no longer // use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } continue; } // Not enough space left in the block -- we need to split it. ASSERT(!insertNextKey); // for level == 0 we need to clone the block first if (level == 0) { error = _InsertEntryIncrementDepth(infos, transaction); if (error != B_OK) RETURN_ERROR(error); level = 2; // _InsertEntryIncrementDepth() moved the root block // content to level 1, where we want to continue. updateNextKey = false; insertNextKey = true; continue; } int32 splitIndex; error = _InsertEntrySplitBlock(level, info, nameLength + 10, transaction, newBlock, splitIndex); if (error != B_OK) RETURN_ERROR(error); DirEntryBlock newEntryBlock( (checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE); ASSERT(newEntryBlock.Check()); if (info.index < splitIndex) { ASSERT(info.entryBlock.FreeSpace() >= nameLength + 10); info.entryBlock.InsertEntry(info.index, name, nameLength, blockIndex); if (info.index == 0) { // we inserted at index 0, so we need to update this // block's key in the parent block updateNextKey = true; nameToUpdate = info.entryBlock.NameAt(0, nameToUpdateLength); // make sure the new block is kept until we no longer // use the name in the next iteration tempBlockUpdate.TransferFrom(info.block); } } else { ASSERT(newEntryBlock.FreeSpace() >= nameLength + 10); info.index -= splitIndex; newEntryBlock.InsertEntry(info.index, name, nameLength, blockIndex); } // the newly created block needs to be inserted in the parent insertNextKey = true; nextNameToInsert = newEntryBlock.NameAt(0, nextNameToInsertLength); nextBlockIndexToInsert = newBlock.Index(); // make sure the new block is kept until we no longer use // the name in the next iteration tempBlockInsert.TransferFrom(newBlock); } } return B_OK; }