diff --git a/src/add-ons/kernel/file_systems/btrfs/BTree.cpp b/src/add-ons/kernel/file_systems/btrfs/BTree.cpp index 6b3c17c563..39e1e9475f 100644 --- a/src/add-ons/kernel/file_systems/btrfs/BTree.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/BTree.cpp @@ -9,6 +9,7 @@ #include "BTree.h" +#include "Journal.h" //#define TRACE_BTRFS @@ -419,12 +420,131 @@ BTree::Path::GetEntry(int slot, btrfs_key* _key, void** _value, uint32* _size, } +/* + * Allocate and copy block and do all the changes that it can. + * for now, we only copy-on-write tree block, + * file data is "nocow" by default. + * + * o parent o + * | ===> \ + * o x o + */ +status_t +BTree::Path::CopyOnWrite(Transaction& transaction, int level, uint32 start, + int num, int length) +{ + Node* node = fNodes[level]; + if (node == NULL) + return B_BAD_VALUE; + + status_t status; + if (transaction.HasBlock(node->BlockNum())) { + // cow-ed block can not be cow-ed again + status = node->MoveEntries(start, start + num - 1, length); + if (status != B_OK) + return status; + + node->SetGeneration(transaction.SystemID()); + if (length < 0) + node->SetItemCount(node->ItemCount() - num); + else if (length > 0) + node->SetItemCount(node->ItemCount() + num); + + return B_OK; + } + + uint64 address; + fsblock_t block; + status = fTree->SystemVolume()->GetNewBlock(address, block); + if (status != B_OK) + return status; + + fNodes[level] = new(std::nothrow) BTree::Node(fTree->SystemVolume()); + if (fNodes[level] == NULL) + return B_NO_MEMORY; + + fNodes[level]->SetToWritable(block, transaction.ID(), true); + + status = fNodes[level]->Copy(node, start, start + num - 1, length); + if (status != B_OK) + return status; + + fNodes[level]->SetGeneration(transaction.SystemID()); + fNodes[level]->SetLogicalAddress(address); + if (length < 0) + fNodes[level]->SetItemCount(node->ItemCount() - num); + else if (length > 0) + fNodes[level]->SetItemCount(node->ItemCount() + num); + else + fNodes[level]->SetItemCount(num); + + // change pointer of this node in parent + int parentSlot; + Node* parentNode = GetNode(level + 1, &parentSlot); + if (parentNode != NULL) + parentNode->Index(parentSlot)->SetLogicalAddress(address); + + if (level == fTree->RootLevel()) + fTree->SetRoot(fNodes[level]); + + delete node; + return B_OK; +} + + +/* Copy-On-Write all internal nodes start from a specific level. + * level > 0: to root + * level <= 0: to leaf + * + * path cow-path path cow-path + * ================================================= + * root cow-root root level < 0 + * | | | + * n1 cow-n1 ...______ + * | | | \ + * n2 cow-n2 n1 cow-n1 + * | / | | + * ...____/ n2 cow-n2 + * | | | + * leaf level > 0 leaf cow-leaf + */ +status_t +BTree::Path::InternalCopy(Transaction& transaction, int level) +{ + if (std::abs(level) >= fTree->RootLevel()) + return B_OK; + + TRACE("Path::InternalCopy() level %i\n", level); + int from, to; + if (level > 0) { + from = level; + to = fTree->RootLevel(); + } else { + from = 0; + to = std::abs(level); + } + + Node* node = NULL; + status_t status; + while (from <= to) { + node = fNodes[from]; + status = CopyOnWrite(transaction, from, 0, node->ItemCount(), 0); + from++; + if (status != B_OK) + return status; + } + + return B_OK; +} + + // #pragma mark - BTree implementation BTree::BTree(Volume* volume) : fRootBlock(0), + fRootLevel(0), fVolume(volume) { mutex_init(&fIteratorLock, "btrfs b+tree iterator"); @@ -434,6 +554,7 @@ BTree::BTree(Volume* volume) BTree::BTree(Volume* volume, btrfs_stream* stream) : fRootBlock(0), + fRootLevel(0), fVolume(volume) { mutex_init(&fIteratorLock, "btrfs b+tree iterator"); @@ -660,23 +781,39 @@ BTree::NextLeaf(Path* path) const } +/* Set root infomation, to use this function root must be valid and + * exists on disk. + */ status_t BTree::SetRoot(off_t logical, fsblock_t* block) { if (block != NULL) { fRootBlock = *block; - //TODO: mapping physical block -> logical address } else { - fLogicalRoot = logical; if (fVolume->FindBlock(logical, fRootBlock) != B_OK) { ERROR("Find() unmapped block %" B_PRId64 "\n", fRootBlock); return B_ERROR; } } + + btrfs_header header; + read_pos(fVolume->Device(), fRootBlock * fVolume->BlockSize(), &header, + sizeof(btrfs_header)); + fRootLevel = header.Level(); + fLogicalRoot = header.LogicalAddress(); return B_OK; } +void +BTree::SetRoot(Node* root) +{ + fRootBlock = root->BlockNum(); + fLogicalRoot = root->LogicalAddress(); + fRootLevel = root->Level(); +} + + void BTree::_AddIterator(TreeIterator* iterator) { diff --git a/src/add-ons/kernel/file_systems/btrfs/BTree.h b/src/add-ons/kernel/file_systems/btrfs/BTree.h index 14e675c1b5..57ba503662 100644 --- a/src/add-ons/kernel/file_systems/btrfs/BTree.h +++ b/src/add-ons/kernel/file_systems/btrfs/BTree.h @@ -27,6 +27,9 @@ enum btree_traversing { }; +class Transaction; + + // #pragma mark - in-memory structures @@ -44,6 +47,7 @@ struct node_and_key { class BTree { public: class Path; + class Node; public: BTree(Volume* volume); @@ -71,8 +75,10 @@ public: Volume* SystemVolume() const { return fVolume; } status_t SetRoot(off_t logical, fsblock_t* block); + void SetRoot(Node* root); fsblock_t RootBlock() const { return fRootBlock; } off_t LogicalRoot() const { return fLogicalRoot; } + uint8 RootLevel() const { return fRootLevel; } private: BTree(const BTree& other); @@ -90,6 +96,7 @@ private: fsblock_t fRootBlock; off_t fLogicalRoot; + uint8 fRootLevel; Volume* fVolume; mutex fIteratorLock; SinglyLinkedList fIterators; @@ -114,6 +121,12 @@ public: { return fNode->header.ItemCount(); } uint8 Level() const { return fNode->header.Level(); } + void SetLogicalAddress(uint64 address) + { fNode->header.SetLogicalAddress(address); } + void SetGeneration(uint64 generation) + { fNode->header.SetGeneration(generation); } + void SetItemCount(uint32 itemCount) + { fNode->header.SetItemCount(itemCount); } btrfs_index* Index(uint32 i) const { return &fNode->index[i]; } @@ -171,6 +184,10 @@ public: int Move(int level, int step); + status_t CopyOnWrite(Transaction& transaction, int level, + uint32 start, int num, int length); + status_t InternalCopy(Transaction& transaction, int level); + BTree* Tree() const { return fTree; } private: Path(const Path&); diff --git a/src/add-ons/kernel/file_systems/btrfs/btrfs.h b/src/add-ons/kernel/file_systems/btrfs/btrfs.h index f2010b91fb..faf2a68fbf 100644 --- a/src/add-ons/kernel/file_systems/btrfs/btrfs.h +++ b/src/add-ons/kernel/file_systems/btrfs/btrfs.h @@ -110,6 +110,13 @@ struct btrfs_header { uint32 ItemCount() const { return B_LENDIAN_TO_HOST_INT32(item_count); } uint8 Level() const { return level; } + + void SetLogicalAddress(uint64 logical) + { logical_address = B_HOST_TO_LENDIAN_INT64(logical); } + void SetGeneration(uint64 gen) + { generation = B_HOST_TO_LENDIAN_INT64(gen); } + void SetItemCount(uint32 itemCount) + { item_count = B_HOST_TO_LENDIAN_INT32(itemCount); } } _PACKED; @@ -121,6 +128,11 @@ struct btrfs_index { { return B_LENDIAN_TO_HOST_INT64(logical_address); } uint64 Generation() const { return B_LENDIAN_TO_HOST_INT64(generation); } + + void SetLogicalAddress(uint64 address) + { logical_address = B_HOST_TO_LENDIAN_INT64(address); } + void SetGeneration(uint64 gen) + { generation = B_HOST_TO_LENDIAN_INT64(gen); } } _PACKED;