/* * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com. * Copyright 2011, Jérôme Duval, korli@users.berlios.de. * Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de. * This file may be used under the terms of the MIT License. */ #ifndef B_TREE_H #define B_TREE_H #include "btrfs.h" #include "Volume.h" #define BTREE_NULL -1LL #define BTREE_FREE -2LL #define BTRFS_MAX_TREE_DEPTH 8 //! Tree traversal direction status, used by functions manipulating trees enum btree_traversing { BTREE_FORWARD = 1, BTREE_EXACT = 0, BTREE_BACKWARD = -1, BTREE_BEGIN = 0, BTREE_END = -1 }; class Transaction; // #pragma mark - in-memory structures template class Stack; class TreeIterator; // needed for searching (utilizing a stack) struct node_and_key { off_t nodeOffset; uint16 keyIndex; }; class BTree { public: class Path; class Node; public: BTree(Volume* volume); BTree(Volume* volume, btrfs_stream* stream); BTree(Volume* volume, fsblock_t rootBlock); ~BTree(); status_t FindExact(Path* path, btrfs_key& key, void** _value, uint32* _size = NULL, uint32* _offset = NULL) const; status_t FindNext(Path* path, btrfs_key& key, void** _value, uint32* _size = NULL, uint32* _offset = NULL) const; status_t FindPrevious(Path* path, btrfs_key& key, void** _value, uint32* _size = NULL, uint32* _offset = NULL) const; /*! Traverse from the root to fill in the path along the way * \return Current slot at leaf if successful, error code (out of memory, * no such entry, unmapped block) otherwise */ status_t Traverse(btree_traversing type, Path* path, const btrfs_key& key) const; status_t PreviousLeaf(Path* path) const; status_t NextLeaf(Path* path) const; /*! Insert consecutive empty entries * \param num Number of entries to be inserted * \param startKey Slot to start inserting * \return Starting slot on success, error code otherwise */ status_t MakeEntries(Transaction& transaction, Path* path, const btrfs_key& startKey, int num, int length); //! MakeEntries and fill in them status_t InsertEntries(Transaction& transaction, Path* path, btrfs_entry* entries, void** data, int num); /*! Like MakeEntries, but here entries are removed. * \param _data Location to store removed data */ status_t RemoveEntries(Transaction& transaction, Path* path, const btrfs_key& startKey, void** _data, int num); 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); BTree& operator=(const BTree& other); // no implementation /*! Search for key in the tree * \param _value Location to store item if search successful * \return B_OK when key found, error code otherwise */ status_t _Find(Path* path, btrfs_key& key, void** _value, uint32* _size, uint32* _offset, btree_traversing type) const; void _AddIterator(TreeIterator* iterator); void _RemoveIterator(TreeIterator* iterator); private: friend class TreeIterator; fsblock_t fRootBlock; off_t fLogicalRoot; uint8 fRootLevel; Volume* fVolume; mutex fIteratorLock; SinglyLinkedList fIterators; public: class Node { public: Node(Volume* volume); Node(Volume* volume, off_t block); ~Node(); uint64 LogicalAddress() const { return fNode->header.LogicalAddress(); } uint64 Flags() const { return fNode->header.Flags(); } uint64 Generation() const { return fNode->header.Generation(); } uint64 Owner() const { return fNode->header.Owner(); } uint32 ItemCount() const { 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]; } btrfs_entry* Item(uint32 i) const { return &fNode->entries[i]; } uint8* ItemData(uint32 i) const { return (uint8*)Item(0) + Item(i)->Offset(); } //! Reset Node and decrements ref-count to the Node's block void Unset(); //! Load node at block offset from disk void SetTo(off_t block); void SetToWritable(off_t block, int32 transactionId, bool empty); int SpaceUsed() const; int SpaceLeft() const; off_t BlockNum() const { return fBlockNumber;} bool IsWritable() const { return fWritable; } /*! * copy node header, items and items data * length is size to insert/remove * if node is a internal node, length isnt used * length = 0: Copy a whole * length < 0: removing * length > 0: inserting */ status_t Copy(const Node* origin, uint32 start, uint32 end, int length) const; //! Shift data in items between start and end by offset length status_t MoveEntries(uint32 start, uint32 end, int length) const; //! Searches for item slot in the node status_t SearchSlot(const btrfs_key& key, int* slot, btree_traversing type) const; private: Node(const Node&); Node& operator=(const Node&); // no implementation //! Internal function used by Copy void _Copy(const Node* origin, uint32 at, uint32 from, uint32 to, int length) const; status_t _SpaceCheck(int length) const; /*! * calculate used space except the header. * type is only for leaf node * type 1: only item space * type 2: only item data space * type 3: both type 1 and 2 */ int _CalculateSpace(uint32 from, uint32 to, uint8 type = 1) const; btrfs_stream* fNode; Volume* fVolume; off_t fBlockNumber; bool fWritable; }; class Path { public: Path(BTree* tree); ~Path(); Node* GetNode(int level, int* _slot = NULL) const; Node* SetNode(off_t block, int slot); Node* SetNode(const Node* node, int slot); status_t GetCurrentEntry(btrfs_key* _key, void** _value, uint32* _size = NULL, uint32* _offset = NULL); status_t GetEntry(int slot, btrfs_key* _key, void** _value, uint32* _size = NULL, uint32* _offset = NULL); status_t SetEntry(int slot, const btrfs_entry& entry, void* value); int Move(int level, int step); /*! * 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 CopyOnWrite(Transaction& transaction, int level, uint32 start, int num, int length); /*! * 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 InternalCopy(Transaction& transaction, int level); BTree* Tree() const { return fTree; } private: Path(const Path&); Path operator=(const Path&); private: Node* fNodes[BTRFS_MAX_TREE_DEPTH]; int fSlots[BTRFS_MAX_TREE_DEPTH]; BTree* fTree; }; }; // class BTree class TreeIterator : public SinglyLinkedListLinkImpl { public: TreeIterator(BTree* tree, const btrfs_key& key); ~TreeIterator(); void Rewind(bool inverse = false); //! Set current key in the iterator status_t Find(const btrfs_key& key); status_t GetNextEntry(void** _value, uint32* _size = NULL, uint32* _offset = NULL); status_t GetPreviousEntry(void** _value, uint32* _size = NULL, uint32* _offset = NULL); BTree* Tree() const { return fTree; } btrfs_key Key() const { return fKey; } private: friend class BTree; //! Iterates through the tree in the specified direction status_t _Traverse(btree_traversing direction); status_t _Find(btree_traversing type, btrfs_key& key, void** _value); //! Like GetEntry in BTree::Path but checks type and moving status_t _GetEntry(btree_traversing type, void** _value, uint32* _size, uint32* _offset); // called by BTree void Stop(); private: BTree* fTree; BTree::Path* fPath; btrfs_key fKey; status_t fIteratorStatus; }; // #pragma mark - BTree::Path inline functions inline status_t BTree::Path::GetCurrentEntry(btrfs_key* _key, void** _value, uint32* _size, uint32* _offset) { return GetEntry(fSlots[0], _key, _value, _size, _offset); } // #pragma mark - TreeIterator inline functions inline status_t TreeIterator::GetNextEntry(void** _value, uint32* _size, uint32* _offset) { return _GetEntry(BTREE_FORWARD, _value, _size, _offset); } inline status_t TreeIterator::GetPreviousEntry(void** _value, uint32* _size, uint32* _offset) { return _GetEntry(BTREE_BACKWARD, _value, _size, _offset); } #endif // B_TREE_H