diff --git a/src/add-ons/kernel/file_systems/btrfs/Attribute.cpp b/src/add-ons/kernel/file_systems/btrfs/Attribute.cpp index ddda28dfc5..1c0b286b7b 100644 --- a/src/add-ons/kernel/file_systems/btrfs/Attribute.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/Attribute.cpp @@ -151,10 +151,11 @@ Attribute::_Lookup(const char* name, size_t nameLength, key.SetType(BTRFS_KEY_TYPE_XATTR_ITEM); key.SetObjectID(fInode->ID()); key.SetOffset(hash); + BTree::Path path(fInode->GetVolume()->FSTree()); btrfs_dir_entry* entries; uint32 length; - status_t status = fInode->GetVolume()->FSTree()->FindExact(key, + status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key, (void**)&entries, &length); if (status != B_OK) { TRACE("AttributeIterator::Lookup(): Couldn't find entry with hash %" diff --git a/src/add-ons/kernel/file_systems/btrfs/BTree.cpp b/src/add-ons/kernel/file_systems/btrfs/BTree.cpp index 4ca1ef1952..ce87f5a74f 100644 --- a/src/add-ons/kernel/file_systems/btrfs/BTree.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/BTree.cpp @@ -25,7 +25,6 @@ BTree::Node::Node(Volume* volume) fNode(NULL), fVolume(volume), fBlockNumber(0), - fCurrentSlot(0), fWritable(false) { } @@ -36,7 +35,6 @@ BTree::Node::Node(Volume* volume, off_t block) fNode(NULL), fVolume(volume), fBlockNumber(0), - fCurrentSlot(0), fWritable(false) { SetTo(block); @@ -142,7 +140,109 @@ BTree::Node::SearchSlot(const btrfs_key& key, int* slot, btree_traversing type) } -//-pragma mark +// #pragma mark - BTree::Path implementation + + +BTree::Path::Path(BTree* tree) + : + fTree(tree) +{ + for (int i = 0; i < BTRFS_MAX_TREE_DEPTH; ++i) { + fNodes[i] = NULL; + fSlots[i] = 0; + } +} + + +BTree::Path::~Path() +{ + for (int i = 0; i < BTRFS_MAX_TREE_DEPTH; ++i) { + delete fNodes[i]; + fNodes[i] = NULL; + fSlots[i] = 0; + } +} + + +BTree::Node* +BTree::Path::GetNode(int level, int* _slot) const +{ + if (_slot != NULL) + *_slot = fSlots[level]; + return fNodes[level]; +} + + +BTree::Node* +BTree::Path::SetNode(off_t block, int slot) +{ + Node node(fTree->SystemVolume(), block); + return SetNode(&node, slot); +} + + +BTree::Node* +BTree::Path::SetNode(const Node* node, int slot) +{ + uint8 level = node->Level(); + if (fNodes[level] == NULL) { + fNodes[level] = new Node(fTree->SystemVolume(), node->BlockNum()); + if (fNodes[level] == NULL) + return NULL; + } else + fNodes[level]->SetTo(node->BlockNum()); + + if (slot == -1) + fSlots[level] = fNodes[level]->ItemCount() - 1; + else + fSlots[level] = slot; + return fNodes[level]; +} + + +int +BTree::Path::Move(int level, int step) +{ + fSlots[level] += step; + if (fSlots[level] < 0) + return -1; + if (fSlots[level] >= fNodes[level]->ItemCount()) + return 1; + return 0; +} + + +status_t +BTree::Path::GetEntry(int slot, btrfs_key* _key, void** _value, uint32* _size, + uint32* _offset) +{ + BTree::Node* leaf = fNodes[0]; + if (slot < 0 || slot >= leaf->ItemCount()) + return B_ENTRY_NOT_FOUND; + + if (_key != NULL) + *_key = leaf->Item(slot)->key; + + uint32 itemSize = leaf->Item(slot)->Size(); + if (_value != NULL) { + *_value = malloc(itemSize); + if (*_value == NULL) + return B_NO_MEMORY; + + memcpy(*_value, leaf->ItemData(slot), itemSize); + } + + if (_size != NULL) + *_size = itemSize; + + if (_offset != NULL) + *_offset = leaf->Item(slot)->Offset(); + + return B_OK; +} + + +// #pragma mark - BTree implementation BTree::BTree(Volume* volume) @@ -206,85 +306,180 @@ btrfs_key::Compare(const btrfs_key& key) const } +/* Traverse from root to fill in the path along way its finding. + * Return current slot at leaf if successful. + */ +status_t +BTree::Traverse(btree_traversing type, Path* path, const btrfs_key& key) + const +{ + TRACE("BTree::Traverse() objectid %" B_PRId64 " type %d offset %" + B_PRId64 " \n", key.ObjectID(), key.Type(), key.Offset()); + fsblock_t physicalBlock = fRootBlock; + Node node(fVolume, physicalBlock); + int slot; + status_t status = B_OK; + + while (node.Level() != 0) { + TRACE("BTree::Traverse() level %d count %d\n", node.Level(), + node.ItemCount()); + status = node.SearchSlot(key, &slot, BTREE_BACKWARD); + if (status != B_OK) + return status; + if (path->SetNode(&node, slot) == NULL) + return B_NO_MEMORY; + + TRACE("BTree::Traverse() getting index %" B_PRIu32 "\n", slot); + + status = fVolume->FindBlock(node.Index(slot)->LogicalAddress(), + physicalBlock); + if (status != B_OK) { + ERROR("BTree::Traverse() unmapped block %" B_PRId64 "\n", + node.Index(slot)->LogicalAddress()); + return status; + } + node.SetTo(physicalBlock); + } + + TRACE("BTree::Traverse() dump count %" B_PRId32 "\n", node.ItemCount()); + status = node.SearchSlot(key, &slot, type); + if (status != B_OK) + return status; + if (path->SetNode(&node, slot) == NULL) + return B_NO_MEMORY; + + TRACE("BTree::Traverse() found %" B_PRIu32 " %" B_PRIu32 "\n", + node.Item(slot)->Offset(), node.Item(slot)->Size()); + return slot; +} + + /*! Searches the key in the tree, and stores the allocated found item in _value, if successful. Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND if not. It can also return other errors to indicate that something went wrong. */ status_t -BTree::_Find(btrfs_key& key, void** _value, uint32* _size, - bool read, btree_traversing type) +BTree::_Find(Path* path, btrfs_key& wanted, void** _value, uint32* _size, + uint32* _offset, btree_traversing type) const { - TRACE("Find() objectid %" B_PRId64 " type %d offset %" B_PRId64 " \n", - key.ObjectID(), key.Type(), key.Offset()); - BTree::Node node(fVolume, fRootBlock); - int slot, ret; - fsblock_t physicalBlock; + status_t status = Traverse(type, path, wanted); + if (status < B_OK) + return status; - while (node.Level() != 0) { - TRACE("Find() level %d\n", node.Level()); - ret = node.SearchSlot(key, &slot, BTREE_BACKWARD); - if (ret != B_OK) - return ret; - TRACE("Find() getting index %" B_PRIu32 "\n", slot); + btrfs_key found; + status = path->GetCurrentEntry(&found, _value, _size, _offset); + if (status != B_OK) + return status; - if (fVolume->FindBlock(node.Index(slot)->LogicalAddress(), physicalBlock) - != B_OK) { - ERROR("Find() unmapped block %" B_PRId64 "\n", - node.Index(slot)->LogicalAddress()); - return B_ERROR; - } - node.SetTo(physicalBlock); - } - - TRACE("Find() dump count %" B_PRId32 "\n", node.ItemCount()); - ret = node.SearchSlot(key, &slot, type); - if ((slot >= node.ItemCount() || node.Item(slot)->key.Type() != key.Type()) - && read == true - || ret != B_OK) { - TRACE("Find() not found %" B_PRId64 " %" B_PRId64 "\n", key.Offset(), - key.ObjectID()); + if (found.Type() != wanted.Type() && wanted.Type() != BTRFS_KEY_TYPE_ANY) return B_ENTRY_NOT_FOUND; - } - if (read == true) { - TRACE("Find() found %" B_PRIu32 " %" B_PRIu32 "\n", - node.Item(slot)->Offset(), node.Item(slot)->Size()); - - if (_value != NULL) { - *_value = malloc(node.Item(slot)->Size()); - memcpy(*_value, node.ItemData(slot), - node.Item(slot)->Size()); - key.SetOffset(node.Item(slot)->key.Offset()); - key.SetObjectID(node.Item(slot)->key.ObjectID()); - if (_size != NULL) - *_size = node.Item(slot)->Size(); - } - } else { - *_value = (void*)&slot; - } + wanted = found; return B_OK; } status_t -BTree::FindNext(btrfs_key& key, void** _value, uint32* _size, bool read) +BTree::FindNext(Path* path, btrfs_key& key, void** _value, uint32* _size, + uint32* _offset) const { - return _Find(key, _value, _size, read, BTREE_FORWARD); + return _Find(path, key, _value, _size, _offset, BTREE_FORWARD); } status_t -BTree::FindPrevious(btrfs_key& key, void** _value, uint32* _size, bool read) +BTree::FindPrevious(Path* path, btrfs_key& key, void** _value, uint32* _size, + uint32* _offset) const { - return _Find(key, _value, _size, read, BTREE_BACKWARD); + return _Find(path, key, _value, _size, _offset, BTREE_BACKWARD); } status_t -BTree::FindExact(btrfs_key& key, void** _value, uint32* _size, bool read) +BTree::FindExact(Path* path, btrfs_key& key, void** _value, uint32* _size, + uint32* _offset) const { - return _Find(key, _value, _size, read, BTREE_EXACT); + return _Find(path, key, _value, _size, _offset, BTREE_EXACT); +} + + +status_t +BTree::PreviousLeaf(Path* path) const +{ + // TODO: use Traverse() ??? + int level = 0; + int slot; + Node* node = NULL; + // iterate to the root until satisfy the condition + while (true) { + node = path->GetNode(level, &slot); + if (node == NULL || slot != 0) + break; + level++; + } + + // the current leaf is already the left most leaf or + // path was not initialized + if (node == NULL) + return B_ENTRY_NOT_FOUND; + + path->Move(level, BTREE_BACKWARD); + fsblock_t physicalBlock; + // change all nodes below this level and slot to the ending + do { + status_t status = fVolume->FindBlock( + node->Index(slot)->LogicalAddress(), physicalBlock); + if (status != B_OK) + return status; + + node = path->SetNode(physicalBlock, -1); + if (node == NULL) + return B_NO_MEMORY; + slot = node->ItemCount() - 1; + level--; + } while(level != 0); + + return B_OK; +} + + +status_t +BTree::NextLeaf(Path* path) const +{ + int level = 0; + int slot; + Node* node = NULL; + // iterate to the root until satisfy the condition + while (true) { + node = path->GetNode(level, &slot); + if (node == NULL || slot < node->ItemCount() - 1) + break; + level++; + } + + // the current leaf is already the right most leaf or + // path was not initialized + if (node == NULL) + return B_ENTRY_NOT_FOUND; + + path->Move(level, BTREE_FORWARD); + fsblock_t physicalBlock; + // change all nodes below this level and slot to the beginning + do { + status_t status = fVolume->FindBlock( + node->Index(slot)->LogicalAddress(), physicalBlock); + if (status != B_OK) + return status; + + node = path->SetNode(physicalBlock, 0); + if (node == NULL) + return B_NO_MEMORY; + slot = 0; + level--; + } while(level != 0); + + return B_OK; } @@ -351,8 +546,8 @@ TreeIterator::Traverse(btree_traversing direction, btrfs_key& key, return B_INTERRUPTED; fCurrentKey.SetOffset(fCurrentKey.Offset() + direction); - status_t status = fTree->_Find(fCurrentKey, value, size, - true, direction); + BTree::Path path(fTree); + status_t status = fTree->_Find(&path, fCurrentKey, value, size, direction); if (status != B_OK) { TRACE("TreeIterator::Traverse() Find failed\n"); return B_ENTRY_NOT_FOUND; diff --git a/src/add-ons/kernel/file_systems/btrfs/BTree.h b/src/add-ons/kernel/file_systems/btrfs/BTree.h index bb034dd20c..30afcaf790 100644 --- a/src/add-ons/kernel/file_systems/btrfs/BTree.h +++ b/src/add-ons/kernel/file_systems/btrfs/BTree.h @@ -42,6 +42,9 @@ struct node_and_key { class BTree { +public: + class Path; + public: BTree(Volume* volume); BTree(Volume* volume, @@ -49,15 +52,24 @@ public: BTree(Volume* volume, fsblock_t rootBlock); ~BTree(); - status_t FindExact(btrfs_key& key, void** value, - uint32* size = NULL, bool read = true); - status_t FindNext(btrfs_key& key, void** value, - uint32* size = NULL, bool read = true); - status_t FindPrevious(btrfs_key& key, void** value, - uint32* size = NULL, bool read = true); + + 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; + + 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; Volume* SystemVolume() const { return fVolume; } - status_t SetRoot(off_t logical, fsblock_t* block); fsblock_t RootBlock() const { return fRootBlock; } off_t LogicalRoot() const { return fLogicalRoot; } @@ -67,8 +79,10 @@ private: BTree& operator=(const BTree& other); // no implementation - status_t _Find(btrfs_key& key, void** value, uint32* size, - bool read, btree_traversing type); + 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: @@ -113,8 +127,7 @@ public: void Unset(); void SetTo(off_t block); - void SetToWritable(off_t block, - int32 transactionId, bool empty); + void SetToWritable(off_t block, int32 transactionId, bool empty); off_t BlockNum() const { return fBlockNumber;} bool IsWritable() const { return fWritable; } @@ -129,18 +142,33 @@ public: btrfs_stream* fNode; Volume* fVolume; off_t fBlockNumber; - uint32 fCurrentSlot; bool fWritable; }; class Path { public: - Path(); + 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); + + int Move(int level, int step); + + BTree* Tree() const { return fTree; } private: Path(const Path&); Path operator=(const Path&); - Node* nodes[BTRFS_MAX_TREE_DEPTH]; + private: + Node* fNodes[BTRFS_MAX_TREE_DEPTH]; + int fSlots[BTRFS_MAX_TREE_DEPTH]; + BTree* fTree; }; }; // class BTree @@ -176,6 +204,17 @@ private: }; +// #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 diff --git a/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp b/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp index 4f9a1b793b..ccfd1b225b 100644 --- a/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp @@ -123,10 +123,11 @@ DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id) key.SetType(BTRFS_KEY_TYPE_DIR_ITEM); key.SetObjectID(fInode->ID()); key.SetOffset(hash); + BTree::Path path(fInode->GetVolume()->FSTree()); btrfs_dir_entry* entries; uint32 length; - status_t status = fInode->GetVolume()->FSTree()->FindExact(key, + status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key, (void**)&entries, &length); if (status != B_OK) { TRACE("DirectoryIterator::Lookup(): Couldn't find entry with hash %" B_PRIu32 diff --git a/src/add-ons/kernel/file_systems/btrfs/Inode.cpp b/src/add-ons/kernel/file_systems/btrfs/Inode.cpp index a2e7bb8bb8..173fe190ed 100644 --- a/src/add-ons/kernel/file_systems/btrfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/Inode.cpp @@ -78,9 +78,11 @@ Inode::UpdateNodeFromDisk() search_key.SetType(BTRFS_KEY_TYPE_INODE_ITEM); search_key.SetObjectID(fID); search_key.SetOffset(0); + BTree::Path path(fVolume->FSTree()); btrfs_inode* node; - if (fVolume->FSTree()->FindExact(search_key, (void**)&node) != B_OK) { + if (fVolume->FSTree()->FindExact(&path, search_key, (void**)&node) + != B_OK) { ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %" B_PRIdINO "\n", fID); return B_ENTRY_NOT_FOUND; @@ -111,9 +113,10 @@ Inode::FindBlock(off_t pos, off_t& physical, off_t* _length) search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); search_key.SetObjectID(fID); search_key.SetOffset(pos + 1); + BTree::Path path(fVolume->FSTree()); btrfs_extent_data* extent_data; - status_t status = fVolume->FSTree()->FindPrevious(search_key, + status_t status = fVolume->FSTree()->FindPrevious(&path, search_key, (void**)&extent_data); if (status != B_OK) { ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32 @@ -166,10 +169,11 @@ Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA); search_key.SetObjectID(fID); search_key.SetOffset(pos + 1); + BTree::Path path(fVolume->FSTree()); uint32 item_size; btrfs_extent_data* extent_data; - status_t status = fVolume->FSTree()->FindPrevious(search_key, + status_t status = fVolume->FSTree()->FindPrevious(&path, search_key, (void**)&extent_data, &item_size); if (status != B_OK) { ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32 @@ -292,9 +296,10 @@ Inode::FindParent(ino_t* id) search_key.SetType(BTRFS_KEY_TYPE_INODE_REF); search_key.SetObjectID(fID); search_key.SetOffset(-1); + BTree::Path path(fVolume->FSTree()); void* node_ref; - if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) { + if (fVolume->FSTree()->FindPrevious(&path, search_key, &node_ref) != B_OK) { ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n", fID); return B_ERROR; diff --git a/src/add-ons/kernel/file_systems/btrfs/Volume.cpp b/src/add-ons/kernel/file_systems/btrfs/Volume.cpp index 159f67e255..dc2fffed2d 100644 --- a/src/add-ons/kernel/file_systems/btrfs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/Volume.cpp @@ -317,15 +317,18 @@ Volume::Mount(const char* deviceName, uint32 flags) TRACE("Volume::Mount() root: %" B_PRIu64 " (physical block %" B_PRIu64 ")\n", fSuperBlock.Root(), fRootTree->RootBlock()); + BTree::Path path(fRootTree); + TRACE("Volume::Mount(): Searching extent root\n"); btrfs_key search_key; search_key.SetOffset(0); search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM); search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE); btrfs_root* root; - if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) { + status = fRootTree->FindExact(&path, search_key, (void**)&root); + if (status != B_OK) { ERROR("Volume::Mount(): Couldn't find extent root\n"); - return B_ERROR; + return status; } TRACE("Volume::Mount(): Found extent root: %" B_PRIu64 "\n", root->LogicalAddress()); @@ -338,9 +341,10 @@ Volume::Mount(const char* deviceName, uint32 flags) TRACE("Volume::Mount(): Searching fs root\n"); search_key.SetOffset(0); search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE); - if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) { + status = fRootTree->FindExact(&path, search_key, (void**)&root); + if (status != B_OK) { ERROR("Volume::Mount(): Couldn't find fs root\n"); - return B_ERROR; + return status; } TRACE("Volume::Mount(): Found fs root: %" B_PRIu64 "\n", root->LogicalAddress()); @@ -353,9 +357,10 @@ Volume::Mount(const char* deviceName, uint32 flags) TRACE("Volume::Mount(): Searching dev root\n"); search_key.SetOffset(0); search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE); - if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) { + status = fRootTree->FindExact(&path, search_key, (void**)&root); + if (status != B_OK) { ERROR("Volume::Mount(): Couldn't find dev root\n"); - return B_ERROR; + return status; } TRACE("Volume::Mount(): Found dev root: %" B_PRIu64 "\n", root->LogicalAddress()); @@ -368,9 +373,10 @@ Volume::Mount(const char* deviceName, uint32 flags) TRACE("Volume::Mount(): Searching checksum root\n"); search_key.SetOffset(0); search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE); - if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) { + status = fRootTree->FindExact(&path, search_key, (void**)&root); + if (status != B_OK) { ERROR("Volume::Mount(): Couldn't find checksum root\n"); - return B_ERROR; + return status; } TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64 "\n", root->LogicalAddress()); @@ -484,11 +490,11 @@ Volume::FindBlock(off_t logical, off_t& physical) btrfs_key search_key; search_key.SetOffset(logical); search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM); - search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE); + search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE); btrfs_chunk* chunk; - uint32 chunk_length; - status_t status = fChunkTree->FindPrevious(search_key, (void**)&chunk, - &chunk_length); + BTree::Path path(fChunkTree); + status_t status = fChunkTree->FindPrevious(&path, search_key, + (void**)&chunk); if (status != B_OK) return status; diff --git a/src/add-ons/kernel/file_systems/btrfs/btrfs.h b/src/add-ons/kernel/file_systems/btrfs/btrfs.h index 54e4634b3b..d2f4518c00 100644 --- a/src/add-ons/kernel/file_systems/btrfs/btrfs.h +++ b/src/add-ons/kernel/file_systems/btrfs/btrfs.h @@ -427,6 +427,7 @@ struct btrfs_extent_data_ref { #define BTRFS_OBJECT_ID_CHECKSUM_TREE 7 #define BTRFS_OBJECT_ID_FIRST_CHUNK_TREE 256 +#define BTRFS_KEY_TYPE_ANY 0 #define BTRFS_KEY_TYPE_INODE_ITEM 1 #define BTRFS_KEY_TYPE_INODE_REF 12 #define BTRFS_KEY_TYPE_XATTR_ITEM 24