diff --git a/src/add-ons/kernel/file_systems/ramfs/AVLTree.h b/src/add-ons/kernel/file_systems/ramfs/AVLTree.h new file mode 100644 index 0000000000..60e11bae48 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AVLTree.h @@ -0,0 +1,1101 @@ +// AVLTree.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef AVL_TREE_H +#define AVL_TREE_H + +#include + +#include + +#include "Misc.h" + +// maximal height of a tree +static const int kMaxAVLTreeHeight = 32; + +// AVLTreeStandardCompare +template +class AVLTreeStandardCompare +{ +public: + inline int operator()(const Value &a, const Value &b) const + { + if (a < b) + return -1; + else if (a > b) + return 1; + return 0; + } +}; + +// AVLTreeStandardGetKey +template +class AVLTreeStandardGetKey +{ +public: + inline const Key &operator()(const Value &a) const + { + return a; + } + + inline Key &operator()(Value &a) const + { + return a; + } +}; + +// AVLTreeStandardNode +template +struct AVLTreeStandardNode { + AVLTreeStandardNode(const Value &a) + : value(a), + parent(NULL), + left(NULL), + right(NULL), + balance_factor(0) + { + } + + Value value; + AVLTreeStandardNode *parent; + AVLTreeStandardNode *left; + AVLTreeStandardNode *right; + int balance_factor; +}; + +// AVLTreeStandardNodeAllocator +template +class AVLTreeStandardNodeAllocator +{ +public: + inline Node *Allocate(const Value &a) const + { + return new(nothrow) AVLTreeStandardNode(a); + } + + inline void Free(Node *node) const + { + delete node; + } +}; + +// AVLTreeStandardGetValue +template +class AVLTreeStandardGetValue +{ +public: + inline Value &operator()(Node *node) const + { + return node->value; + } +}; + +// for convenience +#define AVL_TREE_TEMPLATE_LIST template +#define AVL_TREE_CLASS_NAME AVLTree + +// AVLTree +template, + typename KeyCompare = AVLTreeStandardCompare, + typename GetKey = AVLTreeStandardGetKey, + typename NodeAllocator = AVLTreeStandardNodeAllocator, + typename GetValue = AVLTreeStandardGetValue > +class AVLTree { +public: + class Iterator; + +public: +// The Node parameter must implement this interface. +// struct Node { +// Node *parent; +// Node *left; +// Node *right; +// int balance_factor; +// }; + + AVLTree(); + AVLTree(const KeyCompare &keyCompare, const GetKey &getKey, + const NodeAllocator &allocator, const GetValue &getValue); + ~AVLTree(); + + inline int CountItems() const { return fNodeCount; } + + Value *Find(const Key &key, Iterator *iterator = NULL); + Value *FindClose(const Key &key, bool less, Iterator *iterator = NULL); + void GetIterator(Iterator *iterator, bool reverse = false); + + status_t Insert(const Value &value, Iterator *iterator = NULL); + status_t Remove(const Key &key); + void Remove(Iterator &iterator); + + // debugging + int Check(Node *node = NULL, int level = 0, bool levelsOnly = false) const; + +protected: + enum { + NOT_FOUND = -3, + DUPLICATE = -2, + NO_MEMORY = -1, + OK = 0, + HEIGHT_CHANGED = 1, + + LEFT = -1, + BALANCED = 0, + RIGHT = 1, + }; + + // rotations + void _RotateRight(Node **nodeP); + void _RotateLeft(Node **nodeP); + + // insert + int _BalanceInsertLeft(Node **node); + int _BalanceInsertRight(Node **node); + int _Insert(const Value &value, Node **node, Iterator *iterator); + + // remove + int _BalanceRemoveLeft(Node **node); + int _BalanceRemoveRight(Node **node); + int _RemoveRightMostChild(Node **node, Node **foundNode); + int _Remove(const Key &key, Node **node); + int _Remove(Node *node); + + void _FreeTree(Node *node); + + // debugging + void _DumpNode(Node *node) const; + + // iterator support + inline void _InitIterator(Iterator *iterator, Node *node, + bool reverse = false, bool initialize = false); + + +protected: + friend class Iterator; + + Node *fRoot; + int fNodeCount; + KeyCompare fKeyCompare; + GetKey fGetKey; + NodeAllocator fAllocator; + GetValue fGetValue; +}; + +// Iterator +AVL_TREE_TEMPLATE_LIST +class AVL_TREE_CLASS_NAME::Iterator { +public: + Iterator() + : fTree(NULL), + fCurrent(NULL), + fReverse(false) + { + } + + Iterator(AVL_TREE_CLASS_NAME *tree) + : fTree(NULL), + fCurrent(NULL), + fReverse(false) + { + if (tree) + tree->GetIterator(this); + } + + ~Iterator() + { + } + + inline Value *GetCurrent() + { + return (fTree && fCurrent ? &fTree->fGetValue(fCurrent) : NULL); + } + + inline Value *GetPrevious() + { + if (fReverse) + fCurrent = _GetNextNode(fCurrent); + else + fCurrent = _GetPreviousNode(fCurrent); + return GetCurrent(); + } + + inline Value *GetNext() + { + if (fReverse) + fCurrent = _GetPreviousNode(fCurrent); + else + fCurrent = _GetNextNode(fCurrent); + return GetCurrent(); + } + + inline void Remove() + { + if (fTree) + fTree->Remove(*this); + } + + inline void SetReverse(bool reverse) + { + fReverse = reverse; + } + +private: + friend class AVL_TREE_CLASS_NAME; + + Iterator(const Iterator&); + Iterator & operator=(const Iterator&); + +private: + inline void _SetTo(AVL_TREE_CLASS_NAME *tree, Node *node, + bool reverse = false, bool initialize = false) + { + fTree = tree; + fCurrent = node; + fReverse = reverse; + // initialize to first/last node, if desired + if (initialize && fTree) { + fCurrent = fTree->fRoot; + if (fCurrent) { + if (fReverse) { + while (fCurrent->right) + fCurrent = fCurrent->right; + } else { + while (fCurrent->left) + fCurrent = fCurrent->left; + } + } + } + } + + static inline Node *_GetPreviousNode(Node *node) + { + if (node) { + // The previous node cannot be in the right subtree. + if (node->left) { + // We have a left subtree, so go to the right-most node. + node = node->left; + while (node->right) + node = node->right; + } else { + // No left subtree: Backtrack our path and stop, where we + // took the right branch. + Node *previous; + do { + previous = node; + node = node->parent; + } while (node && previous == node->left); + } + } + return node; + } + + static inline Node *_GetNextNode(Node *node) + { + if (node) { + // The next node cannot be in the left subtree. + if (node->right) { + // We have a right subtree, so go to the left-most node. + node = node->right; + while (node->left) + node = node->left; + } else { + // No right subtree: Backtrack our path and stop, where we + // took the left branch. + Node *previous; + do { + previous = node; + node = node->parent; + } while (node && previous == node->right); + } + } + return node; + } + + inline AVL_TREE_CLASS_NAME *_GetList() const { return fTree; } + inline Node *_GetCurrentNode() const { return fCurrent; } + +private: + AVL_TREE_CLASS_NAME *fTree; + Node *fCurrent; + bool fReverse; +}; + + +// AVLTree + +// constructor +AVL_TREE_TEMPLATE_LIST +AVL_TREE_CLASS_NAME::AVLTree() + : fRoot(NULL), + fNodeCount(0)/*, + fKeyCompare(), + fGetKey(), + fAllocator(), + fGetValue()*/ +{ +} + +// constructor +AVL_TREE_TEMPLATE_LIST +AVL_TREE_CLASS_NAME::AVLTree(const KeyCompare &keyCompare, + const GetKey &getKey, const NodeAllocator &allocator, + const GetValue &getValue) + : fRoot(NULL), + fNodeCount(0), + fKeyCompare(keyCompare), + fGetKey(getKey), + fAllocator(allocator), + fGetValue(getValue) +{ +} + +// destructor +AVL_TREE_TEMPLATE_LIST +AVL_TREE_CLASS_NAME::~AVLTree() +{ + _FreeTree(fRoot); + fRoot = NULL; +} + +// Find +AVL_TREE_TEMPLATE_LIST +Value * +AVL_TREE_CLASS_NAME::Find(const Key &key, Iterator *iterator) +{ + Node *node = fRoot; + while (node) { + int cmp = fKeyCompare(key, fGetKey(fGetValue(node))); + if (cmp == 0) { + if (iterator) + iterator->_SetTo(this, node); + return &fGetValue(node); + } + if (cmp < 0) + node = node->left; + else + node = node->right; + } + return NULL; +} + +// FindClose +AVL_TREE_TEMPLATE_LIST +Value * +AVL_TREE_CLASS_NAME::FindClose(const Key &key, bool less, Iterator *iterator) +{ + Node *node = fRoot; + Node *parent = NULL; + while (node) { + int cmp = fKeyCompare(key, fGetKey(fGetValue(node))); + if (cmp == 0) + break; + parent = node; + if (cmp < 0) + node = node->left; + else + node = node->right; + } + // not found: try to get close + if (!node && parent) { + node = parent; + int expectedCmp = (less ? -1 : 1); + int cmp = fKeyCompare(fGetKey(fGetValue(node)), key); + if (cmp != expectedCmp) { + // The node's value is less although for a greater value was asked, + // or the other way around. We need to iterate to the next node in + // the right directory. If there is no node, we fail. + Iterator it; + it._SetTo(this, node, less); + if (it.GetNext()) + node = it._GetCurrentNode(); + else + node = NULL; + } + } + // set the result + if (node) { + if (iterator) + iterator->_SetTo(this, node); + return &fGetValue(node); + } + return NULL; +} + +// GetIterator +AVL_TREE_TEMPLATE_LIST +void +AVL_TREE_CLASS_NAME::GetIterator(Iterator *iterator, bool reverse) +{ + if (iterator) + iterator->_SetTo(this, NULL, reverse, true); +} + +// Insert +AVL_TREE_TEMPLATE_LIST +status_t +AVL_TREE_CLASS_NAME::Insert(const Value &value, Iterator *iterator) +{ + int result = _Insert(value, &fRoot, iterator); + switch (result) { + case OK: + case HEIGHT_CHANGED: + return B_OK; + case NO_MEMORY: + return B_NO_MEMORY; + case DUPLICATE: + default: + return B_BAD_VALUE; + } +} + +// Remove +AVL_TREE_TEMPLATE_LIST +status_t +AVL_TREE_CLASS_NAME::Remove(const Key &key) +{ + // find node + Node *node = fRoot; + while (node) { + int cmp = fKeyCompare(key, fGetKey(fGetValue(node))); + if (cmp == 0) + break; + else { + if (cmp < 0) + node = node->left; + else + node = node->right; + } + } + // remove it + int result = _Remove(node); + // set result + switch (result) { + case OK: + case HEIGHT_CHANGED: + return B_OK; + case NOT_FOUND: + return B_ENTRY_NOT_FOUND; + default: + return B_BAD_VALUE; + } +} + +// Remove +AVL_TREE_TEMPLATE_LIST +void +AVL_TREE_CLASS_NAME::Remove(Iterator &iterator) +{ + if (Node *node = iterator._GetCurrentNode()) { + iterator.GetNext(); + _Remove(node); + } +} + +// Check +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::Check(Node *node, int level, bool levelsOnly) const +{ + int height = 0; + if (node) { + // check root node parent + if (node == fRoot && node->parent != NULL) { + printf("Root node has parent: %p\n", node->parent); + debugger("Root node has parent."); + } + // check children's parents + if (node->left && node->left->parent != node) { + printf("Left child of node has has wrong parent: %p, should be: " + "%p\n", node->left->parent, node); + _DumpNode(node); + debugger("Left child node has wrong parent."); + } + if (node->right && node->right->parent != node) { + printf("Right child of node has has wrong parent: %p, should be: " + "%p\n", node->right->parent, node); + _DumpNode(node); + debugger("Right child node has wrong parent."); + } + // check heights + int leftHeight = Check(node->left, level + 1); + int rightHeight = Check(node->right, level + 1); + if (node->balance_factor != rightHeight - leftHeight) { + printf("Subtree %p at level %d has wrong balance factor: left " + "height: %d, right height: %d, balance factor: %d\n", + node, level, leftHeight, rightHeight, node->balance_factor); + _DumpNode(node); + debugger("Node has wrong balance factor."); + } + // check AVL property + if (!levelsOnly && (leftHeight - rightHeight > 1 + || leftHeight - rightHeight < -1)) { + printf("Subtree %p at level %d violates the AVL property: left " + "height: %d, right height: %d\n", node, level, leftHeight, + rightHeight); + _DumpNode(node); + debugger("Node violates AVL property."); + } + height = max(leftHeight, rightHeight) + 1; + } + return height; +} + +// _RotateRight +AVL_TREE_TEMPLATE_LIST +void +AVL_TREE_CLASS_NAME::_RotateRight(Node **nodeP) +{ + // rotate the nodes + Node *node = *nodeP; + Node *left = node->left; +//printf("_RotateRight(): balance: node: %d, left: %d\n", +//node->balance_factor, left->balance_factor); + *nodeP = left; + left->parent = node->parent; + node->left = left->right; + if (left->right) + left->right->parent = node; + left->right = node; + node->parent = left; + // adjust the balance factors + // former pivot + if (left->balance_factor >= 0) + node->balance_factor++; + else + node->balance_factor += 1 - left->balance_factor; + // former left + if (node->balance_factor <= 0) + left->balance_factor++; + else + left->balance_factor += node->balance_factor + 1; +//printf("_RotateRight() end: balance: node: %d, left: %d\n", +//node->balance_factor, left->balance_factor); +} + +// _RotateLeft +AVL_TREE_TEMPLATE_LIST +void +AVL_TREE_CLASS_NAME::_RotateLeft(Node **nodeP) +{ + // rotate the nodes + Node *node = *nodeP; + Node *right = node->right; +//printf("_RotateLeft(): balance: node: %d, right: %d\n", +//node->balance_factor, right->balance_factor); + *nodeP = right; + right->parent = node->parent; + node->right = right->left; + if (right->left) + right->left->parent = node; + right->left = node; + node->parent = right; + // adjust the balance factors + // former pivot + if (right->balance_factor <= 0) + node->balance_factor--; + else + node->balance_factor -= right->balance_factor + 1; + // former right + if (node->balance_factor >= 0) + right->balance_factor--; + else + right->balance_factor += node->balance_factor - 1; +//printf("_RotateLeft() end: balance: node: %d, right: %d\n", +//node->balance_factor, right->balance_factor); +} + +// _BalanceInsertLeft +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_BalanceInsertLeft(Node **node) +{ +//printf("_BalanceInsertLeft()\n"); +//_DumpNode(*node); +//Check(*node, 0, true); + int result = HEIGHT_CHANGED; + if ((*node)->balance_factor < LEFT) { + // tree is left heavy + Node **left = &(*node)->left; + if ((*left)->balance_factor == LEFT) { + // left left heavy + _RotateRight(node); + } else { + // left right heavy + _RotateLeft(left); + _RotateRight(node); + } + result = OK; + } else if ((*node)->balance_factor == BALANCED) + result = OK; +//printf("_BalanceInsertLeft() done: %d\n", result); + return result; +} + +// _BalanceInsertRight +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_BalanceInsertRight(Node **node) +{ +//printf("_BalanceInsertRight()\n"); +//_DumpNode(*node); +//Check(*node, 0, true); + int result = HEIGHT_CHANGED; + if ((*node)->balance_factor > RIGHT) { + // tree is right heavy + Node **right = &(*node)->right; + if ((*right)->balance_factor == RIGHT) { + // right right heavy + _RotateLeft(node); + } else { + // right left heavy + _RotateRight(right); + _RotateLeft(node); + } + result = OK; + } else if ((*node)->balance_factor == BALANCED) + result = OK; +//printf("_BalanceInsertRight() done: %d\n", result); + return result; +} + +// _Insert +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_Insert(const Value &value, Node **node, + Iterator *iterator) +{ + struct node_info { + Node **node; + bool left; + }; + node_info stack[kMaxAVLTreeHeight]; + node_info *top = stack; + const node_info *const bottom = stack; + // find insertion point + while (*node) { + int cmp = fKeyCompare(fGetKey(value), fGetKey(fGetValue(*node))); + if (cmp == 0) // duplicate node + return DUPLICATE; + else { + top->node = node; + if (cmp < 0) { + top->left = true; + node = &(*node)->left; + } else { + top->left = false; + node = &(*node)->right; + } + top++; + } + } + // allocate and insert node + *node = fAllocator.Allocate(value); + if (*node) { + (*node)->balance_factor = BALANCED; + fNodeCount++; + } else + return NO_MEMORY; + if (top != bottom) + (*node)->parent = *top[-1].node; + // init the iterator + if (iterator) + iterator->_SetTo(this, *node); + // do the balancing + int result = HEIGHT_CHANGED; + while (result == HEIGHT_CHANGED && top != bottom) { + top--; + node = top->node; + if (top->left) { + // left + (*node)->balance_factor--; + result = _BalanceInsertLeft(node); + } else { + // right + (*node)->balance_factor++; + result = _BalanceInsertRight(node); + } + } +//Check(*node); + return result; +} + +// _BalanceRemoveLeft +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_BalanceRemoveLeft(Node **node) +{ +//printf("_BalanceRemoveLeft()\n"); +//_DumpNode(*node); +//Check(*node, 0, true); + int result = HEIGHT_CHANGED; + if ((*node)->balance_factor > RIGHT) { + // tree is right heavy + Node **right = &(*node)->right; + if ((*right)->balance_factor == RIGHT) { + // right right heavy + _RotateLeft(node); + } else if ((*right)->balance_factor == BALANCED) { + // right none heavy + _RotateLeft(node); + result = OK; + } else { + // right left heavy + _RotateRight(right); + _RotateLeft(node); + } + } else if ((*node)->balance_factor == RIGHT) + result = OK; +//printf("_BalanceRemoveLeft() done: %d\n", result); + return result; +} + +// _BalanceRemoveRight +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_BalanceRemoveRight(Node **node) +{ +//printf("_BalanceRemoveRight()\n"); +//_DumpNode(*node); +//Check(*node, 0, true); + int result = HEIGHT_CHANGED; + if ((*node)->balance_factor < LEFT) { + // tree is left heavy + Node **left = &(*node)->left; + if ((*left)->balance_factor == LEFT) { + // left left heavy + _RotateRight(node); + } else if ((*left)->balance_factor == BALANCED) { + // left none heavy + _RotateRight(node); + result = OK; + } else { + // left right heavy + _RotateLeft(left); + _RotateRight(node); + } + } else if ((*node)->balance_factor == LEFT) + result = OK; +//printf("_BalanceRemoveRight() done: %d\n", result); + return result; +} + +// _RemoveRightMostChild +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_RemoveRightMostChild(Node **node, Node **foundNode) +{ + Node **stack[kMaxAVLTreeHeight]; + Node ***top = stack; + const Node *const *const *const bottom = stack; + // find the child + while ((*node)->right) { + *top = node; + top++; + node = &(*node)->right; + } + // found the rightmost child: remove it + // the found node might have a left child: replace the node with the + // child + *foundNode = *node; + Node *left = (*node)->left; + if (left) + left->parent = (*node)->parent; + *node = left; + (*foundNode)->left = NULL; + (*foundNode)->parent = NULL; + // balancing + int result = HEIGHT_CHANGED; + while (result == HEIGHT_CHANGED && top != bottom) { + top--; + node = *top; + (*node)->balance_factor--; + result = _BalanceRemoveRight(node); + } + return result; +} + +// _Remove +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_Remove(const Key &key, Node **node) +{ + struct node_info { + Node **node; + bool left; + }; + node_info stack[kMaxAVLTreeHeight]; + node_info *top = stack; + const node_info *const bottom = stack; + // find node + while (*node) { + int cmp = fKeyCompare(key, fGetKey(fGetValue(*node))); + if (cmp == 0) + break; + else { + top->node = node; + if (cmp < 0) { + top->left = true; + node = &(*node)->left; + } else { + top->left = false; + node = &(*node)->right; + } + top++; + } + } + if (!*node) + return NOT_FOUND; + // remove and free node + int result = HEIGHT_CHANGED; + Node *oldNode = *node; + Node *replace = NULL; + if ((*node)->left && (*node)->right) { + // node has two children + result = _RemoveRightMostChild(&(*node)->left, &replace); + replace->parent = (*node)->parent; + replace->left = (*node)->left; + replace->right = (*node)->right; + if ((*node)->left) // check necessary, if (*node)->left == replace + (*node)->left->parent = replace; + (*node)->right->parent = replace; + replace->balance_factor = (*node)->balance_factor; + *node = replace; + if (result == HEIGHT_CHANGED) { + replace->balance_factor++; + result = _BalanceRemoveLeft(node); + } + } else if ((*node)->left) { + // node has only left child + replace = (*node)->left; + replace->parent = (*node)->parent; + replace->balance_factor = (*node)->balance_factor + 1; + *node = replace; + } else if ((*node)->right) { + // node has only right child + replace = (*node)->right; + replace->parent = (*node)->parent; + replace->balance_factor = (*node)->balance_factor - 1; + *node = replace; + } else { + // node has no child + *node = NULL; + } + fAllocator.Free(oldNode); + fNodeCount--; + // do the balancing + while (result == HEIGHT_CHANGED && top != bottom) { + top--; + node = top->node; + if (top->left) { + // left + (*node)->balance_factor++; + result = _BalanceRemoveLeft(node); + } else { + // right + (*node)->balance_factor--; + result = _BalanceRemoveRight(node); + } + } +//Check(*node); + return result; +} + +// _Remove +AVL_TREE_TEMPLATE_LIST +int +AVL_TREE_CLASS_NAME::_Remove(Node *node) +{ + if (!node) + return NOT_FOUND; + // remove and free node + Node *parent = node->parent; + bool isLeft = (parent && parent->left == node); + Node **nodeP + = (parent ? (isLeft ? &parent->left : &parent->right) : &fRoot); + int result = HEIGHT_CHANGED; + Node *replace = NULL; + if (node->left && node->right) { + // node has two children + result = _RemoveRightMostChild(&node->left, &replace); + replace->parent = parent; + replace->left = node->left; + replace->right = node->right; + if (node->left) // check necessary, if node->left == replace + node->left->parent = replace; + node->right->parent = replace; + replace->balance_factor = node->balance_factor; + *nodeP = replace; + if (result == HEIGHT_CHANGED) { + replace->balance_factor++; + result = _BalanceRemoveLeft(nodeP); + } + } else if (node->left) { + // node has only left child + replace = node->left; + replace->parent = parent; + replace->balance_factor = node->balance_factor + 1; + *nodeP = replace; + } else if (node->right) { + // node has only right child + replace = node->right; + replace->parent = node->parent; + replace->balance_factor = node->balance_factor - 1; + *nodeP = replace; + } else { + // node has no child + *nodeP = NULL; + } + fAllocator.Free(node); + fNodeCount--; + // do the balancing + while (result == HEIGHT_CHANGED && parent) { + node = parent; + parent = node->parent; + bool oldIsLeft = isLeft; + isLeft = (parent && parent->left == node); + nodeP = (parent ? (isLeft ? &parent->left : &parent->right) : &fRoot); + if (oldIsLeft) { + // left + node->balance_factor++; + result = _BalanceRemoveLeft(nodeP); + } else { + // right + node->balance_factor--; + result = _BalanceRemoveRight(nodeP); + } + } +//Check(node); + return result; +} + +// _FreeTree +AVL_TREE_TEMPLATE_LIST +void +AVL_TREE_CLASS_NAME::_FreeTree(Node *node) +{ + if (node) { + _FreeTree(node->left); + _FreeTree(node->right); + fAllocator.Free(node); + } +} + +// _DumpNode +AVL_TREE_TEMPLATE_LIST +void +AVL_TREE_CLASS_NAME::_DumpNode(Node *node) const +{ + if (!node) + return; + + enum node_type { + ROOT, + LEFT, + RIGHT, + }; + struct node_info { + Node *node; + int id; + int parent; + int level; + int type; + }; + + node_info *queue = new(nothrow) node_info[fNodeCount]; + if (!queue) { + printf("_Dump(): Insufficient memory for allocating queue.\n"); + } + node_info *front = queue; + node_info *back = queue; + back->node = node; + back->id = 0; + back->level = 0; + back->type = ROOT; + back++; + int level = 0; + int nextID = 1; + while (front != back) { + // pop front + node_info *current = front; + front++; + // get to the correct level + node = current->node; + if (level < current->level) { + printf("\n"); + level++; + } + // print node + switch (current->type) { + case ROOT: + printf("[%d:%d]", current->id, node->balance_factor); + break; + case LEFT: + printf("[%d:L:%d:%d]", current->id, current->parent, + node->balance_factor); + break; + case RIGHT: + printf("[%d:R:%d:%d]", current->id, current->parent, + node->balance_factor); + break; + } + // add child nodes + if (node->left) { + back->node = node->left; + back->id = nextID++; + back->parent = current->id; + back->level = current->level + 1; + back->type = LEFT; + back++; + } + if (node->right) { + back->node = node->right; + back->id = nextID++; + back->parent = current->id; + back->level = current->level + 1; + back->type = RIGHT; + back++; + } + } + printf("\n\n"); + delete[] queue; +} + +// _InitIterator +AVL_TREE_TEMPLATE_LIST +inline +void +AVL_TREE_CLASS_NAME::_InitIterator(Iterator *iterator, Node *node, + bool reverse, bool initialize) +{ + iterator->_SetTo(this, node, reverse, initialize); +} + +#endif // AVL_TREE_H diff --git a/src/add-ons/kernel/file_systems/ramfs/AllocationInfo.cpp b/src/add-ons/kernel/file_systems/ramfs/AllocationInfo.cpp new file mode 100644 index 0000000000..7665fafdfa --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AllocationInfo.cpp @@ -0,0 +1,236 @@ +// AllocationInfo.cpp + +#include "AllocationInfo.h" +#include "Debug.h" + +#include "Attribute.h" +#include "Directory.h" +#include "Entry.h" +#include "File.h" +#include "SymLink.h" + +// constructor +AllocationInfo::AllocationInfo() + : fNodeTableArraySize(0), + fNodeTableVectorSize(0), + fNodeTableElementCount(0), + fDirectoryEntryTableArraySize(0), + fDirectoryEntryTableVectorSize(0), + fDirectoryEntryTableElementCount(0), + fNodeAttributeTableArraySize(0), + fNodeAttributeTableVectorSize(0), + fNodeAttributeTableElementCount(0), + + fAttributeCount(0), + fAttributeSize(0), + fDirectoryCount(0), + fEntryCount(0), + fFileCount(0), + fFileSize(0), + fSymLinkCount(0), + fSymLinkSize(0), + + fAreaCount(0), + fAreaSize(0), + fBlockCount(0), + fBlockSize(0), + fListCount(0), + fListSize(0), + fOtherCount(0), + fOtherSize(0), + fStringCount(0), + fStringSize(0) +{ +} + +// destructor +AllocationInfo::~AllocationInfo() +{ +} + +// AddNodeTableAllocation +void +AllocationInfo::AddNodeTableAllocation(size_t arraySize, size_t vectorSize, + size_t elementSize, size_t elementCount) +{ + fNodeTableArraySize += arraySize; + fNodeTableVectorSize += vectorSize * elementSize; + fNodeTableElementCount += elementCount; +} + +// AddDirectoryEntryTableAllocation +void +AllocationInfo::AddDirectoryEntryTableAllocation(size_t arraySize, + size_t vectorSize, + size_t elementSize, + size_t elementCount) +{ + fDirectoryEntryTableArraySize += arraySize; + fDirectoryEntryTableVectorSize += vectorSize * elementSize; + fDirectoryEntryTableElementCount += elementCount; +} + +// AddNodeAttributeTableAllocation +void +AllocationInfo::AddNodeAttributeTableAllocation(size_t arraySize, + size_t vectorSize, + size_t elementSize, + size_t elementCount) +{ + fNodeAttributeTableArraySize += arraySize; + fNodeAttributeTableVectorSize += vectorSize * elementSize; + fNodeAttributeTableElementCount += elementCount; +} + +// AddAttributeAllocation +void +AllocationInfo::AddAttributeAllocation(size_t size) +{ + fAttributeCount++; + fAttributeSize += size; +} + +// AddDirectoryAllocation +void +AllocationInfo::AddDirectoryAllocation() +{ + fDirectoryCount++; +} + +// AddEntryAllocation +void +AllocationInfo::AddEntryAllocation() +{ + fEntryCount++; +} + +// AddFileAllocation +void +AllocationInfo::AddFileAllocation(size_t size) +{ + fFileCount++; + fFileSize += size; +} + +// AddSymLinkAllocation +void +AllocationInfo::AddSymLinkAllocation(size_t size) +{ + fSymLinkCount++; + fSymLinkSize += size; +} + +// AddAreaAllocation +void +AllocationInfo::AddAreaAllocation(size_t size, size_t count) +{ + fAreaCount += count; + fAreaSize += count * size; +} + +// AddBlockAllocation +void +AllocationInfo::AddBlockAllocation(size_t size) +{ + fBlockCount++; + fBlockSize += size; +} + +// AddListAllocation +void +AllocationInfo::AddListAllocation(size_t capacity, size_t elementSize) +{ + fListCount += 1; + fListSize += capacity * elementSize; +} + +// AddOtherAllocation +void +AllocationInfo::AddOtherAllocation(size_t size, size_t count) +{ + fOtherCount += count; + fOtherSize += size * count; +} + +// AddStringAllocation +void +AllocationInfo::AddStringAllocation(size_t size) +{ + fStringCount++; + fStringSize += size; +} + +// Dump +void +AllocationInfo::Dump() const +{ + size_t heapCount = 0; + size_t heapSize = 0; + size_t areaCount = 0; + size_t areaSize = 0; + + PRINT((" node table:\n")); + PRINT((" array size: %9lu\n", fNodeTableArraySize)); + PRINT((" vector size: %9lu\n", fNodeTableVectorSize)); + PRINT((" elements: %9lu\n", fNodeTableElementCount)); + areaCount += 2; + areaSize += fNodeTableArraySize * sizeof(int32) + fNodeTableVectorSize; + + PRINT((" entry table:\n")); + PRINT((" array size: %9lu\n", fDirectoryEntryTableArraySize)); + PRINT((" vector size: %9lu\n", fDirectoryEntryTableVectorSize)); + PRINT((" elements: %9lu\n", fDirectoryEntryTableElementCount)); + areaCount += 2; + areaSize += fDirectoryEntryTableArraySize * sizeof(int32) + + fDirectoryEntryTableVectorSize; + + PRINT((" attribute table:\n")); + PRINT((" array size: %9lu\n", fNodeAttributeTableArraySize)); + PRINT((" vector size: %9lu\n", fNodeAttributeTableVectorSize)); + PRINT((" elements: %9lu\n", fNodeAttributeTableElementCount)); + areaCount += 2; + areaSize += fNodeAttributeTableArraySize * sizeof(int32) + + fNodeAttributeTableVectorSize; + + PRINT((" attributes: %9lu, size: %9lu\n", fAttributeCount, fAttributeSize)); + heapCount += fAttributeCount; + heapSize += fAttributeCount * sizeof(Attribute); + + PRINT((" directories: %9lu\n", fDirectoryCount)); + heapCount += fDirectoryCount; + heapSize += fDirectoryCount * sizeof(Directory); + + PRINT((" entries: %9lu\n", fEntryCount)); + heapCount += fEntryCount; + heapSize += fEntryCount * sizeof(Entry); + + PRINT((" files: %9lu, size: %9lu\n", fFileCount, fFileSize)); + heapCount += fFileCount; + heapSize += fFileCount * sizeof(File); + + PRINT((" symlinks: %9lu, size: %9lu\n", fSymLinkCount, fSymLinkSize)); + heapCount += fSymLinkCount; + heapSize += fSymLinkCount * sizeof(SymLink); + + PRINT((" areas: %9lu, size: %9lu\n", fAreaCount, fAreaSize)); + areaCount += fAreaCount; + areaSize += fAreaSize; + + PRINT((" blocks: %9lu, size: %9lu\n", fBlockCount, fBlockSize)); + + PRINT((" lists: %9lu, size: %9lu\n", fListCount, fListSize)); + heapCount += fListCount; + heapSize += fListSize; + + PRINT((" other: %9lu, size: %9lu\n", fOtherCount, fOtherSize)); + heapCount += fOtherCount; + heapSize += fOtherSize; + + PRINT((" strings: %9lu, size: %9lu\n", fStringCount, fStringSize)); + heapCount += fStringCount; + heapSize += fStringSize; + + PRINT(("heap: %9lu allocations, size: %9lu\n", heapCount, heapSize)); + PRINT(("areas: %9lu allocations, size: %9lu\n", areaCount, areaSize)); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/AllocationInfo.h b/src/add-ons/kernel/file_systems/ramfs/AllocationInfo.h new file mode 100644 index 0000000000..da12677e15 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AllocationInfo.h @@ -0,0 +1,68 @@ +// AllocationInfo.h + +#ifndef ALLOCATION_INFO_H +#define ALLOCATION_INFO_H + +#include + +class AllocationInfo { +public: + AllocationInfo(); + ~AllocationInfo(); + + void AddNodeTableAllocation(size_t arraySize, size_t vectorSize, + size_t elementSize, size_t elementCount); + void AddDirectoryEntryTableAllocation(size_t arraySize, size_t vectorSize, + size_t elementSize, + size_t elementCount); + void AddNodeAttributeTableAllocation(size_t arraySize, size_t vectorSize, + size_t elementSize, + size_t elementCount); + + void AddAttributeAllocation(size_t size); + void AddDirectoryAllocation(); + void AddEntryAllocation(); + void AddFileAllocation(size_t size); + void AddSymLinkAllocation(size_t size); + + void AddAreaAllocation(size_t size, size_t count = 1); + void AddBlockAllocation(size_t size); + void AddListAllocation(size_t capacity, size_t elementSize); + void AddOtherAllocation(size_t size, size_t count = 1); + void AddStringAllocation(size_t size); + + void Dump() const; + +private: + size_t fNodeTableArraySize; + size_t fNodeTableVectorSize; + size_t fNodeTableElementCount; + size_t fDirectoryEntryTableArraySize; + size_t fDirectoryEntryTableVectorSize; + size_t fDirectoryEntryTableElementCount; + size_t fNodeAttributeTableArraySize; + size_t fNodeAttributeTableVectorSize; + size_t fNodeAttributeTableElementCount; + + size_t fAttributeCount; + size_t fAttributeSize; + size_t fDirectoryCount; + size_t fEntryCount; + size_t fFileCount; + size_t fFileSize; + size_t fSymLinkCount; + size_t fSymLinkSize; + + size_t fAreaCount; + size_t fAreaSize; + size_t fBlockCount; + size_t fBlockSize; + size_t fListCount; + size_t fListSize; + size_t fOtherCount; + size_t fOtherSize; + size_t fStringCount; + size_t fStringSize; +}; + +#endif // ALLOCATION_INFO_H diff --git a/src/add-ons/kernel/file_systems/ramfs/AreaUtils.cpp b/src/add-ons/kernel/file_systems/ramfs/AreaUtils.cpp new file mode 100644 index 0000000000..4af567880f --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AreaUtils.cpp @@ -0,0 +1,160 @@ +// AreaUtils.cpp +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#include +#include + +#include "AreaUtils.h" +#include "Debug.h" + +#ifndef USE_STANDARD_FUNCTIONS +#define USE_STANDARD_FUNCTIONS 0 +#endif + + +// area_info_for +static +status_t +area_info_for(void *address, area_info *info) +{ + status_t error = B_OK; + if (address) { + // get the area ID for the ptr + area_id area = area_for(address); + // check if supplied pointer points to the beginning of the area + if (area >= 0) + error = get_area_info(area, info); + else + error = area; + } else + error = B_BAD_VALUE; + return error; +} + +// calloc +void * +AreaUtils::calloc(size_t nmemb, size_t size) +{ +//PRINT(("AreaUtils::calloc(%lu, %lu)\n", nmemb, size)); + return AreaUtils::malloc(nmemb * size); +} + +// free +void +AreaUtils::free(void *ptr) +{ +//PRINT(("AreaUtils::free(%p)\n", ptr)); +#if USE_STANDARD_FUNCTIONS + return ::free(ptr); +#else + if (ptr) { + // get the area for the pointer + area_info info; + if (area_info_for(ptr, &info) == B_OK) { + if (ptr == info.address) { + // everything is fine, delete the area + delete_area(info.area); + } else { + INFORM(("WARNING: AreaUtils::free(%p): area begin is %p." + "Ignored.\n", ptr, info.address)); + } + } + } +#endif +} + +// malloc +void * +AreaUtils::malloc(size_t size) +{ +//PRINT(("AreaUtils::malloc(%lu)\n", size)); +#if USE_STANDARD_FUNCTIONS + return ::malloc(size); +#else + void *address = NULL; + if (size > 0) { + // round to multiple of page size + size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE; + // create an area +#if USER + area_id area = create_area("AreaUtils::malloc", &address, + B_ANY_ADDRESS, size, B_NO_LOCK, + B_WRITE_AREA | B_READ_AREA); +#else + area_id area = create_area("AreaUtils::malloc", &address, + B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK, + B_READ_AREA | B_WRITE_AREA); +#endif + if (area < 0) + address = NULL; + } + return address; +#endif +} + +// realloc +void * +AreaUtils::realloc(void * ptr, size_t size) +{ +//PRINT(("AreaUtils::realloc(%p, %lu)\n", ptr, size)) +#if USE_STANDARD_FUNCTIONS + return ::realloc(ptr, size); +#else + void *newAddress = NULL; + if (size == 0) { + AreaUtils::free(ptr); + } else if (ptr) { + // get the area for the pointer + area_info info; + if (area_info_for(ptr, &info) == B_OK) { + if (ptr == info.address) { + // round to multiple of page size + size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE; + if (size == info.size) { + // nothing to do + newAddress = ptr; + } else if (resize_area(info.area, size) == B_OK) { + // resizing the area went fine + newAddress = ptr; + } else { + // resizing the area failed: we need to allocate a new one + newAddress = AreaUtils::malloc(size); + if (newAddress) { + memcpy(newAddress, ptr, min(size, info.size)); + delete_area(info.area); + } + } + } else { + INFORM(("WARNING: AreaUtils::realloc(%p): area begin is %p." + "Ignored.\n", ptr, info.address)); + } + } + } + return newAddress; +#endif +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/AreaUtils.h b/src/add-ons/kernel/file_systems/ramfs/AreaUtils.h new file mode 100644 index 0000000000..7fb069b378 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AreaUtils.h @@ -0,0 +1,40 @@ +// AreaUtils.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef AREA_UTILS_H +#define AREA_UTILS_H + +namespace AreaUtils { + + void *calloc(size_t nmemb, size_t size); + void free(void *ptr); + void *malloc(size_t size); + void *realloc(void * ptr, size_t size); + +}; + +#endif // AREA_UTILS_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Attribute.cpp b/src/add-ons/kernel/file_systems/ramfs/Attribute.cpp new file mode 100644 index 0000000000..75f74e4d58 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Attribute.cpp @@ -0,0 +1,136 @@ +// Attribute.cpp + +#include "AllocationInfo.h" +#include "Attribute.h" +#include "Misc.h" +#include "Node.h" +#include "ramfs.h" +#include "Volume.h" + +// constructor +Attribute::Attribute(Volume *volume, Node *node, const char *name, + uint32 type) + : DataContainer(volume), + fNode(node), + fName(name), + fType(type), + fIndex(NULL), + fInIndex(false), + fIterators() +{ +} + +// destructor +Attribute::~Attribute() +{ +} + +// InitCheck +status_t +Attribute::InitCheck() const +{ + return (fName.GetString() ? B_OK : B_NO_INIT); +} + +// SetType +void +Attribute::SetType(uint32 type) +{ + if (type != fType) { + if (fIndex) + fIndex->Removed(this); + fType = type; + if (AttributeIndex *index = GetVolume()->FindAttributeIndex(GetName(), + fType)) { + index->Added(this); + } + } +} + +// WriteAt +status_t +Attribute::WriteAt(off_t offset, const void *buffer, size_t size, + size_t *bytesWritten) +{ + // get the current key for the attribute + uint8 oldKey[kMaxIndexKeyLength]; + size_t oldLength; + GetKey(oldKey, &oldLength); + + // write the new value + status_t error = DataContainer::WriteAt(offset, buffer, size, bytesWritten); + + // If there is an index and a change has been made within the key, notify + // the index. + if (offset < kMaxIndexKeyLength && size > 0 && fIndex) + fIndex->Changed(this, oldKey, oldLength); + + // update live queries + const uint8* newKey; + size_t newLength; + GetKey(&newKey, &newLength); + GetVolume()->UpdateLiveQueries(NULL, fNode, GetName(), fType, oldKey, + oldLength, newKey, newLength); + + // node has been changed + if (fNode && size > 0) + fNode->MarkModified(); + + return error; +} + +// SetIndex +void +Attribute::SetIndex(AttributeIndex *index, bool inIndex) +{ + fIndex = index; + fInIndex = inIndex; +} + +// GetKey +void +Attribute::GetKey(const uint8 **key, size_t *length) +{ + if (key && length) { + GetFirstDataBlock(key, length); + *length = min(*length, kMaxIndexKeyLength); + } +} + +// GetKey +void +Attribute::GetKey(uint8 *key, size_t *length) +{ + if (key && length) { + const uint8 *originalKey = NULL; + GetKey(&originalKey, length); + if (length > 0) + memcpy(key, originalKey, *length); + } +} + +// AttachAttributeIterator +void +Attribute::AttachAttributeIterator(AttributeIterator *iterator) +{ + if (iterator && iterator->GetCurrent() == this && !iterator->IsSuspended()) + fIterators.Insert(iterator); +} + +// DetachAttributeIterator +void +Attribute::DetachAttributeIterator(AttributeIterator *iterator) +{ + if (iterator && iterator->GetCurrent() == this && iterator->IsSuspended()) + fIterators.Remove(iterator); +} + +// GetAllocationInfo +void +Attribute::GetAllocationInfo(AllocationInfo &info) +{ + DataContainer::GetAllocationInfo(info); + info.AddAttributeAllocation(GetSize()); + info.AddStringAllocation(fName.GetLength()); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/Attribute.h b/src/add-ons/kernel/file_systems/ramfs/Attribute.h new file mode 100644 index 0000000000..d383fe431c --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Attribute.h @@ -0,0 +1,61 @@ +// Attribute.h + +#ifndef ATTRIBUTE_H +#define ATTRIBUTE_H + +#include "AttributeIndex.h" +#include "AttributeIterator.h" +#include "DataContainer.h" +#include "DLList.h" +#include "String.h" + +class AllocationInfo; +class Node; +class Volume; + +class Attribute : public DataContainer, public DLListLinkImpl { +public: + Attribute(Volume *volume, Node *node, const char *name, uint32 type = 0); + ~Attribute(); + + status_t InitCheck() const; + + void SetNode(Node *node) { fNode = node; } + Node *GetNode() const { return fNode; } + + const char *GetName() { return fName.GetString(); } + + void SetType(uint32 type); + uint32 GetType() const { return fType; } + + virtual status_t WriteAt(off_t offset, const void *buffer, size_t size, + size_t *bytesWritten); + + // index support + void SetIndex(AttributeIndex *index, bool inIndex); + AttributeIndex *GetIndex() const { return fIndex; } + bool IsInIndex() const { return fInIndex; } + void GetKey(const uint8 **key, size_t *length); + void GetKey(uint8 *key, size_t *length); + + // iterator management + void AttachAttributeIterator(AttributeIterator *iterator); + void DetachAttributeIterator(AttributeIterator *iterator); + inline DLList *GetAttributeIteratorList() + { return &fIterators; } + + // debugging + void GetAllocationInfo(AllocationInfo &info); + +private: + Node *fNode; + String fName; + uint32 fType; + AttributeIndex *fIndex; + bool fInIndex; + + // iterator management + DLList fIterators; +}; + +#endif // ATTRIBUTE_H diff --git a/src/add-ons/kernel/file_systems/ramfs/AttributeIndex.cpp b/src/add-ons/kernel/file_systems/ramfs/AttributeIndex.cpp new file mode 100644 index 0000000000..3d85ec0824 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AttributeIndex.cpp @@ -0,0 +1,16 @@ +// AttributeIndex.cpp + +#include "AttributeIndex.h" + +// constructor +AttributeIndex::AttributeIndex(Volume *volume, const char *name, uint32 type, + bool fixedKeyLength, size_t keyLength) + : Index(volume, name, type, fixedKeyLength, keyLength) +{ +} + +// destructor +AttributeIndex::~AttributeIndex() +{ +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/AttributeIndex.h b/src/add-ons/kernel/file_systems/ramfs/AttributeIndex.h new file mode 100644 index 0000000000..0fc73d92bb --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AttributeIndex.h @@ -0,0 +1,22 @@ +// AttributeIndex.h + +#ifndef ATTRIBUTE_INDEX_H +#define ATTRIBUTE_INDEX_H + +#include "Index.h" + +class Attribute; + +class AttributeIndex : public Index { +public: + AttributeIndex(Volume *volume, const char *name, uint32 type, + bool fixedKeyLength, size_t keyLength = 0); + virtual ~AttributeIndex(); + + virtual status_t Added(Attribute *attribute) = 0; + virtual bool Removed(Attribute *attribute) = 0; + virtual status_t Changed(Attribute *attribute, + const uint8 *oldKey, size_t length) = 0; +}; + +#endif // ATTRIBUTE_INDEX_H diff --git a/src/add-ons/kernel/file_systems/ramfs/AttributeIndexImpl.cpp b/src/add-ons/kernel/file_systems/ramfs/AttributeIndexImpl.cpp new file mode 100644 index 0000000000..a39ebb2d9f --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AttributeIndexImpl.cpp @@ -0,0 +1,514 @@ +// AttributeIndexImpl.cpp + +#include + +#include "AttributeIndexImpl.h" +#include "Debug.h" +#include "Entry.h" +#include "EntryListener.h" +#include "IndexImpl.h" +#include "Misc.h" +#include "Node.h" +#include "NodeListener.h" +#include "ramfs.h" +#include "TwoKeyAVLTree.h" +#include "Volume.h" + +// compare_integral +template +static inline +int +compare_integral(const Key &a, const Key &b) +{ + if (a < b) + return -1; + else if (a > b) + return 1; + return 0; +} + +// compare_keys +static +int +compare_keys(const uint8 *key1, size_t length1, const uint8 *key2, + size_t length2, uint32 type) +{ + switch (type) { + case B_INT32_TYPE: + return compare_integral(*(int32*)key1, *(int32*)key2); + case B_UINT32_TYPE: + return compare_integral(*(uint32*)key1, *(uint32*)key2); + case B_INT64_TYPE: + return compare_integral(*(int64*)key1, *(int64*)key2); + case B_UINT64_TYPE: + return compare_integral(*(uint64*)key1, *(uint64*)key2); + case B_FLOAT_TYPE: + return compare_integral(*(float*)key1, *(float*)key2); + case B_DOUBLE_TYPE: + return compare_integral(*(double*)key1, *(double*)key2); + case B_STRING_TYPE: + { + int result = strncmp((const char*)key1, (const char*)key2, + min(length1, length2)); + if (result == 0) { + result = compare_integral(strnlen((const char*)key1, length1), + strnlen((const char*)key2, length2)); + } + return result; + } + } + return -1; +} + +// PrimaryKey +class AttributeIndexImpl::PrimaryKey { +public: + PrimaryKey(Attribute *attribute, const uint8 *key, + size_t length) + : attribute(attribute), key(key), length(length) {} + PrimaryKey(Attribute *attribute) + : attribute(attribute) { attribute->GetKey(&key, &length); } + PrimaryKey(const uint8 *key, size_t length) + : attribute(NULL), key(key), length(length) {} + + Attribute *attribute; + const uint8 *key; + size_t length; +}; + +// GetPrimaryKey +class AttributeIndexImpl::GetPrimaryKey { +public: + inline PrimaryKey operator()(Attribute *a) + { + return PrimaryKey(a); + } + + inline PrimaryKey operator()(Attribute *a) const + { + return PrimaryKey(a); + } +}; + +// PrimaryKeyCompare +class AttributeIndexImpl::PrimaryKeyCompare +{ +public: + PrimaryKeyCompare(uint32 type) : fType(type) {} + + inline int operator()(const PrimaryKey &a, + const PrimaryKey &b) const + { + if (a.attribute != NULL && a.attribute == b.attribute) + return 0; + return compare_keys(a.key, a.length, b.key, b.length, fType); + } + + uint32 fType; +}; + +// AttributeNodeIterator +template +class AttributeNodeIterator { +public: + inline Node **GetCurrent() + { + if (Attribute **attribute = fIterator.GetCurrent()) { + fNode = (*attribute)->GetNode(); + return &fNode; + } + return NULL; + } + + inline Node **GetNext() + { + if (Attribute **attribute = fIterator.GetNext()) { + fNode = (*attribute)->GetNode(); + return &fNode; + } + return NULL; + } + + AttributeIterator fIterator; + Node *fNode; +}; + + +// AttributeTree +class AttributeIndexImpl::AttributeTree + : public TwoKeyAVLTree { +public: + AttributeTree(uint32 type) + : TwoKeyAVLTree(PrimaryKeyCompare(type), + GetPrimaryKey(), AVLTreeStandardCompare(), + AVLTreeStandardGetKey(), + AVLTreeStandardNodeAllocator >(), + AVLTreeStandardGetValue >()) + { + } +}; + + +// Iterator +class AttributeIndexImpl::Iterator + : public NodeEntryIterator< + AttributeNodeIterator >, + public DLListLinkImpl, public EntryListener, + public NodeListener { +public: + Iterator(); + virtual ~Iterator(); + + virtual Entry *GetCurrent(); + virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength); + + virtual status_t Suspend(); + virtual status_t Resume(); + + bool SetTo(AttributeIndexImpl *index, const uint8 *key, size_t length, + bool ignoreValue = false); + void Unset(); + + virtual void EntryRemoved(Entry *entry); + virtual void NodeRemoved(Node *node); + +private: + typedef NodeEntryIterator< + AttributeNodeIterator > BaseClass; + +private: + AttributeIndexImpl *fIndex; +}; + + +// IteratorList +class AttributeIndexImpl::IteratorList : public DLList {}; + + +// AttributeIndexImpl + +// constructor +AttributeIndexImpl::AttributeIndexImpl(Volume *volume, const char *name, + uint32 type, size_t keyLength) + : AttributeIndex(volume, name, type, (keyLength > 0), keyLength), + fAttributes(new(nothrow) AttributeTree(type)), + fIterators(new(nothrow) IteratorList) +{ + if (fInitStatus == B_OK && (!fAttributes || !fIterators)) + fInitStatus = B_NO_MEMORY; +} + +// destructor +AttributeIndexImpl::~AttributeIndexImpl() +{ + if (fIterators) { + // unset the iterators + for (Iterator *iterator = fIterators->GetFirst(); + iterator; + iterator = fIterators->GetNext(iterator)) { + iterator->SetTo(NULL, NULL, 0); + } + delete fIterators; + } + // unset all attributes and delete the tree + if (fAttributes) { + AttributeTree::Iterator it; + fAttributes->GetIterator(&it); + for (Attribute **attribute = it.GetCurrent(); attribute; it.GetNext()) + (*attribute)->SetIndex(NULL, false); + delete fAttributes; + } +} + +// CountEntries +int32 +AttributeIndexImpl::CountEntries() const +{ + return fAttributes->CountItems(); +} + +// Changed +status_t +AttributeIndexImpl::Changed(Attribute *attribute, const uint8 *oldKey, + size_t oldLength) +{ + status_t error = B_BAD_VALUE; + if (attribute && attribute->GetIndex() == this) { + // update the iterators and remove the attribute from the tree + error = B_OK; + if (attribute->IsInIndex()) { + AttributeTree::Iterator it; + Attribute **foundAttribute = fAttributes->Find( + PrimaryKey(attribute, oldKey, oldLength), attribute, &it); + if (foundAttribute && *foundAttribute == attribute) { + Node *node = attribute->GetNode(); + // update the iterators + for (Iterator *iterator = fIterators->GetFirst(); + iterator; + iterator = fIterators->GetNext(iterator)) { + if (iterator->GetCurrentNode() == node) + iterator->NodeRemoved(node); + } + // remove and re-insert the attribute + fAttributes->Remove(it); + } + } + // re-insert the attribute + if (fKeyLength > 0 && attribute->GetSize() != fKeyLength) { + attribute->SetIndex(this, false); + } else { + error = fAttributes->Insert(attribute); + if (error == B_OK) + attribute->SetIndex(this, true); + else + attribute->SetIndex(NULL, false); + } + } + return error; +} + +// Added +status_t +AttributeIndexImpl::Added(Attribute *attribute) +{ +PRINT(("AttributeIndex::Add(%p)\n", attribute)); + status_t error = (attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + size_t size = attribute->GetSize(); + if (fKeyLength > 0 && size != fKeyLength) { + attribute->SetIndex(this, false); + } else { + error = fAttributes->Insert(attribute); + if (error == B_OK) + attribute->SetIndex(this, true); + } + } + return error; +} + +// Removed +bool +AttributeIndexImpl::Removed(Attribute *attribute) +{ +PRINT(("AttributeIndex::Removed(%p)\n", attribute)); + bool result = (attribute && attribute->GetIndex() == this); + if (result) { + if (attribute->IsInIndex()) + fAttributes->Remove(attribute, attribute); + attribute->SetIndex(NULL, false); + } + return result; +} + +// InternalGetIterator +AbstractIndexEntryIterator * +AttributeIndexImpl::InternalGetIterator() +{ + Iterator *iterator = new(nothrow) Iterator; + if (iterator) { + if (!iterator->SetTo(this, NULL, 0, true)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// InternalFind +AbstractIndexEntryIterator * +AttributeIndexImpl::InternalFind(const uint8 *key, size_t length) +{ + if (!key || (fKeyLength > 0 && length != fKeyLength)) + return NULL; + Iterator *iterator = new(nothrow) Iterator; + if (iterator) { + if (!iterator->SetTo(this, key, length)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// _AddIterator +void +AttributeIndexImpl::_AddIterator(Iterator *iterator) +{ + fIterators->Insert(iterator); +} + +// _RemoveIterator +void +AttributeIndexImpl::_RemoveIterator(Iterator *iterator) +{ + fIterators->Remove(iterator); +} + + +// Iterator + +// constructor +AttributeIndexImpl::Iterator::Iterator() + : BaseClass(), + fIndex(NULL) +{ +} + +// destructor +AttributeIndexImpl::Iterator::~Iterator() +{ + SetTo(NULL, NULL, 0); +} + +// GetCurrent +Entry * +AttributeIndexImpl::Iterator::GetCurrent() +{ + return BaseClass::GetCurrent(); +} + +// GetCurrent +Entry * +AttributeIndexImpl::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength) +{ + Entry *entry = GetCurrent(); + if (entry) { + if (Attribute **attribute = fIterator.fIterator.GetCurrent()) { + if ((*attribute)->GetNode() == entry->GetNode()) { + (*attribute)->GetKey(buffer, keyLength); + } else { + FATAL(("Node of current attribute and node of current entry " + "differ: %Ld vs. %Ld\n", + (*attribute)->GetNode()->GetID(), + entry->GetNode()->GetID())); + entry = NULL; + } + } else { + FATAL(("We have a current entry (`%s', node: %Ld), but no current " + "attribute.\n", entry->GetName(), + entry->GetNode()->GetID())); + entry = NULL; + } + } + return entry; +} + +// Suspend +status_t +AttributeIndexImpl::Iterator::Suspend() +{ + status_t error = BaseClass::Suspend(); + if (error == B_OK) { + if (fNode) { + error = fIndex->GetVolume()->AddNodeListener(this, fNode, + NODE_LISTEN_REMOVED); + if (error == B_OK && fEntry) { + error = fIndex->GetVolume()->AddEntryListener(this, fEntry, + ENTRY_LISTEN_REMOVED); + if (error != B_OK) + fIndex->GetVolume()->RemoveNodeListener(this, fNode); + } + if (error != B_OK) + BaseClass::Resume(); + } + } + return error; +} + +// Resume +status_t +AttributeIndexImpl::Iterator::Resume() +{ + status_t error = BaseClass::Resume(); + if (error == B_OK) { + if (fEntry) + error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry); + if (fNode) { + if (error == B_OK) + error = fIndex->GetVolume()->RemoveNodeListener(this, fNode); + else + fIndex->GetVolume()->RemoveNodeListener(this, fNode); + } + } + return error; +} + +// SetTo +bool +AttributeIndexImpl::Iterator::SetTo(AttributeIndexImpl *index, + const uint8 *key, size_t length, bool ignoreValue) +{ + Resume(); + Unset(); + // set the new values + fIndex = index; + if (fIndex) + fIndex->_AddIterator(this); + fInitialized = fIndex; + // get the attribute node's first entry + if (fIndex) { + // get the first node + bool found = true; + if (ignoreValue) + fIndex->fAttributes->GetIterator(&fIterator.fIterator); + else { + found = fIndex->fAttributes->FindFirst(PrimaryKey(key, length), + &(fIterator.fIterator)); + } + // get the first entry + if (found) { + if (Node **nodeP = fIterator.GetCurrent()) { + fNode = *nodeP; + fEntry = fNode->GetFirstReferrer(); + if (!fEntry) + BaseClass::GetNext(); + if (Attribute **attribute = fIterator.fIterator.GetCurrent()) { + const uint8 *attrKey; + size_t attrKeyLength; + (*attribute)->GetKey(&attrKey, &attrKeyLength); + if (!ignoreValue + && compare_keys(attrKey, attrKeyLength, key, length, + fIndex->GetType()) != 0) { + Unset(); + } + } + } + } + } + return fEntry; +} + +// Unset +void +AttributeIndexImpl::Iterator::Unset() +{ + if (fIndex) { + fIndex->_RemoveIterator(this); + fIndex = NULL; + } + BaseClass::Unset(); +} + +// EntryRemoved +void +AttributeIndexImpl::Iterator::EntryRemoved(Entry */*entry*/) +{ + Resume(); + fIsNext = BaseClass::GetNext(); + Suspend(); +} + +// NodeRemoved +void +AttributeIndexImpl::Iterator::NodeRemoved(Node */*node*/) +{ + Resume(); + fEntry = NULL; + fIsNext = BaseClass::GetNext(); + Suspend(); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/AttributeIndexImpl.h b/src/add-ons/kernel/file_systems/ramfs/AttributeIndexImpl.h new file mode 100644 index 0000000000..dcdfecbca0 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AttributeIndexImpl.h @@ -0,0 +1,49 @@ +// AttributeIndexImpl.h + +#ifndef ATTRIBUTE_INDEX_IMPL_H +#define ATTRIBUTE_INDEX_IMPL_H + +#include "AttributeIndex.h" + +// AttributeIndexImpl +class AttributeIndexImpl : public AttributeIndex { +public: + AttributeIndexImpl(Volume *volume, const char *name, uint32 type, + size_t keyLength); + virtual ~AttributeIndexImpl(); + + virtual int32 CountEntries() const; + + virtual status_t Changed(Attribute *attribute, + const uint8 *oldKey, size_t oldLength); + +private: + virtual status_t Added(Attribute *attribute); + virtual bool Removed(Attribute *attribute); + +protected: + virtual AbstractIndexEntryIterator *InternalGetIterator(); + virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key, + size_t length); + +private: + class Iterator; + class IteratorList; + class AttributeTree; + + class PrimaryKey; + class GetPrimaryKey; + class PrimaryKeyCompare; + + friend class Iterator; + +private: + void _AddIterator(Iterator *iterator); + void _RemoveIterator(Iterator *iterator); + +private: + AttributeTree *fAttributes; + IteratorList *fIterators; +}; + +#endif // ATTRIBUTE_INDEX_IMPL_H diff --git a/src/add-ons/kernel/file_systems/ramfs/AttributeIterator.cpp b/src/add-ons/kernel/file_systems/ramfs/AttributeIterator.cpp new file mode 100644 index 0000000000..add194cbf2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AttributeIterator.cpp @@ -0,0 +1,136 @@ +// AttributeIterator.cpp + +#include "AttributeIterator.h" +#include "Node.h" +#include "Volume.h" + +// constructor +AttributeIterator::AttributeIterator(Node *node) + : fNode(node), + fAttribute(NULL), + fSuspended(false), + fIsNext(false), + fDone(false) +{ +} + +// destructor +AttributeIterator::~AttributeIterator() +{ + Unset(); +} + +// SetTo +status_t +AttributeIterator::SetTo(Node *node) +{ + Unset(); + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + fNode = node; + fAttribute = NULL; + fSuspended = false; + fIsNext = false; + fDone = false; + } + return error; +} + +// Unset +void +AttributeIterator::Unset() +{ + if (fNode && fSuspended) + Resume(); + fNode = NULL; + fAttribute = NULL; + fSuspended = false; + fIsNext = false; + fDone = false; +} + +// Suspend +status_t +AttributeIterator::Suspend() +{ + status_t error = (fNode ? B_OK : B_ERROR); + if (error == B_OK) { + if (fNode->GetVolume()->IteratorLock()) { + if (!fSuspended) { + if (fAttribute) + fAttribute->AttachAttributeIterator(this); + fNode->GetVolume()->IteratorUnlock(); + fSuspended = true; + } else + error = B_ERROR; + } else + error = B_ERROR; + } + return error; +} + +// Resume +status_t +AttributeIterator::Resume() +{ + status_t error = (fNode ? B_OK : B_ERROR); + if (error == B_OK) { + if (fNode->GetVolume()->IteratorLock()) { + if (fSuspended) { + if (fAttribute) + fAttribute->DetachAttributeIterator(this); + fSuspended = false; + } + fNode->GetVolume()->IteratorUnlock(); + } else + error = B_ERROR; + } + return error; +} + +// GetNext +status_t +AttributeIterator::GetNext(Attribute **attribute) +{ + status_t error = B_ENTRY_NOT_FOUND; + if (!fDone && fNode && attribute) { + if (fIsNext) { + fIsNext = false; + if (fAttribute) + error = B_OK; + } else + error = fNode->GetNextAttribute(&fAttribute); + *attribute = fAttribute; + } + fDone = (error != B_OK); + return error; +} + +// Rewind +status_t +AttributeIterator::Rewind() +{ + status_t error = (fNode ? B_OK : B_ERROR); + if (error == B_OK) { + if (fNode->GetVolume()->IteratorLock()) { + if (fSuspended && fAttribute) + fAttribute->DetachAttributeIterator(this); + fAttribute = NULL; + fIsNext = false; + fDone = false; + fNode->GetVolume()->IteratorUnlock(); + } else + error = B_ERROR; + } + return error; +} + +// SetCurrent +void +AttributeIterator::SetCurrent(Attribute *attribute, bool isNext) +{ + fIsNext = isNext; + fAttribute = attribute; + fDone = !fAttribute; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/AttributeIterator.h b/src/add-ons/kernel/file_systems/ramfs/AttributeIterator.h new file mode 100644 index 0000000000..bb660afcbb --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/AttributeIterator.h @@ -0,0 +1,46 @@ +// AttributeIterator.h + +#ifndef ATTRIBUTE_ITERATOR_H +#define ATTRIBUTE_ITERATOR_H + +#include + +#include "DLList.h" + +class Attribute; +class Node; + +class AttributeIterator : public DLListLinkImpl { +public: + AttributeIterator(Node *node = NULL); + ~AttributeIterator(); + + status_t SetTo(Node *node); + void Unset(); + + Node *GetNode() const { return fNode; } + + status_t Suspend(); + status_t Resume(); + bool IsSuspended() const { return fSuspended; } + + status_t GetNext(Attribute **attribute); + Attribute *GetCurrent() const { return fAttribute; } + + status_t Rewind(); + +private: + void SetCurrent(Attribute *attribute, bool isNext); + +private: + friend class Node; + +private: + Node *fNode; + Attribute *fAttribute; + bool fSuspended; + bool fIsNext; + bool fDone; +}; + +#endif // ATTRIBUTE_ITERATOR_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Block.h b/src/add-ons/kernel/file_systems/ramfs/Block.h new file mode 100644 index 0000000000..4dbea0c219 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Block.h @@ -0,0 +1,317 @@ +// Block.h + +#ifndef BLOCK_H +#define BLOCK_H + +class Block; +class BlockHeader; +class BlockReference; +class TFreeBlock; + +#include + +// debugging +//#define inline +#define BA_DEFINE_INLINES 1 + +// BlockHeader +class BlockHeader { +public: + inline Block *ToBlock() { return (Block*)this; } + inline TFreeBlock *ToFreeBlock() { return (TFreeBlock*)this; } + + inline void SetPreviousBlock(Block *block); + inline Block *GetPreviousBlock(); + + inline void SetNextBlock(Block *block); + inline Block *GetNextBlock(); + inline bool HasNextBlock() { return (fSize & HAS_NEXT_FLAG); } + + inline void SetSize(size_t size, bool hasNext = false); + inline size_t GetSize() const; + static inline size_t GetUsableSizeFor(size_t size); + inline size_t GetUsableSize() const; + + inline void *GetData(); + + inline void SetFree(bool flag); + inline bool IsFree() const; + + inline void SetReference(BlockReference *ref); + inline BlockReference *GetReference() const { return fReference; } + inline void FixReference(); + + inline void SetTo(Block *previous, size_t size, bool isFree, bool hasNext, + BlockReference *reference = NULL); + +private: + enum { + FREE_FLAG = 0x80000000, + BACK_SKIP_MASK = 0x7fffffff, + }; + + enum { + HAS_NEXT_FLAG = 0x80000000, + SIZE_MASK = 0x7fffffff, + }; + +private: + BlockHeader(); + ~BlockHeader(); + +protected: + size_t fBackSkip; + size_t fSize; + BlockReference *fReference; +}; + +// Block +class Block : public BlockHeader { +public: + static inline Block *MakeBlock(void *address, ssize_t offset, + Block *previous, size_t size, bool isFree, + bool hasNext, + BlockReference *reference = NULL); + +private: + Block(); + ~Block(); +}; + +// TFreeBlock +class TFreeBlock : public Block { +public: + + inline void SetPreviousFreeBlock(TFreeBlock *block) { fPrevious = block; } + inline void SetNextFreeBlock(TFreeBlock *block) { fNext = block; } + inline TFreeBlock *GetPreviousFreeBlock() { return fPrevious; } + inline TFreeBlock *GetNextFreeBlock() { return fNext; } + + inline void SetTo(Block *previous, size_t size, bool hasNext, + TFreeBlock *previousFree, TFreeBlock *nextFree); + +// static inline TFreeBlock *MakeFreeBlock(void *address, ssize_t offset, +// Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree, +// TFreeBlock *nextFree); + +private: + TFreeBlock(); + ~TFreeBlock(); + +private: + TFreeBlock *fPrevious; + TFreeBlock *fNext; +}; + +// BlockReference +class BlockReference { +public: + inline BlockReference() : fBlock(NULL) {} + inline BlockReference(Block *block) : fBlock(block) {} + + inline void SetBlock(Block *block) { fBlock = block; } + inline Block *GetBlock() const { return fBlock; } + + inline void *GetData() const { return fBlock->GetData(); } + inline void *GetDataAt(ssize_t offset) const; + +private: + Block *fBlock; +}; + + +// --------------------------------------------------------------------------- +// inline methods + +// debugging +#if BA_DEFINE_INLINES + +// BlockHeader + +// SetPreviousBlock +inline +void +BlockHeader::SetPreviousBlock(Block *block) +{ + size_t offset = (block ? (char*)this - (char*)block : 0); + fBackSkip = fBackSkip & FREE_FLAG | offset; +} + +// GetPreviousBlock +inline +Block * +BlockHeader::GetPreviousBlock() +{ + if (fBackSkip & BACK_SKIP_MASK) + return (Block*)((char*)this - (fBackSkip & BACK_SKIP_MASK)); + return NULL; +} + +// SetNextBlock +inline +void +BlockHeader::SetNextBlock(Block *block) +{ + if (block) + fSize = ((char*)block - (char*)this) | HAS_NEXT_FLAG; + else + fSize &= SIZE_MASK; +} + +// GetNextBlock +inline +Block * +BlockHeader::GetNextBlock() +{ + if (fSize & HAS_NEXT_FLAG) + return (Block*)((char*)this + (SIZE_MASK & fSize)); + return NULL; +} + +// SetSize +inline +void +BlockHeader::SetSize(size_t size, bool hasNext) +{ + fSize = size; + if (hasNext) + fSize |= HAS_NEXT_FLAG; +} + +// GetSize +inline +size_t +BlockHeader::GetSize() const +{ + return (fSize & SIZE_MASK); +} + +// GetUsableSizeFor +inline +size_t +BlockHeader::GetUsableSizeFor(size_t size) +{ + return (size - sizeof(BlockHeader)); +} + +// GetUsableSize +inline +size_t +BlockHeader::GetUsableSize() const +{ + return GetUsableSizeFor(GetSize()); +} + +// GetData +inline +void * +BlockHeader::GetData() +{ + return (char*)this + sizeof(BlockHeader); +} + +// SetFree +inline +void +BlockHeader::SetFree(bool flag) +{ + if (flag) + fBackSkip |= FREE_FLAG; + else + fBackSkip &= ~FREE_FLAG; +} + +// IsFree +inline +bool +BlockHeader::IsFree() const +{ + return (fBackSkip & FREE_FLAG); +} + +// SetTo +inline +void +BlockHeader::SetTo(Block *previous, size_t size, bool isFree, bool hasNext, + BlockReference *reference) +{ + SetPreviousBlock(previous); + SetSize(size, hasNext); + SetFree(isFree); + SetReference(reference); +} + +// SetReference +inline +void +BlockHeader::SetReference(BlockReference *ref) +{ + fReference = ref; + FixReference(); +} + +// FixReference +inline +void +BlockHeader::FixReference() +{ + if (fReference) + fReference->SetBlock(ToBlock()); +} + + +// Block + +// MakeBlock +/*inline +Block * +Block::MakeBlock(void *address, ssize_t offset, Block *previous, size_t size, + bool isFree, bool hasNext, BlockReference *reference) +{ + Block *block = (Block*)((char*)address + offset); + block->SetTo(previous, size, isFree, hasNext, reference); + return block; +}*/ + + +// TFreeBlock + +// SetTo +inline +void +TFreeBlock::SetTo(Block *previous, size_t size, bool hasNext, + TFreeBlock *previousFree, TFreeBlock *nextFree) +{ + Block::SetTo(previous, size, true, hasNext, NULL); + SetPreviousFreeBlock(previousFree); + SetNextFreeBlock(nextFree); +} + +// MakeFreeBlock +/*inline +TFreeBlock * +TFreeBlock::MakeFreeBlock(void *address, ssize_t offset, Block *previous, + size_t size, bool hasNext, TFreeBlock *previousFree, + TFreeBlock *nextFree) +{ + TFreeBlock *block = (TFreeBlock*)((char*)address + offset); + block->SetTo(previous, size, hasNext, previousFree, nextFree); + if (hasNext) + block->GetNextBlock()->SetPreviousBlock(block); + return block; +}*/ + + +// BlockReference + +// GetDataAt +inline +void * +BlockReference::GetDataAt(ssize_t offset) const +{ + return (char*)fBlock->GetData() + offset; +} + +#endif // BA_DEFINE_INLINES + +#endif // BLOCK_H diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/ramfs/BlockAllocator.cpp new file mode 100644 index 0000000000..652c886395 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockAllocator.cpp @@ -0,0 +1,426 @@ +// BlockAllocator.cpp + +// debugging +#define BA_DEFINE_INLINES 1 + +#include "AllocationInfo.h" +#include "BlockAllocator.h" +#include "BlockAllocatorArea.h" +#include "BlockAllocatorAreaBucket.h" +#include "Debug.h" +#include "DLList.h" + +// BlockAllocator + +// constructor +BlockAllocator::BlockAllocator(size_t areaSize) + : fReferenceManager(), + fBuckets(NULL), + fBucketCount(0), + fAreaSize(areaSize), + fAreaCount(0), + fFreeBytes(0) +{ + // create and init buckets + fBucketCount = bucket_containing_size(areaSize) + 1; + fBuckets = new(nothrow) AreaBucket[fBucketCount]; + size_t minSize = 0; + for (int32 i = 0; i < fBucketCount; i++) { + size_t maxSize = (1 << i) * kMinNetBlockSize; + fBuckets[i].SetIndex(i); + fBuckets[i].SetSizeLimits(minSize, maxSize); + minSize = maxSize; + } +} + +// destructor +BlockAllocator::~BlockAllocator() +{ + if (fBuckets) + delete[] fBuckets; +} + +// InitCheck +status_t +BlockAllocator::InitCheck() const +{ + RETURN_ERROR(fBuckets ? B_OK : B_NO_MEMORY); +} + +// AllocateBlock +BlockReference * +BlockAllocator::AllocateBlock(size_t usableSize) +{ +#if ENABLE_BA_PANIC +if (fPanic) + return NULL; +#endif +//PRINT(("BlockAllocator::AllocateBlock(%lu)\n", usableSize)); + Block *block = NULL; + if (usableSize > 0 && usableSize <= Area::GetMaxFreeBytesFor(fAreaSize)) { + // get a block reference + BlockReference *reference = fReferenceManager.AllocateReference(); + if (reference) { + block = _AllocateBlock(usableSize); + // set reference / cleanup on failure + if (block) + block->SetReference(reference); + else + fReferenceManager.FreeReference(reference); + } + D(SanityCheck(false)); + } +//PRINT(("BlockAllocator::AllocateBlock() done: %p\n", block)); + return (block ? block->GetReference() : NULL); +} + +// FreeBlock +void +BlockAllocator::FreeBlock(BlockReference *blockReference) +{ +#if ENABLE_BA_PANIC +if (fPanic) + return; +#endif +D(if (!CheckBlock(blockReference)) return;); + Block *block = (blockReference ? blockReference->GetBlock() : NULL); +//PRINT(("BlockAllocator::FreeBlock(%p)\n", block)); + Area *area = NULL; + if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) { + _FreeBlock(area, block, true); + D(SanityCheck(false)); + if (_DefragmentingRecommended()) + _Defragment(); + } +//PRINT(("BlockAllocator::FreeBlock() done\n")); +} + +// ResizeBlock +BlockReference * +BlockAllocator::ResizeBlock(BlockReference *blockReference, size_t usableSize) +{ +#if ENABLE_BA_PANIC +if (fPanic) + return NULL; +#endif +D(if (!CheckBlock(blockReference)) return NULL;); +//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", blockReference, usableSize)); + Block *block = (blockReference ? blockReference->GetBlock() : NULL); + Block *resultBlock = NULL; + Area *area = NULL; + if (block && !block->IsFree() && (area = _AreaForBlock(block)) != NULL) { +//PRINT(("BlockAllocator::ResizeBlock(%p, %lu)\n", block, usableSize)); + if (usableSize) { + // try to let the area resize the block + size_t blockSize = block->GetSize(); + size_t areaFreeBytes = area->GetFreeBytes(); + bool needsDefragmenting = area->NeedsDefragmenting(); +//PRINT((" block reference: %p / %p\n", blockReference, block->GetReference())); + resultBlock = area->ResizeBlock(block, usableSize); + block = blockReference->GetBlock(); + if (resultBlock) { +//PRINT((" area succeeded in resizing the block\n")); +//PRINT((" block reference now: %p\n", resultBlock->GetReference())); + // the area was able to resize the block + _RethinkAreaBucket(area, area->GetBucket(), + needsDefragmenting); + fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes; + // Defragment only, if the area was able to resize the block, + // the new block is smaller than the old one and defragmenting + // is recommended. + if (blockSize > resultBlock->GetSize() + && _DefragmentingRecommended()) { + _Defragment(); + } + } else { +//PRINT((" area failed to resize the block\n")); + // the area failed: allocate a new block, copy the data, and + // free the old one + resultBlock = _AllocateBlock(usableSize); + block = blockReference->GetBlock(); + if (resultBlock) { + memcpy(resultBlock->GetData(), block->GetData(), + block->GetUsableSize()); + resultBlock->SetReference(block->GetReference()); + _FreeBlock(area, block, false); + } + } + } else + FreeBlock(blockReference); + D(SanityCheck(false)); +//PRINT(("BlockAllocator::ResizeBlock() done: %p\n", resultBlock)); + } + return (resultBlock ? resultBlock->GetReference() : NULL); +} + +// SanityCheck +bool +BlockAllocator::SanityCheck(bool deep) const +{ + // iterate through all areas of all buckets + int32 areaCount = 0; + size_t freeBytes = 0; + for (int32 i = 0; i < fBucketCount; i++) { + AreaBucket *bucket = fBuckets + i; + if (deep) { + if (!bucket->SanityCheck(deep)) + return false; + } + for (Area *area = bucket->GetFirstArea(); + area; + area = bucket->GetNextArea(area)) { + areaCount++; + freeBytes += area->GetFreeBytes(); + } + } + // area count + if (areaCount != fAreaCount) { + FATAL(("fAreaCount is %ld, but should be %ld\n", fAreaCount, + areaCount)); + BA_PANIC("BlockAllocator: Bad free bytes."); + return false; + } + // free bytes + if (fFreeBytes != freeBytes) { + FATAL(("fFreeBytes is %lu, but should be %lu\n", fFreeBytes, + freeBytes)); + BA_PANIC("BlockAllocator: Bad free bytes."); + return false; + } + return true; +} + +// CheckArea +bool +BlockAllocator::CheckArea(Area *checkArea) +{ + for (int32 i = 0; i < fBucketCount; i++) { + AreaBucket *bucket = fBuckets + i; + for (Area *area = bucket->GetFirstArea(); + area; + area = bucket->GetNextArea(area)) { + if (area == checkArea) + return true; + } + } + FATAL(("Area %p is not a valid Area!\n", checkArea)); + BA_PANIC("Invalid Area."); + return false; +} + +// CheckBlock +bool +BlockAllocator::CheckBlock(Block *block, size_t minSize) +{ + Area *area = _AreaForBlock(block); + return (area/* && CheckArea(area)*/ && area->CheckBlock(block, minSize)); +} + +// CheckBlock +bool +BlockAllocator::CheckBlock(BlockReference *reference, size_t minSize) +{ + return (fReferenceManager.CheckReference(reference) + && CheckBlock(reference->GetBlock(), minSize)); +} + +// GetAllocationInfo +void +BlockAllocator::GetAllocationInfo(AllocationInfo &info) +{ + fReferenceManager.GetAllocationInfo(info); + info.AddOtherAllocation(sizeof(AreaBucket), fBucketCount); + info.AddAreaAllocation(fAreaSize, fAreaCount); +} + +// _AreaForBlock +inline +BlockAllocator::Area * +BlockAllocator::_AreaForBlock(Block *block) +{ + Area *area = NULL; + area_id id = area_for(block); + area_info info; + if (id >= 0 && get_area_info(id, &info) == B_OK) + area = (Area*)info.address; +D(if (!CheckArea(area)) return NULL;); + return area; +} + +// _AllocateBlock +Block * +BlockAllocator::_AllocateBlock(size_t usableSize, bool dontCreateArea) +{ + Block *block = NULL; + // Get the last area (the one with the most free space) and try + // to let it allocate a block. If that fails, allocate a new area. + // find a bucket for the allocation +// TODO: optimize + AreaBucket *bucket = NULL; + int32 index = bucket_containing_min_size(usableSize); + for (; index < fBucketCount; index++) { + if (!fBuckets[index].IsEmpty()) { + bucket = fBuckets + index; + break; + } + } + // get an area: if we have one, from the bucket, else create a new + // area + Area *area = NULL; + if (bucket) + area = bucket->GetFirstArea(); + else if (!dontCreateArea) { + area = Area::Create(fAreaSize); + if (area) { + fAreaCount++; + fFreeBytes += area->GetFreeBytes(); + bucket = fBuckets + area->GetBucketIndex(); + bucket->AddArea(area); +PRINT(("New area allocated. area count now: %ld, free bytes: %lu\n", +fAreaCount, fFreeBytes)); + } + } + // allocate a block + if (area) { + size_t areaFreeBytes = area->GetFreeBytes(); + bool needsDefragmenting = area->NeedsDefragmenting(); + block = area->AllocateBlock(usableSize); + // move the area into another bucket, if necessary + if (block) { + _RethinkAreaBucket(area, bucket, needsDefragmenting); + fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes; + } +#if ENABLE_BA_PANIC +else if (!fPanic) { +FATAL(("Block allocation failed unexpectedly.\n")); +PRINT((" usableSize: %lu, areaFreeBytes: %lu\n", usableSize, areaFreeBytes)); +BA_PANIC("Block allocation failed unexpectedly."); +//block = area->AllocateBlock(usableSize); +} +#endif + } + return block; +} + +// _FreeBlock +void +BlockAllocator::_FreeBlock(Area *area, Block *block, bool freeReference) +{ + size_t areaFreeBytes = area->GetFreeBytes(); + AreaBucket *bucket = area->GetBucket(); + bool needsDefragmenting = area->NeedsDefragmenting(); + // free the block and the block reference + BlockReference *reference = block->GetReference(); + area->FreeBlock(block); + if (reference && freeReference) + fReferenceManager.FreeReference(reference); + // move the area into another bucket, if necessary + _RethinkAreaBucket(area, bucket, needsDefragmenting); + fFreeBytes = fFreeBytes + area->GetFreeBytes() - areaFreeBytes; +} + +// _RethinkAreaBucket +inline +void +BlockAllocator::_RethinkAreaBucket(Area *area, AreaBucket *bucket, + bool needsDefragmenting) +{ + AreaBucket *newBucket = fBuckets + area->GetBucketIndex(); + if (newBucket != bucket + || needsDefragmenting != area->NeedsDefragmenting()) { + bucket->RemoveArea(area); + newBucket->AddArea(area); + } +} + +// _DefragmentingRecommended +inline +bool +BlockAllocator::_DefragmentingRecommended() +{ + // Don't know, whether this makes much sense: We don't try to defragment, + // when not at least a complete area could be deleted, and some tolerance + // being left (a fixed value plus 1/32 of the used bytes). + size_t usedBytes = fAreaCount * Area::GetMaxFreeBytesFor(fAreaSize) + - fFreeBytes; + return (fFreeBytes > fAreaSize + kDefragmentingTolerance + usedBytes / 32); +} + +// _Defragment +bool +BlockAllocator::_Defragment() +{ + bool success = false; + // We try to empty the least populated area by moving its blocks to other + // areas. + if (fFreeBytes > fAreaSize) { + // find the least populated area + // find the bucket with the least populated areas + AreaBucket *bucket = NULL; + for (int32 i = fBucketCount - 1; i >= 0; i--) { + if (!fBuckets[i].IsEmpty()) { + bucket = fBuckets + i; + break; + } + } + // find the area in the bucket + Area *area = NULL; + if (bucket) { + area = bucket->GetFirstArea(); + Area *bucketArea = area; + while ((bucketArea = bucket->GetNextArea(bucketArea)) != NULL) { + if (bucketArea->GetFreeBytes() > area->GetFreeBytes()) + area = bucketArea; + } + } + if (area) { + // remove the area from the bucket + bucket->RemoveArea(area); + fFreeBytes -= area->GetFreeBytes(); + // iterate through the blocks in the area and try to find a new + // home for them + success = true; + while (Block *block = area->GetFirstUsedBlock()) { + Block *newBlock = _AllocateBlock(block->GetUsableSize(), true); + if (newBlock) { + // got a new block: copy the data to it and free the old + // one + memcpy(newBlock->GetData(), block->GetData(), + block->GetUsableSize()); + newBlock->SetReference(block->GetReference()); + block->SetReference(NULL); + area->FreeBlock(block, true); +#if ENABLE_BA_PANIC + if (fPanic) { + PRINT(("Panicked while trying to free block %p\n", + block)); + success = false; + break; + } +#endif + } else { + success = false; + break; + } + } + // delete the area + if (success && area->IsEmpty()) { + area->Delete(); + fAreaCount--; +PRINT(("defragmenting: area deleted\n")); + } else { +PRINT(("defragmenting: failed to empty area\n")); + // failed: re-add the area + fFreeBytes += area->GetFreeBytes(); + AreaBucket *newBucket = fBuckets + area->GetBucketIndex(); + newBucket->AddArea(area); + } + } + D(SanityCheck(false)); + } + return success; +} + +#if ENABLE_BA_PANIC +bool BlockAllocator::fPanic = false; +#endif diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockAllocator.h b/src/add-ons/kernel/file_systems/ramfs/BlockAllocator.h new file mode 100644 index 0000000000..6cb793f79f --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockAllocator.h @@ -0,0 +1,70 @@ +// BlockAllocator.h + +#ifndef BLOCK_ALLOCATOR_H +#define BLOCK_ALLOCATOR_H + +#include + +#include "Block.h" +#include "BlockReferenceManager.h" +#include "Debug.h" +#include "List.h" + +#define ENABLE_BA_PANIC 1 +#if ENABLE_BA_PANIC +#define BA_PANIC(x) { PANIC(x); BlockAllocator::fPanic = true; } +#endif + +class AllocationInfo; + +// BlockAllocator +class BlockAllocator { +public: + BlockAllocator(size_t areaSize); + ~BlockAllocator(); + + status_t InitCheck() const; + + BlockReference *AllocateBlock(size_t usableSize); + void FreeBlock(BlockReference *block); + BlockReference *ResizeBlock(BlockReference *block, size_t usableSize); + + size_t GetAvailableBytes() const { return fAreaCount * fAreaSize; } + size_t GetFreeBytes() const { return fFreeBytes; } + size_t GetUsedBytes() const { return fAreaCount * fAreaSize + - fFreeBytes; } + +public: + class Area; + class AreaBucket; + + // debugging only + bool SanityCheck(bool deep = false) const; + bool CheckArea(Area *area); + bool CheckBlock(Block *block, size_t minSize = 0); + bool CheckBlock(BlockReference *reference, size_t minSize = 0); + void GetAllocationInfo(AllocationInfo &info); + +private: + inline Area *_AreaForBlock(Block *block); + Block *_AllocateBlock(size_t usableSize, bool dontCreateArea = false); + void _FreeBlock(Area *area, Block *block, bool freeReference); + inline void _RethinkAreaBucket(Area *area, AreaBucket *bucket, + bool needsDefragmenting); + inline bool _DefragmentingRecommended(); + bool _Defragment(); + +private: + BlockReferenceManager fReferenceManager; + AreaBucket *fBuckets; + int32 fBucketCount; + size_t fAreaSize; + int32 fAreaCount; + size_t fFreeBytes; +#if ENABLE_BA_PANIC +public: + static bool fPanic; +#endif +}; + +#endif // BLOCK_ALLOCATOR_H diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorArea.cpp b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorArea.cpp new file mode 100644 index 0000000000..98daa2e0e5 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorArea.cpp @@ -0,0 +1,596 @@ +// BlockAllocatorArea.cpp + +#include "BlockAllocatorArea.h" +#include "Debug.h" + +// constructor +BlockAllocator::Area::Area(area_id id, size_t size) + : fBucket(NULL), + fID(id), + fSize(size), + fFreeBytes(0), + fFreeBlockCount(1), + fUsedBlockCount(0), + fFirstBlock(NULL), + fLastBlock(NULL), + fFirstFree(NULL), + fLastFree(NULL) +{ + size_t headerSize = block_align_ceil(sizeof(Area)); + fFirstFree = (TFreeBlock*)((char*)this + headerSize); + fFirstFree->SetTo(NULL, block_align_floor(fSize - headerSize), false, NULL, + NULL); + fFirstBlock = fLastBlock = fLastFree = fFirstFree; + fFreeBytes = fFirstFree->GetUsableSize(); +} + +// Create +BlockAllocator::Area * +BlockAllocator::Area::Create(size_t size) +{ + Area *area = NULL; + void *base = NULL; +#if USER + area_id id = create_area("block alloc", &base, B_ANY_ADDRESS, + size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); +#else + area_id id = create_area("block alloc", &base, B_ANY_KERNEL_ADDRESS, + size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA); +#endif + if (id >= 0) { + area = new(base) Area(id, size); + } else { + ERROR(("BlockAllocator::Area::Create(%lu): Failed to create area: %s\n", + size, strerror(id))); + } + return area; +} + +// Delete +void +BlockAllocator::Area::Delete() +{ + delete_area(fID); +} + +// AllocateBlock +Block * +BlockAllocator::Area::AllocateBlock(size_t usableSize, bool dontDefragment) +{ +if (kMinBlockSize != block_align_ceil(sizeof(TFreeBlock))) { +FATAL(("kMinBlockSize is not correctly initialized! Is %lu, but should be: " +"%lu\n", kMinBlockSize, block_align_ceil(sizeof(TFreeBlock)))); +BA_PANIC("kMinBlockSize not correctly initialized."); +return NULL; +} + if (usableSize == 0) + return NULL; + Block *newBlock = NULL; + size_t size = max(usableSize + sizeof(BlockHeader), kMinBlockSize); + size = block_align_ceil(size); + if (size <= _GetBlockFreeBytes()) { + // find first fit + TFreeBlock *block = _FindFreeBlock(size); + if (!block && !dontDefragment) { + // defragmenting is necessary + _Defragment(); + block = _FindFreeBlock(size); + if (!block) { + // no free block + // Our data structures seem to be corrupted, since + // _GetBlockFreeBytes() promised that we would have enough + // free space. + FATAL(("Couldn't find free block of min size %lu after " + "defragmenting, although we should have %lu usable free " + "bytes!\n", size, _GetBlockFreeBytes())); + BA_PANIC("Bad area free bytes."); + } + } + if (block) { + // found a free block + size_t remainder = block->GetSize() - size; + if (remainder >= kMinBlockSize) { + // enough space left for a free block + Block *freePrev = block->GetPreviousBlock(); +// TFreeBlock *prevFree = block->GetPreviousFreeBlock(); +// TFreeBlock *nextFree = block->GetNextFreeBlock(); +// newBlock = block; + _MoveResizeFreeBlock(block, size, remainder); + // setup the new block +// newBlock->SetSize(size, true); +// newBlock->SetFree(false); + newBlock = _MakeUsedBlock(block, 0, freePrev, size, true); + } else { + // not enough space left: take the free block over completely + // remove the block from the free list + _RemoveFreeBlock(block); + newBlock = block; + newBlock->SetFree(false); + } + if (fFreeBlockCount) + fFreeBytes -= newBlock->GetSize(); + else + fFreeBytes = 0; + fUsedBlockCount++; + } + } + D(SanityCheck()); + return newBlock; +} + +// FreeBlock +void +BlockAllocator::Area::FreeBlock(Block *block, bool dontDefragment) +{ + if (block) { + // mark the block free and insert it into the free list + block->SetFree(true); + TFreeBlock *freeBlock = (TFreeBlock*)block; + _InsertFreeBlock(freeBlock); + fUsedBlockCount--; + if (fFreeBlockCount == 1) + fFreeBytes += freeBlock->GetUsableSize(); + else + fFreeBytes += freeBlock->GetSize(); + // try coalescing with the next and the previous free block +D(SanityCheck()); + _CoalesceWithNext(freeBlock); +D(SanityCheck()); + _CoalesceWithNext(freeBlock->GetPreviousFreeBlock()); + // defragment, if sensible + if (!dontDefragment && _DefragmentingRecommended()) + _Defragment(); + D(SanityCheck()); + } +} + +// ResizeBlock +Block * +BlockAllocator::Area::ResizeBlock(Block *block, size_t newUsableSize, + bool dontDefragment) +{ +//PRINT(("Area::ResizeBlock(%p, %lu)\n", block, newUsableSize)); +// newUsableSize must be >0 ! + if (newUsableSize == 0) + return NULL; + Block *resultBlock = NULL; + if (block) { + size_t size = block->GetSize(); + size_t newSize = max(newUsableSize + sizeof(BlockHeader), + kMinBlockSize); + newSize = block_align_ceil(newSize); + if (newSize == size) { + // size doesn't change: nothing to do + resultBlock = block; + } else if (newSize < size) { + // shrink the block + size_t sizeDiff = size - newSize; + Block *nextBlock = block->GetNextBlock(); + if (nextBlock && nextBlock->IsFree()) { + // join the space with the adjoining free block + TFreeBlock *freeBlock = nextBlock->ToFreeBlock(); + _MoveResizeFreeBlock(freeBlock, -sizeDiff, + freeBlock->GetSize() + sizeDiff); + // resize the block and we're done + block->SetSize(newSize, true); + fFreeBytes += sizeDiff; + } else if (sizeDiff >= sizeof(TFreeBlock)) { + // the freed space is large enough for a free block + TFreeBlock *newFree = _MakeFreeBlock(block, newSize, block, + sizeDiff, nextBlock, NULL, NULL); + _InsertFreeBlock(newFree); + block->SetSize(newSize, true); + if (fFreeBlockCount == 1) + fFreeBytes += newFree->GetUsableSize(); + else + fFreeBytes += newFree->GetSize(); + if (!dontDefragment && _DefragmentingRecommended()) + _Defragment(); + } // else: insufficient space for a free block: no changes + resultBlock = block; + } else { +//PRINT((" grow...\n")); + // grow the block + size_t sizeDiff = newSize - size; + Block *nextBlock = block->GetNextBlock(); + if (nextBlock && nextBlock->IsFree() + && nextBlock->GetSize() >= sizeDiff) { +//PRINT((" adjoining free block\n")); + // there is a adjoining free block and it is large enough + TFreeBlock *freeBlock = nextBlock->ToFreeBlock(); + size_t freeSize = freeBlock->GetSize(); + if (freeSize - sizeDiff >= sizeof(TFreeBlock)) { + // the remaining space is still large enough for a free + // block + _MoveResizeFreeBlock(freeBlock, sizeDiff, + freeSize - sizeDiff); + block->SetSize(newSize, true); + fFreeBytes -= sizeDiff; + } else { + // the remaining free space wouldn't be large enough for + // a free block: consume the free block completely + Block *freeNext = freeBlock->GetNextBlock(); + _RemoveFreeBlock(freeBlock); + block->SetSize(size + freeSize, freeNext); + _FixBlockList(block, block->GetPreviousBlock(), freeNext); + if (fFreeBlockCount == 0) + fFreeBytes = 0; + else + fFreeBytes -= freeSize; + } + resultBlock = block; + } else { +//PRINT((" no adjoining free block\n")); + // no (large enough) adjoining free block: allocate + // a new block and copy the data to it + BlockReference *reference = block->GetReference(); + resultBlock = AllocateBlock(newUsableSize, dontDefragment); + block = reference->GetBlock(); + if (resultBlock) { + resultBlock->SetReference(reference); + memcpy(resultBlock->GetData(), block->GetData(), + block->GetUsableSize()); + FreeBlock(block, dontDefragment); + resultBlock = reference->GetBlock(); + } + } + } + } + D(SanityCheck()); +//PRINT(("Area::ResizeBlock() done: %p\n", resultBlock)); + return resultBlock; +} + +// SanityCheck +bool +BlockAllocator::Area::SanityCheck() const +{ + // area ID + if (fID < 0) { + FATAL(("Area ID < 0: %lx\n", fID)); + BA_PANIC("Bad area ID."); + return false; + } + // size + size_t areaHeaderSize = block_align_ceil(sizeof(Area)); + if (fSize < areaHeaderSize + sizeof(TFreeBlock)) { + FATAL(("Area too small to contain area header and at least one free " + "block: %lu bytes\n", fSize)); + BA_PANIC("Bad area size."); + return false; + } + // free bytes + if (fFreeBytes > fSize) { + FATAL(("Free size greater than area size: %lu vs %lu\n", fFreeBytes, + fSize)); + BA_PANIC("Bad area free bytes."); + return false; + } + // block count + if (fFreeBlockCount + fUsedBlockCount == 0) { + FATAL(("Area contains no blocks at all.\n")); + BA_PANIC("Bad area block count."); + return false; + } + // block list + uint32 usedBlockCount = 0; + uint32 freeBlockCount = 0; + size_t freeBytes = 0; + if (!fFirstBlock || !fLastBlock) { + FATAL(("Invalid block list: first or last block NULL: first: %p, " + "last: %p\n", fFirstBlock, fLastBlock)); + BA_PANIC("Bad area block list."); + return false; + } else { + // iterate through block list and also check free list + int32 blockCount = fFreeBlockCount + fUsedBlockCount; + Block *block = fFirstBlock; + Block *prevBlock = NULL; + Block *prevFree = NULL; + Block *nextFree = fFirstFree; + bool blockListOK = true; + for (int32 i = 0; i < blockCount; i++) { + blockListOK = false; + if (!block) { + FATAL(("Encountered NULL in block list at index %ld, although " + "list should have %ld blocks\n", i, blockCount)); + BA_PANIC("Bad area block list."); + return false; + } + uint64 address = (uint32)block; + // block within area? + if (address < (uint32)this + areaHeaderSize + || address + sizeof(TFreeBlock) > (uint32)this + fSize) { + FATAL(("Utterly mislocated block: %p, area: %p, " + "size: %lu\n", block, this, fSize)); + BA_PANIC("Bad area block."); + return false; + } + // block too large for area? + size_t blockSize = block->GetSize(); + if (blockSize < sizeof(TFreeBlock) + || address + blockSize > (uint32)this + fSize) { + FATAL(("Mislocated block: %p, size: %lu, area: %p, " + "size: %lu\n", block, blockSize, this, fSize)); + BA_PANIC("Bad area block."); + return false; + } + // alignment + if (block_align_floor(address) != address + || block_align_floor(blockSize) != blockSize) { + FATAL(("Block %ld not properly aligned: %p, size: %lu\n", + i, block, blockSize)); + BA_PANIC("Bad area block."); + return false; + } + // previous block + if (block->GetPreviousBlock() != prevBlock) { + FATAL(("Previous block of block %ld was not the previous " + "block in list: %p vs %p\n", i, + block->GetPreviousBlock(), prevBlock)); + BA_PANIC("Bad area block list."); + return false; + } + // additional checks for free block list + if (block->IsFree()) { + freeBlockCount++; + TFreeBlock *freeBlock = block->ToFreeBlock(); + if (prevFree) + freeBytes += freeBlock->GetSize(); + else + freeBytes += freeBlock->GetUsableSize(); + // block == next free block of previous free block + if (freeBlock != nextFree) { + FATAL(("Free block %ld is not the next block in free " + "list: %p vs %p\n", i, freeBlock, nextFree)); + BA_PANIC("Bad area free list."); + return false; + } + // previous free block + if (freeBlock->GetPreviousFreeBlock() != prevFree) { + FATAL(("Previous free block of block %ld was not the " + " previous block in free list: %p vs %p\n", i, + freeBlock->GetPreviousFreeBlock(), prevFree)); + BA_PANIC("Bad area free list."); + return false; + } + prevFree = freeBlock; + nextFree = freeBlock->GetNextFreeBlock(); + } else + usedBlockCount++; + prevBlock = block; + block = block->GetNextBlock(); + blockListOK = true; + } + // final checks on block list + if (blockListOK) { + if (block) { + FATAL(("More blocks in block list than expected\n")); + BA_PANIC("Bad area block count."); + return false; + } else if (fLastBlock != prevBlock) { + FATAL(("last block in block list was %p, but should be " + "%p\n", fLastBlock, prevBlock)); + BA_PANIC("Bad area last block."); + return false; + } else if (prevFree != fLastFree) { + FATAL(("last block in free list was %p, but should be %p\n", + fLastFree, prevFree)); + BA_PANIC("Bad area last free block."); + return false; + } + // block counts (a bit reduntant) + if (freeBlockCount != fFreeBlockCount) { + FATAL(("Free block count is %ld, but should be %ld\n", + fFreeBlockCount, freeBlockCount)); + BA_PANIC("Bad area free block count."); + return false; + } + if (usedBlockCount != fUsedBlockCount) { + FATAL(("Used block count is %ld, but should be %ld\n", + fUsedBlockCount, usedBlockCount)); + BA_PANIC("Bad area used block count."); + return false; + } + // free bytes + if (fFreeBytes != freeBytes) { + FATAL(("Free bytes is %lu, but should be %lu\n", + fFreeBytes, freeBytes)); + BA_PANIC("Bad area free bytes."); + return false; + } + } + } + return true; +} + +// CheckBlock +bool +BlockAllocator::Area::CheckBlock(Block *checkBlock, size_t minSize) +{ + for (Block *block = fFirstBlock; block; block = block->GetNextBlock()) { + if (block == checkBlock) + return (block->GetUsableSize() >= minSize); + } + FATAL(("Block %p is not in area %p!\n", checkBlock, this)); + BA_PANIC("Invalid Block."); + return false; +} + +// _FindFreeBlock +TFreeBlock * +BlockAllocator::Area::_FindFreeBlock(size_t minSize) +{ + // first fit + for (TFreeBlock *block = GetFirstFreeBlock(); + block; + block = block->GetNextFreeBlock()) { + if (block->GetSize() >= minSize) + return block; + } + return NULL; +} + +// _InsertFreeBlock +void +BlockAllocator::Area::_InsertFreeBlock(TFreeBlock *block) +{ + if (block) { + // find the free block before which this one has to be inserted + TFreeBlock *nextFree = NULL; + for (nextFree = GetFirstFreeBlock(); + nextFree; + nextFree = nextFree->GetNextFreeBlock()) { + if ((uint32)nextFree > (uint32)block) + break; + } + // get the previous block and insert the block between the two + TFreeBlock *prevFree + = (nextFree ? nextFree->GetPreviousFreeBlock() : fLastFree); + _FixFreeList(block, prevFree, nextFree); + fFreeBlockCount++; + } +} + +// _RemoveFreeBlock +void +BlockAllocator::Area::_RemoveFreeBlock(TFreeBlock *block) +{ + if (block) { + TFreeBlock *prevFree = block->GetPreviousFreeBlock(); + TFreeBlock *nextFree = block->GetNextFreeBlock(); + if (prevFree) + prevFree->SetNextFreeBlock(nextFree); + else + fFirstFree = nextFree; + if (nextFree) + nextFree->SetPreviousFreeBlock(prevFree); + else + fLastFree = prevFree; + } + fFreeBlockCount--; +} + +// _MoveResizeFreeBlock +TFreeBlock * +BlockAllocator::Area::_MoveResizeFreeBlock(TFreeBlock *freeBlock, + ssize_t offset, size_t newSize) +{ + TFreeBlock *movedFree = NULL; + if (freeBlock && offset) { + // move the header of the free block + Block *freePrev = freeBlock->GetPreviousBlock(); + TFreeBlock *prevFree = freeBlock->GetPreviousFreeBlock(); + TFreeBlock *nextFree = freeBlock->GetNextFreeBlock(); + movedFree = _MakeFreeBlock(freeBlock, offset, freePrev, newSize, + freeBlock->HasNextBlock(), prevFree, nextFree); + // update the free list + _FixFreeList(movedFree, prevFree, nextFree); + } + return movedFree; +} + +// _MakeFreeBlock +inline +TFreeBlock * +BlockAllocator::Area::_MakeFreeBlock(void *address, ssize_t offset, + Block *previous, size_t size, + bool hasNext, TFreeBlock *previousFree, + TFreeBlock *nextFree) +{ + TFreeBlock *block = (TFreeBlock*)((char*)address + offset); + block->SetTo(previous, size, hasNext, previousFree, nextFree); + if (hasNext) + block->GetNextBlock()->SetPreviousBlock(block); + else + fLastBlock = block; + return block; +} + +// _CoalesceWithNext +bool +BlockAllocator::Area::_CoalesceWithNext(TFreeBlock *block) +{ + bool result = false; + TFreeBlock *nextFree = NULL; + if (block && (nextFree = block->GetNextFreeBlock()) != NULL + && block->GetNextBlock() == nextFree) { + _RemoveFreeBlock(nextFree); + Block *nextBlock = nextFree->GetNextBlock(); + block->SetSize(block->GetSize() + nextFree->GetSize(), nextBlock); + if (nextBlock) + nextBlock->SetPreviousBlock(block); + else + fLastBlock = block; + result = true; + } + return result; +} + +// _MakeUsedBlock +inline +Block * +BlockAllocator::Area::_MakeUsedBlock(void *address, ssize_t offset, + Block *previous, size_t size, + bool hasNext) +{ + Block *block = (Block*)((char*)address + offset); + block->SetTo(previous, size, false, hasNext, NULL); + if (hasNext) + block->GetNextBlock()->SetPreviousBlock(block); + else + fLastBlock = block; + return block; +} + +// _Defragment +void +BlockAllocator::Area::_Defragment() +{ +D(SanityCheck()); +//PRINT(("BlockAllocator::Area::_Defragment()\n")); + // A trivial strategy for now: Keep the last free block and move the + // others so that they can be joined with it. This is done iteratively + // by moving the first free block to adjoin to the second one and + // coalescing them. A free block is moved by moving the data blocks in + // between. + TFreeBlock *nextFree = NULL; + while (fFirstFree && (nextFree = fFirstFree->GetNextFreeBlock()) != NULL) { + Block *prevBlock = fFirstFree->GetPreviousBlock(); + Block *nextBlock = fFirstFree->GetNextBlock(); + size_t size = fFirstFree->GetSize(); + // Used blocks are relatively position independed. We can move them + // en bloc and only need to adjust the previous pointer of the first + // one. + if (!nextBlock->IsFree()) { + // move the used blocks + size_t chunkSize = (char*)nextFree - (char*)nextBlock; + Block *nextFreePrev = nextFree->GetPreviousBlock(); + Block *movedBlock = fFirstFree; + memmove(movedBlock, nextBlock, chunkSize); + movedBlock->SetPreviousBlock(prevBlock); + // init the first free block + Block *movedNextFreePrev = (Block*)((char*)nextFreePrev - size); + fFirstFree = _MakeFreeBlock(movedBlock, chunkSize, + movedNextFreePrev, size, true, NULL, nextFree); + nextFree->SetPreviousFreeBlock(fFirstFree); + // fix the references of the moved blocks + for (Block *block = movedBlock; + block != fFirstFree; + block = block->GetNextBlock()) { + block->FixReference(); + } + } else { + // uncoalesced adjoining free block: That should never happen, + // since we always coalesce as early as possible. + INFORM(("Warning: Found uncoalesced adjoining free blocks!\n")); + } + // coalesce the first two blocks +D(SanityCheck()); + _CoalesceWithNext(fFirstFree); +D(SanityCheck()); + } +//D(SanityCheck()); +//PRINT(("BlockAllocator::Area::_Defragment() done\n")); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorArea.h b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorArea.h new file mode 100644 index 0000000000..b7e1b55530 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorArea.h @@ -0,0 +1,175 @@ +// BlockAllocatorArea.h + +#ifndef BLOCK_ALLOCATOR_AREA_H +#define BLOCK_ALLOCATOR_AREA_H + +#include "BlockAllocator.h" +#include "BlockAllocatorMisc.h" +#include "DLList.h" + +class BlockAllocator::Area : public DLListLinkImpl { +public: + static Area *Create(size_t size); + void Delete(); + + inline void SetBucket(AreaBucket *bucket) { fBucket = bucket; } + inline AreaBucket *GetBucket() const { return fBucket; } + + inline Block *GetFirstBlock() const { return fFirstBlock; } + inline Block *GetLastBlock() const { return fLastBlock; } + + inline TFreeBlock *GetFirstFreeBlock() const { return fFirstFree; } + inline TFreeBlock *GetLastFreeBlock() const { return fLastFree; } + + inline bool IsEmpty() const { return (fUsedBlockCount == 0); } + inline Block *GetFirstUsedBlock() const; + + static inline size_t GetMaxFreeBytesFor(size_t areaSize); + + inline size_t GetFreeBytes() const { return fFreeBytes; } + inline bool NeedsDefragmenting() const { return (fFreeBlockCount > 1); } + + inline int32 GetBucketIndex(); + + Block *AllocateBlock(size_t usableSize, bool dontDefragment = false); + void FreeBlock(Block *block, bool dontDefragment = false); + Block *ResizeBlock(Block *block, size_t newSize, + bool dontDefragment = false); + + // debugging only + bool SanityCheck() const; + bool CheckBlock(Block *block, size_t minSize = 0); + +private: + inline size_t _GetBlockFreeBytes() + { return fFreeBytes + sizeof(BlockHeader); } + + Area(area_id id, size_t size); + ~Area(); + + inline void _FixBlockList(Block *block, Block *prevBlock, + Block *nextBlock); + inline void _FixFreeList(TFreeBlock *block, TFreeBlock *prevFree, + TFreeBlock *nextFree); + + TFreeBlock *_FindFreeBlock(size_t minSize); + void _InsertFreeBlock(TFreeBlock *block); + void _RemoveFreeBlock(TFreeBlock *block); + TFreeBlock * _MoveResizeFreeBlock(TFreeBlock *freeBlock, ssize_t offset, + size_t newSize); + inline TFreeBlock *_MakeFreeBlock(void *address, ssize_t offset, + Block *previous, size_t size, bool hasNext, TFreeBlock *previousFree, + TFreeBlock *nextFree); + bool _CoalesceWithNext(TFreeBlock *block); + + inline Block *_MakeUsedBlock(void *address, ssize_t offset, + Block *previous, size_t size, bool hasNext); + + inline bool _DefragmentingRecommended(); + void _Defragment(); + +private: + AreaBucket *fBucket; + area_id fID; + size_t fSize; + size_t fFreeBytes; + size_t fFreeBlockCount; + size_t fUsedBlockCount; + Block *fFirstBlock; + Block *fLastBlock; + TFreeBlock *fFirstFree; + TFreeBlock *fLastFree; +}; + +typedef BlockAllocator::Area Area; + + +// inline methods + +// debugging +#if BA_DEFINE_INLINES + +// GetFirstUsedBlock +inline +Block * +BlockAllocator::Area::GetFirstUsedBlock() const +{ + // Two assumptions: + // 1) There is always a first block. If that isn't so, our structure are + // corrupt. + // 2) If the first block is free, the second (if any) is not. Otherwise + // there were adjoining free blocks, which our coalescing strategy + // prevents. + return (fFirstBlock->IsFree() ? fFirstBlock->GetNextBlock() : fFirstBlock); +} + +// GetMaxFreeBytesFor +inline +size_t +BlockAllocator::Area::GetMaxFreeBytesFor(size_t areaSize) +{ + size_t headerSize = block_align_ceil(sizeof(Area)); + return Block::GetUsableSizeFor(block_align_floor(areaSize - headerSize)); +} + +// GetBucketIndex +inline +int32 +BlockAllocator::Area::GetBucketIndex() +{ + return bucket_containing_size(GetFreeBytes()); +} + +// _FixBlockList +inline +void +BlockAllocator::Area::_FixBlockList(Block *block, Block *prevBlock, + Block *nextBlock) +{ + if (block) { + if (prevBlock) + prevBlock->SetNextBlock(block); + else + fFirstBlock = block; + if (nextBlock) + nextBlock->SetPreviousBlock(block); + else + fLastBlock = block; + } +} + +// _FixFreeList +inline +void +BlockAllocator::Area::_FixFreeList(TFreeBlock *block, TFreeBlock *prevFree, + TFreeBlock *nextFree) +{ + if (block) { + if (prevFree) + prevFree->SetNextFreeBlock(block); + else + fFirstFree = block; + if (nextFree) + nextFree->SetPreviousFreeBlock(block); + else + fLastFree = block; + block->SetPreviousFreeBlock(prevFree); + block->SetNextFreeBlock(nextFree); + } +} + +// _DefragmentingRecommended +inline +bool +BlockAllocator::Area::_DefragmentingRecommended() +{ + // Defragmenting Condition: At least more than 5 free blocks and + // free / block ratio greater 1 / 10. Don't know, if that makes any + // sense. ;-) + return (fFreeBlockCount > 5 && fUsedBlockCount / fFreeBlockCount < 10); +} + +#endif // BA_DEFINE_INLINES + + +#endif // BLOCK_ALLOCATOR_AREA_H diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorAreaBucket.cpp b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorAreaBucket.cpp new file mode 100644 index 0000000000..9f325f4c9d --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorAreaBucket.cpp @@ -0,0 +1,51 @@ +// BlockAllocatorAreaBucket.cpp + +#include "BlockAllocatorAreaBucket.h" + +// constructor +BlockAllocator::AreaBucket::AreaBucket() + : fAreas(), + fIndex(-1), + fMinSize(0), + fMaxSize(0) +{ +} + +// destructor +BlockAllocator::AreaBucket::~AreaBucket() +{ + while (Area *area = fAreas.GetFirst()) { + RemoveArea(area); + area->Delete(); + } +} + +// SanityCheck +bool +BlockAllocator::AreaBucket::SanityCheck(bool deep) const +{ + // check area list + for (Area *area = GetFirstArea(); area; area = GetNextArea(area)) { + if (deep) { + if (!area->SanityCheck()) + return false; + } + // bucket + if (area->GetBucket() != this) { + FATAL(("Area %p is in bucket %p, but thinks it is in bucket %p\n", + area, this, area->GetBucket())); + BA_PANIC("Wrong area bucket."); + return false; + } + // size + size_t areaSize = area->GetFreeBytes(); + if (areaSize < fMinSize || areaSize >= fMaxSize) { + FATAL(("Area is in wrong bucket: free: %lu, min: %lu, max: %lu\n", + areaSize, fMinSize, fMaxSize)); + BA_PANIC("Area in wrong bucket."); + return false; + } + } + return true; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorAreaBucket.h b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorAreaBucket.h new file mode 100644 index 0000000000..e687463a7b --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorAreaBucket.h @@ -0,0 +1,97 @@ +// BlockAllocatorAreaBucket.h + +#ifndef BLOCK_ALLOCATOR_AREA_BUCKET_H +#define BLOCK_ALLOCATOR_AREA_BUCKET_H + +#include "BlockAllocator.h" +#include "BlockAllocatorArea.h" +#include "Debug.h" +#include "DLList.h" + +class BlockAllocator::AreaBucket { +public: + AreaBucket(); + ~AreaBucket(); + + inline void SetIndex(int32 index) { fIndex = index; } + inline int32 GetIndex() const { return fIndex; } + + inline void SetSizeLimits(size_t minSize, size_t maxSize); + inline size_t GetMinSize() const { return fMinSize; } // incl. + inline size_t GetMaxSize() const { return fMaxSize; } // excl. + + inline void AddArea(Area *area); + inline void RemoveArea(Area *area); + + inline Area *GetFirstArea() const { return fAreas.GetFirst(); } + inline Area *GetLastArea() const { return fAreas.GetLast(); } + inline Area *GetNextArea(Area* area) const; + + inline bool IsEmpty() const { return fAreas.IsEmpty(); } + + // debugging only + bool SanityCheck(bool deep = false) const; + +private: + DLList fAreas; + int32 fIndex; + size_t fMinSize; + size_t fMaxSize; +}; + +typedef BlockAllocator::AreaBucket AreaBucket; + + +// inline methods + +// debugging +#if BA_DEFINE_INLINES + +// SetSizeLimits +/*! \brief Sets the size limits for areas this bucket may contain. + \param minSize Minimal area size. Inclusively. + \param maxSize Maximal area size. Exlusively. +*/ +inline +void +BlockAllocator::AreaBucket::SetSizeLimits(size_t minSize, size_t maxSize) +{ + fMinSize = minSize; + fMaxSize = maxSize; +} + +// AddArea +inline +void +BlockAllocator::AreaBucket::AddArea(Area *area) +{ + if (area) { + fAreas.Insert(area, area->NeedsDefragmenting()); + area->SetBucket(this); + D(SanityCheck(false)); + } +} + +// RemoveArea +inline +void +BlockAllocator::AreaBucket::RemoveArea(Area *area) +{ + if (area) { + fAreas.Remove(area); + area->SetBucket(NULL); + D(SanityCheck(false)); + } +} + +// GetNextArea +inline +Area * +BlockAllocator::AreaBucket::GetNextArea(Area* area) const +{ + return fAreas.GetNext(area); +} + +#endif // BA_DEFINE_INLINES + +#endif // BLOCK_ALLOCATOR_AREA_BUCKET_H diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorMisc.h b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorMisc.h new file mode 100644 index 0000000000..eb8f8fdc10 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockAllocatorMisc.h @@ -0,0 +1,36 @@ +// BlockAllocatorMisc.h + +#ifndef BLOCK_ALLOCATOR_MISC_H +#define BLOCK_ALLOCATOR_MISC_H + +#include "Block.h" +#include "Misc.h" + +// block alignment -- start offsets and size +static const size_t kBlockAlignment = 4; // must be a power of 2 + +// block_align_{floor,ceil} +static inline size_t block_align_floor(size_t value) + { return value & ~(kBlockAlignment - 1); } +static inline size_t block_align_ceil(size_t value) + { return (value + kBlockAlignment - 1) & ~(kBlockAlignment - 1); } + +// minimal size of a gross/net block +// BAD DOG: No initializers in the kernel! +//static const size_t kMinBlockSize = block_align_ceil(sizeof(TFreeBlock)); +#define kMinBlockSize (block_align_ceil(sizeof(TFreeBlock))) +static const size_t kMinNetBlockSize = 8; + +static const size_t kDefragmentingTolerance = 10240; + +// bucket_containing_size -- bucket for to contain an area with size +static inline int bucket_containing_size(size_t size) + { return fls(size / kMinNetBlockSize) + 1; } + +// bucket_containing_min_size -- bucket containing areas >= size +static inline int +bucket_containing_min_size(size_t size) + { return (size ? bucket_containing_size(size - 1) + 1 : 0); } + + +#endif BLOCK_ALLOCATOR_MISC_H diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockReferenceManager.cpp b/src/add-ons/kernel/file_systems/ramfs/BlockReferenceManager.cpp new file mode 100644 index 0000000000..ebf78ccc48 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockReferenceManager.cpp @@ -0,0 +1,130 @@ +// BlockReferenceManager.cpp + +#include "AllocationInfo.h" +#include "Block.h" +#include "BlockAllocator.h" // only for BA_PANIC +#include "BlockReferenceManager.h" +#include "Debug.h" + +static const int kBlockReferenceTableSize = 128; + +// constructor +BlockReferenceManager::BlockReferenceManager() + : fTables(10), + fFreeList(NULL) +{ +} + +// destructor +BlockReferenceManager::~BlockReferenceManager() +{ +} + +// AllocateReference +BlockReference * +BlockReferenceManager::AllocateReference() +{ + BlockReference *reference = NULL; + if (!fFreeList) + _AddTable(); + if (fFreeList) { + reference = fFreeList; + fFreeList = *(BlockReference**)fFreeList; + } + return reference; +} + +// FreeReference +void +BlockReferenceManager::FreeReference(BlockReference *reference) +{ + if (reference) { + *(BlockReference**)reference = fFreeList; + fFreeList = reference; + } +} + +// CheckReference +bool +BlockReferenceManager::CheckReference(BlockReference *reference) +{ + if (reference) { + uint32 address = (uint32)reference; + int32 tableCount = fTables.CountItems(); + for (int32 i = 0; i < tableCount; i++) { + Table *table = &fTables.ItemAt(i); + uint32 first = (uint32)table->GetReferences(); + uint32 last = (uint32)(table->GetReferences() + table->GetSize()); + if (first <= address && address < last) + return true; + } + } + FATAL(("BlockReference %p does not exist!\n", reference)); + BA_PANIC("BlockReference doesn't exist."); + return false; +} + +// GetAllocationInfo +void +BlockReferenceManager::GetAllocationInfo(AllocationInfo &info) +{ + info.AddListAllocation(fTables.GetCapacity(), sizeof(Table)); + int32 count = fTables.CountItems(); + for (int32 i = 0; i < count; i++) { + Table &table = fTables.ItemAt(i); + info.AddOtherAllocation(table.GetSize() * sizeof(BlockReference)); + } +} + +// _AddTable +status_t +BlockReferenceManager::_AddTable() +{ + status_t error = B_OK; + // add a new table + Table dummy; + if (fTables.AddItem(dummy)) { + int32 index = fTables.CountItems() - 1; + Table &table = fTables.ItemAt(index); + error = table.Init(kBlockReferenceTableSize); + if (error == B_OK) { + // add the references to the free list + uint32 count = table.GetSize(); + BlockReference *references = table.GetReferences(); + for (uint32 i = 0; i < count; i++) { + BlockReference *reference = references + i; + *(BlockReference**)reference = fFreeList; + fFreeList = reference; + } + } else + fTables.RemoveItem(index); + } else + SET_ERROR(error, B_NO_MEMORY); + return error; +} + + +// Table + +// destructor +BlockReferenceManager::Table::~Table() +{ + if (fReferences) + delete[] fReferences; +} + +// Init +status_t +BlockReferenceManager::Table::Init(int32 size) +{ + status_t error = (size > 0 ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + fReferences = new(nothrow) BlockReference[size]; + if (fReferences) + fSize = size; + else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/BlockReferenceManager.h b/src/add-ons/kernel/file_systems/ramfs/BlockReferenceManager.h new file mode 100644 index 0000000000..a0fb11cc20 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/BlockReferenceManager.h @@ -0,0 +1,49 @@ +// BlockReferenceManager.h + +#ifndef BLOCK_REFERENCE_MANAGER_H +#define BLOCK_REFERENCE_MANAGER_H + +#include "List.h" + +class AllocationInfo; +class BlockReference; + +class BlockReferenceManager { +public: + BlockReferenceManager(); + ~BlockReferenceManager(); + + BlockReference *AllocateReference(); + void FreeReference(BlockReference *reference); + + // debugging only + bool CheckReference(BlockReference *reference); + void GetAllocationInfo(AllocationInfo &info); + +private: + status_t _AddTable(); + +private: + class Table { + public: + Table() : fSize(0), fReferences(NULL) {} + Table(int) : fSize(0), fReferences(NULL) {} + ~Table(); + + status_t Init(int32 size); + + BlockReference *GetReferences() { return fReferences; } + + int32 GetSize() const { return fSize; } + + private: + uint32 fSize; + BlockReference *fReferences; + }; + + List fTables; + BlockReference *fFreeList; +}; + +#endif // BLOCK_REFERENCE_MANAGER_H + diff --git a/src/add-ons/kernel/file_systems/ramfs/DataContainer.cpp b/src/add-ons/kernel/file_systems/ramfs/DataContainer.cpp new file mode 100644 index 0000000000..37e6f4d6ee --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/DataContainer.cpp @@ -0,0 +1,417 @@ +// DataContainer.cpp + +#include "AllocationInfo.h" +#include "Attribute.h" // for debugging only +#include "Block.h" +#include "DataContainer.h" +#include "Debug.h" +#include "Misc.h" +#include "Node.h" // for debugging only +#include "Volume.h" + +// constructor +DataContainer::DataContainer(Volume *volume) + : fVolume(volume), + fSize(0) +{ +} + +// destructor +DataContainer::~DataContainer() +{ + Resize(0); +} + +// InitCheck +status_t +DataContainer::InitCheck() const +{ + return (fVolume ? B_OK : B_ERROR); +} + +// Resize +status_t +DataContainer::Resize(off_t newSize) +{ + status_t error = B_OK; + if (newSize < 0) + newSize = 0; + if (newSize != fSize) { + // Shrinking should never fail. Growing can fail, if we run out of + // memory. Then we try to shrink back to the original size. + off_t oldSize = fSize; + error = _Resize(newSize); + if (error == B_NO_MEMORY && newSize > fSize) + _Resize(oldSize); + } + return error; +} + +// ReadAt +status_t +DataContainer::ReadAt(off_t offset, void *_buffer, size_t size, + size_t *bytesRead) +{ + uint8 *buffer = (uint8*)_buffer; + status_t error = (buffer && offset >= 0 && bytesRead ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // read not more than we have to offer + offset = min(offset, fSize); + size = min(size, size_t(fSize - offset)); + // iterate through the blocks, reading as long as there's something + // left to read + size_t blockSize = fVolume->GetBlockSize(); + *bytesRead = 0; + while (size > 0) { + size_t inBlockOffset = offset % blockSize; + size_t toRead = min(size, size_t(blockSize - inBlockOffset)); + void *blockData = _GetBlockDataAt(offset / blockSize, + inBlockOffset, toRead); +D( +if (!blockData) { + Node *node = NULL; + if (Attribute *attribute = dynamic_cast(this)) { + FATAL(("attribute `%s' of\n", attribute->GetName())); + node = attribute->GetNode(); + } else { + node = dynamic_cast(this); + } + if (node) +// FATAL(("node `%s'\n", node->GetName())); + FATAL(("container size: %Ld, offset: %Ld, buffer size: %lu\n", + fSize, offset, size)); + return B_ERROR; +} +); + memcpy(buffer, blockData, toRead); + buffer += toRead; + size -= toRead; + offset += toRead; + *bytesRead += toRead; + } + } + return error; +} + +// WriteAt +status_t +DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size, + size_t *bytesWritten) +{ +//PRINT(("DataContainer::WriteAt(%Ld, %p, %lu, %p), fSize: %Ld\n", offset, _buffer, size, bytesWritten, fSize)); + const uint8 *buffer = (const uint8*)_buffer; + status_t error = (buffer && offset >= 0 && bytesWritten + ? B_OK : B_BAD_VALUE); + // resize the container, if necessary + if (error == B_OK) { + off_t newSize = offset + size; + off_t oldSize = fSize; + if (newSize > fSize) { + error = Resize(newSize); + // pad with zero, if necessary + if (error == B_OK && offset > oldSize) + _ClearArea(offset, oldSize - offset); + } + } + if (error == B_OK) { + // iterate through the blocks, writing as long as there's something + // left to write + size_t blockSize = fVolume->GetBlockSize(); + *bytesWritten = 0; + while (size > 0) { + size_t inBlockOffset = offset % blockSize; + size_t toWrite = min(size, size_t(blockSize - inBlockOffset)); + void *blockData = _GetBlockDataAt(offset / blockSize, + inBlockOffset, toWrite); +D(if (!blockData) return B_ERROR;); + memcpy(blockData, buffer, toWrite); + buffer += toWrite; + size -= toWrite; + offset += toWrite; + *bytesWritten += toWrite; + } + } +//PRINT(("DataContainer::WriteAt() done: %lx, fSize: %Ld\n", error, fSize)); + return error; +} + +// GetFirstDataBlock +void +DataContainer::GetFirstDataBlock(const uint8 **data, size_t *length) +{ + if (data && length) { + if (_IsBlockMode()) { + BlockReference *block = _GetBlockList()->ItemAt(0); + *data = (const uint8*)block->GetData(); + *length = min(fSize, fVolume->GetBlockSize()); + } else { + *data = fSmallBuffer; + *length = fSize; + } + } +} + +// GetAllocationInfo +void +DataContainer::GetAllocationInfo(AllocationInfo &info) +{ + if (_IsBlockMode()) { + BlockList *blocks = _GetBlockList(); + info.AddListAllocation(blocks->GetCapacity(), sizeof(BlockReference*)); + int32 blockCount = blocks->CountItems(); + for (int32 i = 0; i < blockCount; i++) + info.AddBlockAllocation(blocks->ItemAt(i)->GetBlock()->GetSize()); + } else { + // ... + } +} + +// _RequiresBlockMode +inline +bool +DataContainer::_RequiresBlockMode(size_t size) +{ + return (size > kSmallDataContainerSize); +} + +// _IsBlockMode +inline +bool +DataContainer::_IsBlockMode() const +{ + return (fSize > kSmallDataContainerSize); +} + +// _Resize +status_t +DataContainer::_Resize(off_t newSize) +{ +//PRINT(("DataContainer::_Resize(%Ld), fSize: %Ld\n", newSize, fSize)); + status_t error = B_OK; + if (newSize != fSize) { + size_t blockSize = fVolume->GetBlockSize(); + int32 blockCount = _CountBlocks(); + int32 newBlockCount = (newSize + blockSize - 1) / blockSize; + if (newBlockCount == blockCount) { + // only the last block needs to be resized + if (_IsBlockMode() && _RequiresBlockMode(newSize)) { + // keep block mode + error = _ResizeLastBlock((newSize - 1) % blockSize + 1); + } else if (!_IsBlockMode() && !_RequiresBlockMode(newSize)) { + // keep small buffer mode + fSize = newSize; + } else if (fSize < newSize) { + // switch to block mode + _SwitchToBlockMode(newSize); + } else { + // switch to small buffer mode + _SwitchToSmallBufferMode(newSize); + } + } else if (newBlockCount < blockCount) { + // shrink + if (_IsBlockMode()) { + // remove the last blocks + BlockList *blocks = _GetBlockList(); + for (int32 i = blockCount - 1; i >= newBlockCount; i--) { + BlockReference *block = blocks->ItemAt(i); + blocks->RemoveItem(i); + fVolume->FreeBlock(block); + fSize = (fSize - 1) / blockSize * blockSize; + } + // resize the last block to the correct size, respectively + // switch to small buffer mode + if (_RequiresBlockMode(newSize)) + error = _ResizeLastBlock((newSize - 1) % blockSize + 1); + else + _SwitchToSmallBufferMode(newSize); + } else { + // small buffer mode: just set the new size + fSize = newSize; + } + } else { + // grow + if (_RequiresBlockMode(newSize)) { + // resize the first block to the correct size, respectively + // switch to block mode + if (_IsBlockMode()) + error = _ResizeLastBlock(blockSize); + else { + error = _SwitchToBlockMode(min((size_t)newSize, + blockSize)); + } + // add new blocks + BlockList *blocks = _GetBlockList(); + while (error == B_OK && fSize < newSize) { + size_t newBlockSize = min(size_t(newSize - fSize), + blockSize); + BlockReference *block = NULL; + error = fVolume->AllocateBlock(newBlockSize, &block); + if (error == B_OK) { + if (blocks->AddItem(block)) + fSize += newBlockSize; + else { + SET_ERROR(error, B_NO_MEMORY); + fVolume->FreeBlock(block); + } + } + } + } else { + // no need to switch to block mode: just set the new size + fSize = newSize; + } + } + } +//PRINT(("DataContainer::_Resize() done: %lx, fSize: %Ld\n", error, fSize)); + return error; +} + +// _GetBlockList +inline +DataContainer::BlockList * +DataContainer::_GetBlockList() +{ + return (BlockList*)fBlocks; +} + +// _GetBlockList +inline +const DataContainer::BlockList * +DataContainer::_GetBlockList() const +{ + return (BlockList*)fBlocks; +} + +// _CountBlocks +inline +int32 +DataContainer::_CountBlocks() const +{ + if (_IsBlockMode()) + return _GetBlockList()->CountItems(); + else if (fSize == 0) // small buffer mode, empty buffer + return 0; + return 1; // small buffer mode, non-empty buffer +} + +// _GetBlockDataAt +inline +void * +DataContainer::_GetBlockDataAt(int32 index, size_t offset, size_t DARG(size)) +{ + if (_IsBlockMode()) { + BlockReference *block = _GetBlockList()->ItemAt(index); +D(if (!fVolume->CheckBlock(block, offset + size)) return NULL;); + return block->GetDataAt(offset); + } else { +D( +if (offset + size > kSmallDataContainerSize) { + FATAL(("DataContainer: Data access exceeds small buffer.\n")); + PANIC("DataContainer: Data access exceeds small buffer."); + return NULL; +} +); + return fSmallBuffer + offset; + } +} + +// _ClearArea +void +DataContainer::_ClearArea(off_t offset, off_t size) +{ + // constrain the area to the data area + offset = min(offset, fSize); + size = min(size, fSize - offset); + // iterate through the blocks, clearing as long as there's something + // left to clear + size_t blockSize = fVolume->GetBlockSize(); + while (size > 0) { + size_t inBlockOffset = offset % blockSize; + size_t toClear = min(size_t(size), blockSize - inBlockOffset); + void *blockData = _GetBlockDataAt(offset / blockSize, inBlockOffset, + toClear); +D(if (!blockData) return;); + memset(blockData, 0, toClear); + size -= toClear; + offset += toClear; + } +} + +// _ResizeLastBlock +status_t +DataContainer::_ResizeLastBlock(size_t newSize) +{ +//PRINT(("DataContainer::_ResizeLastBlock(%lu), fSize: %Ld\n", newSize, fSize)); + int32 blockCount = _CountBlocks(); + status_t error = (fSize > 0 && blockCount > 0 && newSize > 0 + ? B_OK : B_BAD_VALUE); +D( +if (!_IsBlockMode()) { + FATAL(("Call of _ResizeLastBlock() in small buffer mode.\n")); + PANIC("Call of _ResizeLastBlock() in small buffer mode."); + return B_ERROR; +} +); + if (error == B_OK) { + size_t blockSize = fVolume->GetBlockSize(); + size_t oldSize = (fSize - 1) % blockSize + 1; + if (newSize != oldSize) { + BlockList *blocks = _GetBlockList(); + BlockReference *block = blocks->ItemAt(blockCount - 1); + BlockReference *newBlock = fVolume->ResizeBlock(block, newSize); + if (newBlock) { + if (newBlock != block) + blocks->ReplaceItem(blockCount - 1, newBlock); + fSize += off_t(newSize) - oldSize; + } else + SET_ERROR(error, B_NO_MEMORY); + } + } +//PRINT(("DataContainer::_ResizeLastBlock() done: %lx, fSize: %Ld\n", error, fSize)); + return error; +} + +// _SwitchToBlockMode +status_t +DataContainer::_SwitchToBlockMode(size_t newBlockSize) +{ + // allocate a new block + BlockReference *block = NULL; + status_t error = fVolume->AllocateBlock(newBlockSize, &block); + if (error == B_OK) { + // copy the data from the small buffer into the block + if (fSize > 0) + memcpy(block->GetData(), fSmallBuffer, fSize); + // construct the block list and add the block + new (fBlocks) BlockList(10); + BlockList *blocks = _GetBlockList(); + if (blocks->AddItem(block)) { + fSize = newBlockSize; + } else { + // error: destroy the block list and free the block + SET_ERROR(error, B_NO_MEMORY); + blocks->~BlockList(); + if (fSize > 0) + memcpy(fSmallBuffer, block->GetData(), fSize); + fVolume->FreeBlock(block); + } + } + return error; +} + +// _SwitchToSmallBufferMode +void +DataContainer::_SwitchToSmallBufferMode(size_t newSize) +{ + // remove the first (and only) block + BlockList *blocks = _GetBlockList(); + BlockReference *block = blocks->ItemAt(0); + blocks->RemoveItem(0L); + // destroy the block list and copy the data into the small buffer + blocks->~BlockList(); + if (newSize > 0) + memcpy(fSmallBuffer, block->GetData(), newSize); + // free the block and set the new size + fVolume->FreeBlock(block); + fSize = newSize; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/DataContainer.h b/src/add-ons/kernel/file_systems/ramfs/DataContainer.h new file mode 100644 index 0000000000..55c2f1d736 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/DataContainer.h @@ -0,0 +1,84 @@ +// DataContainer.h + +#ifndef DATA_CONTAINER_H +#define DATA_CONTAINER_H + +#include "List.h" + +class AllocationInfo; +class BlockReference; +class Volume; + +// Size of the DataContainer's small buffer. If it contains data up to this +// size, no blocks are allocated, but the small buffer is used instead. +// 16 bytes are for free, since they are shared with the block list. +// (actually even more, since the list has an initial size). +// I ran a test analyzing what sizes the attributes in my system have: +// size percentage bytes used in average +// <= 0 0.00 93.45 +// <= 4 25.46 75.48 +// <= 8 30.54 73.02 +// <= 16 52.98 60.37 +// <= 32 80.19 51.74 +// <= 64 94.38 70.54 +// <= 126 96.90 128.23 +// +// For average memory usage it is assumed, that attributes larger than 126 +// bytes have size 127, that the list has an initial capacity of 10 entries +// (40 bytes), that the block reference consumes 4 bytes and the block header +// 12 bytes. The optimal length is actually 35, with 51.05 bytes per +// attribute, but I conservatively rounded to 32. +static const size_t kSmallDataContainerSize = 32; + +class DataContainer { +public: + DataContainer(Volume *volume); + virtual ~DataContainer(); + + status_t InitCheck() const; + + Volume *GetVolume() const { return fVolume; } + + status_t Resize(off_t newSize); + off_t GetSize() const { return fSize; } + + virtual status_t ReadAt(off_t offset, void *buffer, size_t size, + size_t *bytesRead); + virtual status_t WriteAt(off_t offset, const void *buffer, size_t size, + size_t *bytesWritten); + + void GetFirstDataBlock(const uint8 **data, size_t *length); + + // debugging + void GetAllocationInfo(AllocationInfo &info); + +private: + typedef List BlockList; + +private: + static inline bool _RequiresBlockMode(size_t size); + inline bool _IsBlockMode() const; + + inline BlockList *_GetBlockList(); + inline const BlockList *_GetBlockList() const; + inline int32 _CountBlocks() const; + inline void *_GetBlockDataAt(int32 index, size_t offset, size_t size); + + void _ClearArea(off_t offset, off_t size); + + status_t _Resize(off_t newSize); + status_t _ResizeLastBlock(size_t newSize); + + status_t _SwitchToBlockMode(size_t newBlockSize); + void _SwitchToSmallBufferMode(size_t newSize); + +private: + Volume *fVolume; + off_t fSize; + union { + uint8 fBlocks[sizeof(BlockList)]; + uint8 fSmallBuffer[kSmallDataContainerSize]; + }; +}; + +#endif // DATA_CONTAINER_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Directory.cpp b/src/add-ons/kernel/file_systems/ramfs/Directory.cpp new file mode 100644 index 0000000000..2da28a52af --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Directory.cpp @@ -0,0 +1,348 @@ +// Directory.cpp + +#include "AllocationInfo.h" +#include "Debug.h" +#include "Directory.h" +#include "Entry.h" +#include "EntryIterator.h" +#include "File.h" +#include "SymLink.h" +#include "Volume.h" + +// constructor +Directory::Directory(Volume *volume) + : Node(volume, NODE_TYPE_DIRECTORY), + fEntries() +{ +} + +// destructor +Directory::~Directory() +{ + // delete all entries + while (Entry *entry = fEntries.GetFirst()) { + if (DeleteEntry(entry) != B_OK) { + FATAL(("Could not delete all entries in directory.\n")); + break; + } + } +} + +// Link +status_t +Directory::Link(Entry *entry) +{ + if (fReferrers.IsEmpty()) + return Node::Link(entry); + return B_IS_A_DIRECTORY; +} + +// Unlink +status_t +Directory::Unlink(Entry *entry) +{ + if (entry == fReferrers.GetFirst()) + return Node::Unlink(entry); + return B_BAD_VALUE; +} + +// SetSize +status_t +Directory::SetSize(off_t /*newSize*/) +{ + return B_IS_A_DIRECTORY; +} + +// GetSize +off_t +Directory::GetSize() const +{ + return 0; +} + +// GetParent +Directory * +Directory::GetParent() const +{ + Entry *entry = fReferrers.GetFirst(); + return (entry ? entry->GetParent() : NULL); +} + +// CreateDirectory +status_t +Directory::CreateDirectory(const char *name, Directory **directory) +{ + status_t error = (name && directory ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // create directory + if (Directory *node = new(nothrow) Directory(GetVolume())) { + error = _CreateCommon(node, name); + // deletes the node on failure + if (error == B_OK) + *directory = node; + } else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// CreateFile +status_t +Directory::CreateFile(const char *name, File **file) +{ + status_t error = (name && file ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // create file + if (File *node = new(nothrow) File(GetVolume())) { + error = _CreateCommon(node, name); + // deletes the node on failure + if (error == B_OK) + *file = node; + } else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// CreateSymLink +status_t +Directory::CreateSymLink(const char *name, const char *path, SymLink **symLink) +{ + status_t error = (name && symlink ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // create symlink + if (SymLink *node = new(nothrow) SymLink(GetVolume())) { + error = node->SetLinkedPath(path); + if (error == B_OK) { + error = _CreateCommon(node, name); + // deletes the node on failure + if (error == B_OK) + *symLink = node; + } else + delete node; + } else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// AddEntry +status_t +Directory::AddEntry(Entry *entry) +{ + status_t error = (entry && !entry->GetParent() ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + fEntries.Insert(entry); + entry->SetParent(this); + error = GetVolume()->EntryAdded(GetID(), entry); + if (error == B_OK) { + MarkModified(); + } else { + fEntries.Remove(entry); + entry->SetParent(NULL); + } + } + return error; +} + +// CreateEntry +status_t +Directory::CreateEntry(Node *node, const char *name, Entry **_entry) +{ + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // create an entry + Entry *entry = new(nothrow) Entry(name); + if (entry) { + error = entry->InitCheck(); + if (error == B_OK) { + // link to the node + error = entry->Link(node); + if (error == B_OK) { + // add the entry + error = AddEntry(entry); + if (error == B_OK) { + if (_entry) + *_entry = entry; + } else { + // failure: unlink the node + entry->Unlink(); + } + } + } + // delete the entry on failure + if (error != B_OK) + delete entry; + } else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// RemoveEntry +status_t +Directory::RemoveEntry(Entry *entry) +{ + status_t error = (entry && entry->GetParent() == this ? B_OK + : B_BAD_VALUE); + if (error == B_OK) { + // move all iterators pointing to the entry to the next entry + if (GetVolume()->IteratorLock()) { + // set the iterators' current entry + Entry *nextEntry = fEntries.GetNext(entry); + DLList *iterators = entry->GetEntryIteratorList(); + for (EntryIterator *iterator = iterators->GetFirst(); + iterator; + iterator = iterators->GetNext(iterator)) { + iterator->SetCurrent(nextEntry, true); + } + // Move the iterators from one list to the other, or just remove + // them, if there is no next entry. + if (nextEntry) { + DLList *nextIterators + = nextEntry->GetEntryIteratorList(); + nextIterators->MoveFrom(iterators); + } else + iterators->RemoveAll(); + GetVolume()->IteratorUnlock(); + } else + error = B_ERROR; + // remove the entry + if (error == B_OK) { + error = GetVolume()->EntryRemoved(GetID(), entry); + if (error == B_OK) { + fEntries.Remove(entry); + entry->SetParent(NULL); + MarkModified(); + } + } + } + return error; +} + +// DeleteEntry +status_t +Directory::DeleteEntry(Entry *entry) +{ + status_t error = RemoveEntry(entry); + if (error == B_OK) { + error = entry->Unlink(); + if (error == B_OK) + delete entry; + else { + FATAL(("Failed to Unlink() entry %p from node %Ld!\n", entry, + entry->GetNode()->GetID())); + AddEntry(entry); + } + } + return error; +} + +// FindEntry +status_t +Directory::FindEntry(const char *name, Entry **_entry) const +{ + status_t error = (name && _entry ? B_OK : B_BAD_VALUE); + if (error == B_OK) { +/* + Entry *entry = NULL; + while (GetNextEntry(&entry) == B_OK) { + if (!strcmp(entry->GetName(), name)) { + *_entry = entry; + return B_OK; + } + } + error = B_ENTRY_NOT_FOUND; +*/ + error = GetVolume()->FindEntry(GetID(), name, _entry); + } + return error; +} + +// FindNode +status_t +Directory::FindNode(const char *name, Node **node) const +{ + status_t error = (name && node ? B_OK : B_BAD_VALUE); + Entry *entry = NULL; + if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) + *node = entry->GetNode(); + return error; +} + +// FindAndGetNode +status_t +Directory::FindAndGetNode(const char *name, Node **node, Entry **_entry) const +{ + status_t error = (name && node ? B_OK : B_BAD_VALUE); + Entry *entry = NULL; + if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) { + *node = entry->GetNode(); + if (_entry) + *_entry = entry; + error = GetVolume()->GetVNode(*node); + } + return error; +} + +// GetPreviousEntry +status_t +Directory::GetPreviousEntry(Entry **entry) const +{ + status_t error = (entry ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (!*entry) + *entry = fEntries.GetLast(); + else if ((*entry)->GetParent() == this) + *entry = fEntries.GetPrevious(*entry); + else + error = B_BAD_VALUE; + if (error == B_OK && !*entry) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// GetNextEntry +status_t +Directory::GetNextEntry(Entry **entry) const +{ + status_t error = (entry ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (!*entry) + *entry = fEntries.GetFirst(); + else if ((*entry)->GetParent() == this) + *entry = fEntries.GetNext(*entry); + else + error = B_BAD_VALUE; + if (error == B_OK && !*entry) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// GetAllocationInfo +void +Directory::GetAllocationInfo(AllocationInfo &info) +{ + Node::GetAllocationInfo(info); + info.AddDirectoryAllocation(); + Entry *entry = NULL; + while (GetNextEntry(&entry) == B_OK) + entry->GetAllocationInfo(info); +} + +// _CreateCommon +status_t +Directory::_CreateCommon(Node *node, const char *name) +{ + status_t error = node->InitCheck(); + if (error == B_OK) { + // add node to directory + error = CreateEntry(node, name); + } + if (error != B_OK) + delete node; + return error; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/Directory.h b/src/add-ons/kernel/file_systems/ramfs/Directory.h new file mode 100644 index 0000000000..09b5cf8636 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Directory.h @@ -0,0 +1,56 @@ +// Directory.h + +#ifndef DIRECTORY_H +#define DIRECTORY_H + +#include "DLList.h" +#include "Node.h" + +class Entry; +class File; +class SymLink; + +class Directory : public Node { +public: + Directory(Volume *volume); + virtual ~Directory(); + + virtual status_t Link(Entry *entry); + virtual status_t Unlink(Entry *entry); + + virtual status_t SetSize(off_t newSize); + virtual off_t GetSize() const; + + Directory *GetParent() const; + + status_t CreateDirectory(const char *name, Directory **directory); + status_t CreateFile(const char *name, File **file); + status_t CreateSymLink(const char *name, const char *path, + SymLink **symLink); + + bool IsEmpty() const { return fEntries.IsEmpty(); } + + status_t AddEntry(Entry *entry); + status_t CreateEntry(Node *node, const char *name, Entry **entry = NULL); + status_t RemoveEntry(Entry *entry); + status_t DeleteEntry(Entry *entry); + + status_t FindEntry(const char *name, Entry **entry) const; + status_t FindNode(const char *name, Node **node) const; + status_t FindAndGetNode(const char *name, Node **node, + Entry **entry = NULL) const; + + status_t GetPreviousEntry(Entry **entry) const; + status_t GetNextEntry(Entry **entry) const; + + // debugging + virtual void GetAllocationInfo(AllocationInfo &info); + +private: + status_t _CreateCommon(Node *node, const char *name); + +private: + DLList fEntries; +}; + +#endif // DIRECTORY_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Entry.cpp b/src/add-ons/kernel/file_systems/ramfs/Entry.cpp new file mode 100644 index 0000000000..230de6ae57 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Entry.cpp @@ -0,0 +1,104 @@ +// Entry.cpp + +#include "AllocationInfo.h" +#include "Debug.h" +#include "Entry.h" +#include "EntryIterator.h" +#include "Node.h" +#include "Volume.h" + +// constructor +Entry::Entry(const char *name, Node *node, Directory *parent) + : fParent(parent), + fNode(node), + fName(name), + fReferrerLink(), + fIterators() +{ + if (node) + Link(node); +} + +// destructor +Entry::~Entry() +{ + if (fNode) + Unlink(); +} + +// InitCheck +status_t +Entry::InitCheck() const +{ + return (fName.GetString() ? B_OK : B_NO_INIT); +} + +// Link +status_t +Entry::Link(Node *node) +{ + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // We first link to the new node and then unlink the old one. So, no + // harm is done, if both are the same. + Node *oldNode = fNode; + error = node->Link(this); + if (error == B_OK) { + fNode = node; + if (oldNode) + oldNode->Unlink(this); + } + } + return error; +} + +// Unlink +status_t +Entry::Unlink() +{ + status_t error = (fNode ? B_OK : B_BAD_VALUE); + if (error == B_OK && (error = fNode->Unlink(this)) == B_OK) + fNode = NULL; + return error; +} + +// SetName +status_t +Entry::SetName(const char *newName) +{ + status_t error = (newName ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (fName.SetTo(newName)) { +// if (fNode) +// fNode->MarkModified(); + } else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// AttachEntryIterator +void +Entry::AttachEntryIterator(EntryIterator *iterator) +{ + if (iterator && iterator->GetCurrent() == this && !iterator->IsSuspended()) + fIterators.Insert(iterator); +} + +// DetachEntryIterator +void +Entry::DetachEntryIterator(EntryIterator *iterator) +{ + if (iterator && iterator->GetCurrent() == this && iterator->IsSuspended()) + fIterators.Remove(iterator); +} + +// GetAllocationInfo +void +Entry::GetAllocationInfo(AllocationInfo &info) +{ + info.AddEntryAllocation(); + info.AddStringAllocation(fName.GetLength()); + fNode->GetAllocationInfo(info); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/Entry.h b/src/add-ons/kernel/file_systems/ramfs/Entry.h new file mode 100644 index 0000000000..6dd5510489 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Entry.h @@ -0,0 +1,69 @@ +// Entry.h + +#ifndef ENTRY_H +#define ENTRY_H + +#include + +#include "DLList.h" +#include "fsproto.h" +#include "String.h" + +class AllocationInfo; +class Directory; +class EntryIterator; +class Node; + +class Entry : public DLListLinkImpl { +public: + Entry(const char *name, Node *node = NULL, Directory *parent = NULL); + ~Entry(); + + status_t InitCheck() const; + + inline void SetParent(Directory *parent) { fParent = parent; } + Directory *GetParent() const { return fParent; } + +// inline void SetNode(Node *node) { fNode = node; } + status_t Link(Node *node); + status_t Unlink(); + Node *GetNode() const { return fNode; } + + status_t SetName(const char *newName); + inline const char *GetName() const { return fName.GetString(); } + +// inline Volume *GetVolume() const { return fVolume; } + + inline DLListLink *GetReferrerLink() { return &fReferrerLink; } + + // entry iterator management + void AttachEntryIterator(EntryIterator *iterator); + void DetachEntryIterator(EntryIterator *iterator); + inline DLList *GetEntryIteratorList() + { return &fIterators; } + + // debugging + void GetAllocationInfo(AllocationInfo &info); + +private: + Directory *fParent; + Node *fNode; + String fName; + DLListLink fReferrerLink; + // iterator management + DLList fIterators; +}; + +// GetNodeReferrerLink +class GetNodeReferrerLink { +private: + typedef DLListLink Link; + +public: + inline Link *operator()(Entry *entry) const + { + return entry->GetReferrerLink(); + } +}; + +#endif // ENTRY_H diff --git a/src/add-ons/kernel/file_systems/ramfs/EntryIterator.cpp b/src/add-ons/kernel/file_systems/ramfs/EntryIterator.cpp new file mode 100644 index 0000000000..6d1e70214f --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/EntryIterator.cpp @@ -0,0 +1,137 @@ +// EntryIterator.cpp + +#include "Directory.h" +#include "Entry.h" +#include "EntryIterator.h" +#include "Volume.h" + +// constructor +EntryIterator::EntryIterator(Directory *directory) + : fDirectory(directory), + fEntry(NULL), + fSuspended(false), + fIsNext(false), + fDone(false) +{ +} + +// destructor +EntryIterator::~EntryIterator() +{ + Unset(); +} + +// SetTo +status_t +EntryIterator::SetTo(Directory *directory) +{ + Unset(); + status_t error = (directory ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + fDirectory = directory; + fEntry = NULL; + fSuspended = false; + fIsNext = false; + fDone = false; + } + return error; +} + +// Unset +void +EntryIterator::Unset() +{ + if (fDirectory && fSuspended) + Resume(); + fDirectory = NULL; + fEntry = NULL; + fSuspended = false; + fIsNext = false; + fDone = false; +} + +// Suspend +status_t +EntryIterator::Suspend() +{ + status_t error = (fDirectory ? B_OK : B_ERROR); + if (error == B_OK) { + if (fDirectory->GetVolume()->IteratorLock()) { + if (!fSuspended) { + if (fEntry) + fEntry->AttachEntryIterator(this); + fDirectory->GetVolume()->IteratorUnlock(); + fSuspended = true; + } else + error = B_ERROR; + } else + error = B_ERROR; + } + return error; +} + +// Resume +status_t +EntryIterator::Resume() +{ + status_t error = (fDirectory ? B_OK : B_ERROR); + if (error == B_OK) { + if (fDirectory->GetVolume()->IteratorLock()) { + if (fSuspended) { + if (fEntry) + fEntry->DetachEntryIterator(this); + fSuspended = false; + } + fDirectory->GetVolume()->IteratorUnlock(); + } else + error = B_ERROR; + } + return error; +} + +// GetNext +status_t +EntryIterator::GetNext(Entry **entry) +{ + status_t error = B_ENTRY_NOT_FOUND; + if (!fDone && fDirectory && entry) { + if (fIsNext) { + fIsNext = false; + if (fEntry) + error = B_OK; + } else + error = fDirectory->GetNextEntry(&fEntry); + *entry = fEntry; + } + fDone = (error != B_OK); + return error; +} + +// Rewind +status_t +EntryIterator::Rewind() +{ + status_t error = (fDirectory ? B_OK : B_ERROR); + if (error == B_OK) { + if (fDirectory->GetVolume()->IteratorLock()) { + if (fSuspended && fEntry) + fEntry->DetachEntryIterator(this); + fEntry = NULL; + fIsNext = false; + fDone = false; + fDirectory->GetVolume()->IteratorUnlock(); + } else + error = B_ERROR; + } + return error; +} + +// SetCurrent +void +EntryIterator::SetCurrent(Entry *entry, bool isNext) +{ + fIsNext = isNext; + fEntry = entry; + fDone = !fEntry; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/EntryIterator.h b/src/add-ons/kernel/file_systems/ramfs/EntryIterator.h new file mode 100644 index 0000000000..05ffbf8851 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/EntryIterator.h @@ -0,0 +1,46 @@ +// EntryIterator.h + +#ifndef ENTRY_ITERATOR_H +#define ENTRY_ITERATOR_H + +#include + +#include "DLList.h" + +class Directory; +class Entry; + +class EntryIterator : public DLListLinkImpl { +public: + EntryIterator(Directory *directory = NULL); + ~EntryIterator(); + + status_t SetTo(Directory *directory); + void Unset(); + + Directory *GetDirectory() const { return fDirectory; } + + status_t Suspend(); + status_t Resume(); + bool IsSuspended() const { return fSuspended; } + + status_t GetNext(Entry **entry); + Entry *GetCurrent() const { return fEntry; } + + status_t Rewind(); + +private: + void SetCurrent(Entry *entry, bool isNext); + +private: + friend class Directory; + +private: + Directory *fDirectory; + Entry *fEntry; + bool fSuspended; + bool fIsNext; + bool fDone; +}; + +#endif // ENTRY_ITERATOR_H diff --git a/src/add-ons/kernel/file_systems/ramfs/EntryListener.cpp b/src/add-ons/kernel/file_systems/ramfs/EntryListener.cpp new file mode 100644 index 0000000000..b2f1a3bec9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/EntryListener.cpp @@ -0,0 +1,26 @@ +// EntryListener.cpp + +#include "EntryListener.h" + +// constructor +EntryListener::EntryListener() +{ +} + +// destructor +EntryListener::~EntryListener() +{ +} + +// EntryAdded +void +EntryListener::EntryAdded(Entry */*entry*/) +{ +} + +// EntryRemoved +void +EntryListener::EntryRemoved(Entry */*entry*/) +{ +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/EntryListener.h b/src/add-ons/kernel/file_systems/ramfs/EntryListener.h new file mode 100644 index 0000000000..5103e51ca1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/EntryListener.h @@ -0,0 +1,25 @@ +// EntryListener.h + +#ifndef ENTRY_LISTENER_H +#define ENTRY_LISTENER_H + +class Entry; + +// listening flags +enum { + ENTRY_LISTEN_ANY_ENTRY = 0x01, + ENTRY_LISTEN_ADDED = 0x02, + ENTRY_LISTEN_REMOVED = 0x04, + ENTRY_LISTEN_ALL = ENTRY_LISTEN_ADDED | ENTRY_LISTEN_REMOVED, +}; + +class EntryListener { +public: + EntryListener(); + virtual ~EntryListener(); + + virtual void EntryAdded(Entry *entry); + virtual void EntryRemoved(Entry *entry); +}; + +#endif // ENTRY_LISTENER_H diff --git a/src/add-ons/kernel/file_systems/ramfs/File.cpp b/src/add-ons/kernel/file_systems/ramfs/File.cpp new file mode 100644 index 0000000000..db26e3780c --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/File.cpp @@ -0,0 +1,75 @@ +// File.cpp + +#include "AllocationInfo.h" +#include "File.h" +#include "SizeIndex.h" +#include "Volume.h" + +// constructor +File::File(Volume *volume) + : Node(volume, NODE_TYPE_FILE), + DataContainer(volume) +{ +} + +// destructor +File::~File() +{ +} + +// ReadAt +status_t +File::ReadAt(off_t offset, void *buffer, size_t size, size_t *bytesRead) +{ + status_t error = DataContainer::ReadAt(offset, buffer, size, bytesRead); + // TODO: update access time? + return error; +} + +// WriteAt +status_t +File::WriteAt(off_t offset, const void *buffer, size_t size, + size_t *bytesWritten) +{ + off_t oldSize = DataContainer::GetSize(); + status_t error = DataContainer::WriteAt(offset, buffer, size, + bytesWritten); + MarkModified(); + // update the size index, if our size has changed + if (oldSize != DataContainer::GetSize()) { + if (SizeIndex *index = GetVolume()->GetSizeIndex()) + index->Changed(this, oldSize); + } + return error; +} + +// SetSize +status_t +File::SetSize(off_t newSize) +{ + status_t error = B_OK; + off_t oldSize = DataContainer::GetSize(); + if (newSize != oldSize) { + error = DataContainer::Resize(newSize); + MarkModified(); + // update the size index + if (SizeIndex *index = GetVolume()->GetSizeIndex()) + index->Changed(this, oldSize); + } + return error; +} + +// GetSize +off_t +File::GetSize() const +{ + return DataContainer::GetSize(); +} + +// GetAllocationInfo +void +File::GetAllocationInfo(AllocationInfo &info) +{ + info.AddFileAllocation(GetSize()); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/File.h b/src/add-ons/kernel/file_systems/ramfs/File.h new file mode 100644 index 0000000000..a16c90be87 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/File.h @@ -0,0 +1,28 @@ +// File.h + +#ifndef FILE_H +#define FILE_H + +#include "DataContainer.h" +#include "Node.h" + +class File : public Node, public DataContainer { +public: + File(Volume *volume); + virtual ~File(); + + Volume *GetVolume() const { return Node::GetVolume(); } + + virtual status_t ReadAt(off_t offset, void *buffer, size_t size, + size_t *bytesRead); + virtual status_t WriteAt(off_t offset, const void *buffer, size_t size, + size_t *bytesWritten); + + virtual status_t SetSize(off_t newSize); + virtual off_t GetSize() const; + + // debugging + virtual void GetAllocationInfo(AllocationInfo &info); +}; + +#endif // FILE_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Index.cpp b/src/add-ons/kernel/file_systems/ramfs/Index.cpp new file mode 100644 index 0000000000..416670b818 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Index.cpp @@ -0,0 +1,192 @@ +// Index.cpp + +#include "Debug.h" +#include "Directory.h" +#include "Entry.h" +#include "Index.h" +#include "IndexImpl.h" + +// Index + +// constructor +Index::Index(Volume *volume, const char *name, uint32 type, + bool fixedKeyLength, size_t keyLength) + : fVolume(volume), + fInitStatus(B_OK), + fName(name), + fType(type), + fKeyLength(keyLength), + fFixedKeyLength(fixedKeyLength) +{ + if (!fVolume) + fInitStatus = B_BAD_VALUE; + else if (!fName.GetString()) + fInitStatus = B_NO_MEMORY; +} + +// destructor +Index::~Index() +{ +} + +// InitCheck +status_t +Index::InitCheck() const +{ + return fInitStatus; +} + +// GetIterator +bool +Index::GetIterator(IndexEntryIterator *iterator) +{ + bool result = false; + if (iterator) { + AbstractIndexEntryIterator *actualIterator = InternalGetIterator(); + if (actualIterator) { + iterator->SetIterator(actualIterator); + result = true; + } + } + return result; +} + +// Find +bool +Index::Find(const uint8 *key, size_t length, IndexEntryIterator *iterator) +{ + bool result = false; + if (key && iterator) { + AbstractIndexEntryIterator *actualIterator + = InternalFind(key, length); + if (actualIterator) { + iterator->SetIterator(actualIterator); + result = true; + } + } + return result; +} + +// Dump +void +Index::Dump() +{ + PRINT(("Index: `%s', type: %lx\n", GetName(), GetType())); + for (IndexEntryIterator it(this); it.GetCurrent(); it.GetNext()) { + Entry *entry = it.GetCurrent(); + PRINT((" entry: `%s', dir: %Ld\n", entry->GetName(), + entry->GetParent()->GetID())); + } +} + + +// IndexEntryIterator + +// constructor +IndexEntryIterator::IndexEntryIterator() + : fIterator(NULL) +{ +} + +// constructor +IndexEntryIterator::IndexEntryIterator(Index *index) + : fIterator(NULL) +{ + if (index) + index->GetIterator(this); +} + +// destructor +IndexEntryIterator::~IndexEntryIterator() +{ + SetIterator(NULL); +} + +// GetCurrent +Entry * +IndexEntryIterator::GetCurrent() +{ + return (fIterator ? fIterator->GetCurrent() : NULL); +} + +// GetCurrent +Entry * +IndexEntryIterator::GetCurrent(uint8 *buffer, size_t *keyLength) +{ + return (fIterator ? fIterator->GetCurrent(buffer, keyLength) : NULL); +} + +// GetPrevious +Entry * +IndexEntryIterator::GetPrevious() +{ + return (fIterator ? fIterator->GetPrevious() : NULL); +} + +// GetNext +Entry * +IndexEntryIterator::GetNext() +{ + return (fIterator ? fIterator->GetNext() : NULL); +} + +// GetNext +Entry * +IndexEntryIterator::GetNext(uint8 *buffer, size_t *keyLength) +{ + Entry *entry = NULL; + if (fIterator && fIterator->GetNext()) + entry = GetCurrent(buffer, keyLength); + return entry; +} + +// Suspend +status_t +IndexEntryIterator::Suspend() +{ + return (fIterator ? fIterator->Suspend() : B_BAD_VALUE); +} + +// Resume +status_t +IndexEntryIterator::Resume() +{ + return (fIterator ? fIterator->Resume() : B_BAD_VALUE); +} + +// SetIterator +void +IndexEntryIterator::SetIterator(AbstractIndexEntryIterator *iterator) +{ + if (fIterator) + delete fIterator; + fIterator = iterator; +} + + +// AbstractIndexEntryIterator + +// constructor +AbstractIndexEntryIterator::AbstractIndexEntryIterator() +{ +} + +// destructor +AbstractIndexEntryIterator::~AbstractIndexEntryIterator() +{ +} + +// Suspend +status_t +AbstractIndexEntryIterator::Suspend() +{ + return B_OK; +} + +// Resume +status_t +AbstractIndexEntryIterator::Resume() +{ + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/Index.h b/src/add-ons/kernel/file_systems/ramfs/Index.h new file mode 100644 index 0000000000..77db01ad2d --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Index.h @@ -0,0 +1,81 @@ +// Index.h + +#ifndef INDEX_H +#define INDEX_H + +#include + +#include "String.h" + +class AbstractIndexEntryIterator; +class Entry; +class IndexEntryIterator; +class Node; +class Volume; + +// Index +class Index { +public: + Index(Volume *volume, const char *name, uint32 type, + bool fixedKeyLength, size_t keyLength = 0); + virtual ~Index(); + + status_t InitCheck() const; + + Volume *GetVolume() const { return fVolume; } + void GetVolume(Volume *volume) { fVolume = volume; } + + const char *GetName() const { return fName.GetString(); } + uint32 GetType() const { return fType; } + bool HasFixedKeyLength() const { return fFixedKeyLength; } + size_t GetKeyLength() const { return fKeyLength; } + + virtual int32 CountEntries() const = 0; + + bool GetIterator(IndexEntryIterator *iterator); + bool Find(const uint8 *key, size_t length, + IndexEntryIterator *iterator); + + // debugging + void Dump(); + +protected: + virtual AbstractIndexEntryIterator *InternalGetIterator() = 0; + virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key, + size_t length) = 0; + +protected: + Volume *fVolume; + status_t fInitStatus; + String fName; + uint32 fType; + size_t fKeyLength; + bool fFixedKeyLength; +}; + +// IndexEntryIterator +class IndexEntryIterator { +public: + IndexEntryIterator(); + IndexEntryIterator(Index *index); + ~IndexEntryIterator(); + + Entry *GetCurrent(); + Entry *GetCurrent(uint8 *buffer, size_t *keyLength); + Entry *GetPrevious(); + Entry *GetNext(); + Entry *GetNext(uint8 *buffer, size_t *keyLength); + + status_t Suspend(); + status_t Resume(); + +private: + void SetIterator(AbstractIndexEntryIterator *iterator); + +private: + friend class Index; + + AbstractIndexEntryIterator *fIterator; +}; + +#endif // INDEX_H diff --git a/src/add-ons/kernel/file_systems/ramfs/IndexDirectory.cpp b/src/add-ons/kernel/file_systems/ramfs/IndexDirectory.cpp new file mode 100644 index 0000000000..6cac79ef20 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/IndexDirectory.cpp @@ -0,0 +1,204 @@ +// IndexDirectory.cpp + +#include + +#include "AttributeIndexImpl.h" +#include "Debug.h" +#include "IndexDirectory.h" +#include "LastModifiedIndex.h" +#include "NameIndex.h" +#include "SizeIndex.h" + +// constructor +IndexDirectory::IndexDirectory(Volume *volume) + : fVolume(volume), + fNameIndex(NULL), + fLastModifiedIndex(NULL), + fSizeIndex(NULL), + fIndices() +{ + fNameIndex = new(nothrow) NameIndex(volume); + fLastModifiedIndex = new(nothrow) LastModifiedIndex(volume); + fSizeIndex = new(nothrow) SizeIndex(volume); + if (fNameIndex && fLastModifiedIndex && fSizeIndex) { + if (!fIndices.AddItem(fNameIndex) + || !fIndices.AddItem(fLastModifiedIndex) + || !fIndices.AddItem(fSizeIndex)) { + fIndices.MakeEmpty(); + delete fNameIndex; + delete fLastModifiedIndex; + delete fSizeIndex; + fNameIndex = NULL; + fLastModifiedIndex = NULL; + fSizeIndex = NULL; + } + } +} + +// destructor +IndexDirectory::~IndexDirectory() +{ + // delete the default indices + if (fNameIndex) { + fIndices.RemoveItem(fNameIndex); + delete fNameIndex; + } + if (fLastModifiedIndex) { + fIndices.RemoveItem(fLastModifiedIndex); + delete fLastModifiedIndex; + } + if (fSizeIndex) { + fIndices.RemoveItem(fSizeIndex); + delete fSizeIndex; + } + // delete the attribute indices + int32 count = fIndices.CountItems(); + for (int i = 0; i < count; i++) + delete fIndices.ItemAt(i); +} + +// InitCheck +status_t +IndexDirectory::InitCheck() const +{ + return (fNameIndex && fLastModifiedIndex && fSizeIndex ? B_OK + : B_NO_MEMORY); +} + +// CreateIndex +status_t +IndexDirectory::CreateIndex(const char *name, uint32 type, + AttributeIndex **_index) +{ + status_t error = (name ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (!FindIndex(name)) { + // create the index + AttributeIndex *index = NULL; + switch (type) { + case B_INT32_TYPE: + index = new(nothrow) AttributeIndexImpl(fVolume, + name, type, sizeof(int32)); + break; + case B_UINT32_TYPE: + index = new(nothrow) AttributeIndexImpl(fVolume, + name, type, sizeof(uint32)); + break; + case B_INT64_TYPE: + index = new(nothrow) AttributeIndexImpl(fVolume, + name, type, sizeof(int64)); + break; + case B_UINT64_TYPE: + index = new(nothrow) AttributeIndexImpl(fVolume, + name, type, sizeof(uint64)); + break; + case B_FLOAT_TYPE: + index = new(nothrow) AttributeIndexImpl(fVolume, + name, type, sizeof(float)); + break; + case B_DOUBLE_TYPE: + index = new(nothrow) AttributeIndexImpl(fVolume, + name, type, sizeof(double)); + break; + case B_STRING_TYPE: + index = new(nothrow) AttributeIndexImpl(fVolume, + name, type, 0); + break; + default: + error = B_BAD_VALUE; + break; + } + if (error == B_OK && !index) + error = B_NO_MEMORY; + // add the index + if (error == B_OK) { + if (fIndices.AddItem(index)) { + if (_index) + *_index = index; + } else { + delete index; + error = B_NO_MEMORY; + } + } + } else + error = B_FILE_EXISTS; + } + return error; +} + +// DeleteIndex +bool +IndexDirectory::DeleteIndex(const char *name, uint32 type) +{ + return DeleteIndex(FindIndex(name, type)); +} + +// DeleteIndex +bool +IndexDirectory::DeleteIndex(Index *index) +{ + bool result = false; + if (index && !IsSpecialIndex(index)) { + int32 i = fIndices.IndexOf(index); + if (i >= 0) { + fIndices.RemoveItem(i); + delete index; + result = true; + } + } + return result; +} + +// FindIndex +Index * +IndexDirectory::FindIndex(const char *name) +{ + if (name) { + int32 count = fIndices.CountItems(); + for (int32 i = 0; i < count; i++) { + Index *index = fIndices.ItemAt(i); + if (!strcmp(index->GetName(), name)) + return index; + } + } + return NULL; +} + +// FindIndex +Index * +IndexDirectory::FindIndex(const char *name, uint32 type) +{ + Index *index = FindIndex(name); + if (index && index->GetType() != type) + index = NULL; + return index; +} + +// FindAttributeIndex +AttributeIndex * +IndexDirectory::FindAttributeIndex(const char *name) +{ + AttributeIndex *attrIndex = NULL; + if (Index *index = FindIndex(name)) + attrIndex = dynamic_cast(index); + return attrIndex; +} + +// FindAttributeIndex +AttributeIndex * +IndexDirectory::FindAttributeIndex(const char *name, uint32 type) +{ + AttributeIndex *attrIndex = NULL; + if (Index *index = FindIndex(name, type)) + attrIndex = dynamic_cast(index); + return attrIndex; +} + +// IsSpecialIndex +bool +IndexDirectory::IsSpecialIndex(Index *index) const +{ + return (index == fNameIndex || index == fLastModifiedIndex + || index == fSizeIndex); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/IndexDirectory.h b/src/add-ons/kernel/file_systems/ramfs/IndexDirectory.h new file mode 100644 index 0000000000..a7c1964975 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/IndexDirectory.h @@ -0,0 +1,48 @@ +// IndexDirectory.h + +#ifndef INDEX_DIRECTORY_H +#define INDEX_DIRECTORY_H + +#include "List.h" + +class AttributeIndex; +class Index; +class LastModifiedIndex; +class NameIndex; +class SizeIndex; +class Volume; + +class IndexDirectory { +public: + IndexDirectory(Volume *volume); + ~IndexDirectory(); + + status_t InitCheck() const; + + status_t CreateIndex(const char *name, uint32 type, + AttributeIndex **index = NULL); + bool DeleteIndex(const char *name, uint32 type); + bool DeleteIndex(Index *index); + + Index *FindIndex(const char *name); + Index *FindIndex(const char *name, uint32 type); + AttributeIndex *FindAttributeIndex(const char *name); + AttributeIndex *FindAttributeIndex(const char *name, uint32 type); + + bool IsSpecialIndex(Index *index) const; + NameIndex *GetNameIndex() const { return fNameIndex; } + LastModifiedIndex *GetLastModifiedIndex() const + { return fLastModifiedIndex; } + SizeIndex *GetSizeIndex() const { return fSizeIndex; } + + Index *IndexAt(int32 index) const { return fIndices.ItemAt(index); } + +private: + Volume *fVolume; + NameIndex *fNameIndex; + LastModifiedIndex *fLastModifiedIndex; + SizeIndex *fSizeIndex; + List fIndices; +}; + +#endif // INDEX_DIRECTORY_H diff --git a/src/add-ons/kernel/file_systems/ramfs/IndexImpl.h b/src/add-ons/kernel/file_systems/ramfs/IndexImpl.h new file mode 100644 index 0000000000..726908ba9e --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/IndexImpl.h @@ -0,0 +1,148 @@ +// IndexImpl.h + +#ifndef INDEX_IMPL_H +#define INDEX_IMPL_H + +#include "Index.h" +#include "Node.h" + +// AbstractIndexEntryIterator +class AbstractIndexEntryIterator { +public: + AbstractIndexEntryIterator(); + virtual ~AbstractIndexEntryIterator(); + + virtual Entry *GetCurrent() = 0; + virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength) = 0; + virtual Entry *GetPrevious() = 0; + virtual Entry *GetNext() = 0; + + virtual status_t Suspend(); + virtual status_t Resume(); +}; + + +// NodeEntryIterator +template +class NodeEntryIterator : public AbstractIndexEntryIterator { +public: + NodeEntryIterator(); + virtual ~NodeEntryIterator(); + + void Unset(); + + virtual Entry *GetCurrent(); + virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength) = 0; + virtual Entry *GetPrevious(); + virtual Entry *GetNext(); + + virtual status_t Suspend(); + virtual status_t Resume(); + + Node *GetCurrentNode() const { return fNode; } + +protected: + NodeIterator fIterator; + Node *fNode; + Entry *fEntry; + bool fInitialized; + bool fIsNext; + bool fSuspended; +}; + +// constructor +template +NodeEntryIterator::NodeEntryIterator() + : AbstractIndexEntryIterator(), + fIterator(), + fNode(NULL), + fEntry(NULL), + fInitialized(false), + fIsNext(false), + fSuspended(false) +{ +} + +// destructor +template +NodeEntryIterator::~NodeEntryIterator() +{ +} + +// Unset +template +void +NodeEntryIterator::Unset() +{ + fNode = NULL; + fEntry = NULL; + fInitialized = false; + fIsNext = false; + fSuspended = false; +} + +// GetCurrent +template +Entry * +NodeEntryIterator::GetCurrent() +{ + return fEntry; +} + +// GetPrevious +template +Entry * +NodeEntryIterator::GetPrevious() +{ + return NULL; // backwards iteration not implemented +} + +// GetNext +template +Entry * +NodeEntryIterator::GetNext() +{ + if (!fInitialized || !fNode || fSuspended) + return NULL; + if (!(fEntry && fIsNext)) { + while (fNode) { + if (fEntry) + fEntry = fNode->GetNextReferrer(fEntry); + while (fNode && !fEntry) { + fNode = NULL; + if (Node **nodeP = fIterator.GetNext()) { + fNode = *nodeP; + fEntry = fNode->GetFirstReferrer(); + } + } + if (fEntry) + break; + } + } + fIsNext = false; + return fEntry; +} + +// Suspend +template +status_t +NodeEntryIterator::Suspend() +{ + status_t error = (fInitialized && !fSuspended ? B_OK : B_BAD_VALUE); + if (error == B_OK) + fSuspended = true; + return error; +} + +// Resume +template +status_t +NodeEntryIterator::Resume() +{ + status_t error = (fInitialized && fSuspended ? B_OK : B_BAD_VALUE); + if (error == B_OK) + fSuspended = false; + return error; +} + +#endif // INDEX_IMPL_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Jamfile b/src/add-ons/kernel/file_systems/ramfs/Jamfile new file mode 100644 index 0000000000..0edcad998d --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Jamfile @@ -0,0 +1,62 @@ +SubDir HAIKU_TOP src tests add-ons kernel file_systems userlandfs r5 src test + ramfs ; + +SetSubDirSupportedPlatforms r5 bone dano ; + +local userlandFSTop = [ FDirName $(HAIKU_TOP) src tests add-ons kernel + file_systems userlandfs r5 ] ; +local userlandFSIncludes = [ FDirName $(userlandFSTop) headers ] ; + +DEFINES += USER=1 ; + +SubDirC++Flags -include + [ FDirName $(userlandFSIncludes) shared Compatibility.h ] ; + +SubDirSysHdrs [ FDirName $(userlandFSIncludes) public ] ; +SubDirHdrs [ FDirName $(userlandFSIncludes) shared ] ; + +if $(OSPLAT) = X86 { +# SubDirC++Flags -include [ FDirName $(UFS_TOP) src kernel_add_on +# kernel-cpp.h ] ; + SubDirC++Flags -include [ FDirName $(SUBDIR) cpp.h ] ; +} + +SEARCH_SOURCE += [ FDirName $(userlandFSTop) src shared ] ; + +Addon ramfs + : # relpath - obsolete + : Debug.cpp + Locker.cpp + String.cpp + + AllocationInfo.cpp + AreaUtils.cpp + Attribute.cpp + AttributeIndex.cpp + AttributeIndexImpl.cpp + AttributeIterator.cpp + BlockAllocator.cpp + BlockAllocatorArea.cpp + BlockAllocatorAreaBucket.cpp + BlockReferenceManager.cpp + DataContainer.cpp + Directory.cpp + Entry.cpp + EntryIterator.cpp + EntryListener.cpp + File.cpp + Index.cpp + IndexDirectory.cpp + kernel_interface.cpp + LastModifiedIndex.cpp + NameIndex.cpp + Node.cpp + NodeListener.cpp + NodeTable.cpp + Query.cpp + SizeIndex.cpp + SymLink.cpp + Volume.cpp + : false # is executable + : UserlandFSServer +; diff --git a/src/add-ons/kernel/file_systems/ramfs/LastModifiedIndex.cpp b/src/add-ons/kernel/file_systems/ramfs/LastModifiedIndex.cpp new file mode 100644 index 0000000000..72152b37c1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/LastModifiedIndex.cpp @@ -0,0 +1,373 @@ +// LastModifiedIndex.cpp + +#include + +#include "Debug.h" +#include "Entry.h" +#include "EntryListener.h" +#include "IndexImpl.h" +#include "LastModifiedIndex.h" +#include "Node.h" +#include "NodeListener.h" +#include "Volume.h" + +// LastModifiedIndexPrimaryKey +class LastModifiedIndexPrimaryKey { +public: + LastModifiedIndexPrimaryKey(Node *node, time_t modified) + : node(node), modified(modified) {} + LastModifiedIndexPrimaryKey(Node *node) + : node(node), modified(node->GetMTime()) {} + LastModifiedIndexPrimaryKey(time_t modified) + : node(NULL), modified(modified) {} + + Node *node; + time_t modified; +}; + +// LastModifiedIndexGetPrimaryKey +class LastModifiedIndexGetPrimaryKey { +public: + inline LastModifiedIndexPrimaryKey operator()(Node *a) + { + return LastModifiedIndexPrimaryKey(a); + } + + inline LastModifiedIndexPrimaryKey operator()(Node *a) const + { + return LastModifiedIndexPrimaryKey(a); + } +}; + +// LastModifiedIndexPrimaryKeyCompare +class LastModifiedIndexPrimaryKeyCompare +{ +public: + inline int operator()(const LastModifiedIndexPrimaryKey &a, + const LastModifiedIndexPrimaryKey &b) const + { + if (a.node != NULL && a.node == b.node) + return 0; + if (a.modified < b.modified) + return -1; + if (a.modified > b.modified) + return 1; + return 0; + } +}; + + +// NodeTree +typedef TwoKeyAVLTree + _NodeTree; +class LastModifiedIndex::NodeTree : public _NodeTree {}; + + +// IteratorList +class LastModifiedIndex::IteratorList : public DLList {}; + + +// Iterator +class LastModifiedIndex::Iterator + : public NodeEntryIterator, + public DLListLinkImpl, public EntryListener, + public NodeListener { +public: + Iterator(); + virtual ~Iterator(); + + virtual Entry *GetCurrent(); + virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength); + + virtual status_t Suspend(); + virtual status_t Resume(); + + bool SetTo(LastModifiedIndex *index, time_t modified, + bool ignoreValue = false); + void Unset(); + + virtual void EntryRemoved(Entry *entry); + virtual void NodeRemoved(Node *node); + +private: + typedef NodeEntryIterator BaseClass; + +private: + LastModifiedIndex *fIndex; +}; + + +// LastModifiedIndex + +// constructor +LastModifiedIndex::LastModifiedIndex(Volume *volume) + : Index(volume, "last_modified", B_INT32_TYPE, true, sizeof(time_t)), + fNodes(new(nothrow) NodeTree), + fIterators(new(nothrow) IteratorList) +{ + if (fInitStatus == B_OK && (!fNodes || !fIterators)) + fInitStatus = B_NO_MEMORY; + if (fInitStatus == B_OK) { + fInitStatus = fVolume->AddNodeListener(this, + NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL); + } +} + +// destructor +LastModifiedIndex::~LastModifiedIndex() +{ + if (fVolume) + fVolume->RemoveNodeListener(this, NULL); + if (fIterators) { + // unset the iterators + for (Iterator *iterator = fIterators->GetFirst(); + iterator; + iterator = fIterators->GetNext(iterator)) { + iterator->SetTo(NULL, 0); + } + delete fIterators; + } + if (fNodes) + delete fNodes; +} + +// CountEntries +int32 +LastModifiedIndex::CountEntries() const +{ + return fNodes->CountItems(); +} + +// Changed +status_t +LastModifiedIndex::Changed(Node *node, time_t oldModified) +{ + status_t error = B_BAD_VALUE; + if (node) { + NodeTree::Iterator it; + Node **foundNode = fNodes->Find(LastModifiedIndexPrimaryKey(node, + oldModified), node, &it); + if (foundNode && *foundNode == node) { + // update the iterators + for (Iterator *iterator = fIterators->GetFirst(); + iterator; + iterator = fIterators->GetNext(iterator)) { + if (iterator->GetCurrentNode() == node) + iterator->NodeRemoved(node); + } + // remove and re-insert the node + fNodes->Remove(it); + error = fNodes->Insert(node); + + // udpate live queries + time_t newModified = node->GetMTime(); + fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(), + (const uint8*)&oldModified, sizeof(oldModified), + (const uint8*)&newModified, sizeof(newModified)); + } + } + return error; +} + +// NodeAdded +void +LastModifiedIndex::NodeAdded(Node *node) +{ + if (node) + fNodes->Insert(node); +} + +// NodeRemoved +void +LastModifiedIndex::NodeRemoved(Node *node) +{ + if (node) + fNodes->Remove(node, node); +} + +// InternalGetIterator +AbstractIndexEntryIterator * +LastModifiedIndex::InternalGetIterator() +{ + Iterator *iterator = new(nothrow) Iterator; + if (iterator) { + if (!iterator->SetTo(this, 0, true)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// InternalFind +AbstractIndexEntryIterator * +LastModifiedIndex::InternalFind(const uint8 *key, size_t length) +{ + if (!key || length != sizeof(time_t)) + return NULL; + Iterator *iterator = new(nothrow) Iterator; + if (iterator) { + if (!iterator->SetTo(this, *(const time_t*)key)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// _AddIterator +void +LastModifiedIndex::_AddIterator(Iterator *iterator) +{ + fIterators->Insert(iterator); +} + +// _RemoveIterator +void +LastModifiedIndex::_RemoveIterator(Iterator *iterator) +{ + fIterators->Remove(iterator); +} + + +// Iterator + +// constructor +LastModifiedIndex::Iterator::Iterator() + : BaseClass(), + fIndex(NULL) +{ +} + +// destructor +LastModifiedIndex::Iterator::~Iterator() +{ + SetTo(NULL, 0); +} + +// GetCurrent +Entry * +LastModifiedIndex::Iterator::GetCurrent() +{ + return BaseClass::GetCurrent(); +} + +// GetCurrent +Entry * +LastModifiedIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength) +{ + Entry *entry = GetCurrent(); + if (entry) { + *(time_t*)buffer = entry->GetNode()->GetMTime(); + *keyLength = sizeof(time_t); + } + return entry; +} + +// Suspend +status_t +LastModifiedIndex::Iterator::Suspend() +{ + status_t error = BaseClass::Suspend(); + if (error == B_OK) { + if (fNode) { + error = fIndex->GetVolume()->AddNodeListener(this, fNode, + NODE_LISTEN_REMOVED); + if (error == B_OK && fEntry) { + error = fIndex->GetVolume()->AddEntryListener(this, fEntry, + ENTRY_LISTEN_REMOVED); + if (error != B_OK) + fIndex->GetVolume()->RemoveNodeListener(this, fNode); + } + if (error != B_OK) + BaseClass::Resume(); + } + } + return error; +} + +// Resume +status_t +LastModifiedIndex::Iterator::Resume() +{ + status_t error = BaseClass::Resume(); + if (error == B_OK) { + if (fEntry) + error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry); + if (fNode) { + if (error == B_OK) + error = fIndex->GetVolume()->RemoveNodeListener(this, fNode); + else + fIndex->GetVolume()->RemoveNodeListener(this, fNode); + } + } + return error; +} + +// SetTo +bool +LastModifiedIndex::Iterator::SetTo(LastModifiedIndex *index, time_t modified, + bool ignoreValue) +{ + Resume(); + Unset(); + // set the new values + fIndex = index; + if (fIndex) + fIndex->_AddIterator(this); + fInitialized = fIndex; + // get the node's first entry + if (fIndex) { + // get the first node + bool found = true; + if (ignoreValue) + fIndex->fNodes->GetIterator(&fIterator); + else + found = fIndex->fNodes->FindFirst(modified, &fIterator); + // get the first entry + if (found) { + if (Node **nodeP = fIterator.GetCurrent()) { + fNode = *nodeP; + fEntry = fNode->GetFirstReferrer(); + if (!fEntry) + BaseClass::GetNext(); + if (!ignoreValue && fNode && fNode->GetMTime() != modified) + Unset(); + } + } + } + return fEntry; +} + +// Unset +void +LastModifiedIndex::Iterator::Unset() +{ + if (fIndex) { + fIndex->_RemoveIterator(this); + fIndex = NULL; + } + BaseClass::Unset(); +} + +// EntryRemoved +void +LastModifiedIndex::Iterator::EntryRemoved(Entry */*entry*/) +{ + Resume(); + fIsNext = BaseClass::GetNext(); + Suspend(); +} + +// NodeRemoved +void +LastModifiedIndex::Iterator::NodeRemoved(Node */*node*/) +{ + Resume(); + fEntry = NULL; + fIsNext = BaseClass::GetNext(); + Suspend(); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/LastModifiedIndex.h b/src/add-ons/kernel/file_systems/ramfs/LastModifiedIndex.h new file mode 100644 index 0000000000..bb5f566587 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/LastModifiedIndex.h @@ -0,0 +1,44 @@ +// LastModifiedIndex.h + +#ifndef LAST_MODIFIED_INDEX_H +#define LAST_MODIFIED_INDEX_H + +#include "Index.h" +#include "NodeListener.h" +#include "TwoKeyAVLTree.h" + +// LastModifiedIndex +class LastModifiedIndex : public Index, private NodeListener { +public: + LastModifiedIndex(Volume *volume); + virtual ~LastModifiedIndex(); + + virtual int32 CountEntries() const; + + virtual status_t Changed(Node *node, time_t oldModified); + +private: + virtual void NodeAdded(Node *node); + virtual void NodeRemoved(Node *node); + +protected: + virtual AbstractIndexEntryIterator *InternalGetIterator(); + virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key, + size_t length); + +private: + class Iterator; + class IteratorList; + class NodeTree; + friend class Iterator; + +private: + void _AddIterator(Iterator *iterator); + void _RemoveIterator(Iterator *iterator); + +private: + NodeTree *fNodes; + IteratorList *fIterators; +}; + +#endif // LAST_MODIFIED_INDEX_H diff --git a/src/add-ons/kernel/file_systems/ramfs/List.h b/src/add-ons/kernel/file_systems/ramfs/List.h new file mode 100644 index 0000000000..21aeb87533 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/List.h @@ -0,0 +1,385 @@ +// List.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef LIST_H +#define LIST_H + +#include +#include +#include + +#include + +template +class DefaultDefaultItemCreator { +public: + static inline ITEM GetItem() { return ITEM(0); } +}; + +/*! + \class List + \brief A generic list implementation. +*/ +template > +class List { +public: + typedef ITEM item_t; + typedef List list_t; + +private: + static item_t sDefaultItem; + static const size_t kDefaultChunkSize = 10; + static const size_t kMaximalChunkSize = 1024 * 1024; + +public: + List(size_t chunkSize = kDefaultChunkSize); + ~List(); + + inline const item_t &GetDefaultItem() const; + inline item_t &GetDefaultItem(); + + bool AddItem(const item_t &item, int32 index); + bool AddItem(const item_t &item); +// bool AddList(list_t *list, int32 index); +// bool AddList(list_t *list); + + bool RemoveItem(const item_t &item); + bool RemoveItem(int32 index); + + bool ReplaceItem(int32 index, const item_t &item); + + bool MoveItem(int32 oldIndex, int32 newIndex); + + void MakeEmpty(); + + int32 CountItems() const; + bool IsEmpty() const; + const item_t &ItemAt(int32 index) const; + item_t &ItemAt(int32 index); + const item_t *Items() const; + int32 IndexOf(const item_t &item) const; + bool HasItem(const item_t &item) const; + + // debugging + int32 GetCapacity() const { return fCapacity; } + +private: + inline static void _MoveItems(item_t* items, int32 offset, int32 count); + bool _Resize(size_t count); + +private: + size_t fCapacity; + size_t fChunkSize; + int32 fItemCount; + item_t *fItems; +}; + +// sDefaultItem +template +List::item_t + List::sDefaultItem( + DEFAULT_ITEM_SUPPLIER::GetItem()); + +// constructor +template +List::List(size_t chunkSize) + : fCapacity(0), + fChunkSize(chunkSize), + fItemCount(0), + fItems(NULL) +{ + if (fChunkSize == 0 || fChunkSize > kMaximalChunkSize) + fChunkSize = kDefaultChunkSize; + _Resize(0); +} + +// destructor +template +List::~List() +{ + MakeEmpty(); + free(fItems); +} + +// GetDefaultItem +template +inline +const List::item_t & +List::GetDefaultItem() const +{ + return sDefaultItem; +} + +// GetDefaultItem +template +inline +List::item_t & +List::GetDefaultItem() +{ + return sDefaultItem; +} + +// _MoveItems +template +inline +void +List::_MoveItems(item_t* items, int32 offset, int32 count) +{ + if (count > 0 && offset != 0) + memmove(items + offset, items, count * sizeof(item_t)); +} + +// AddItem +template +bool +List::AddItem(const item_t &item, int32 index) +{ + bool result = (index >= 0 && index <= fItemCount + && _Resize(fItemCount + 1)); + if (result) { + _MoveItems(fItems + index, 1, fItemCount - index - 1); + new(fItems + index) item_t(item); + } + return result; +} + +// AddItem +template +bool +List::AddItem(const item_t &item) +{ + bool result = true; + if ((int32)fCapacity > fItemCount) { + new(fItems + fItemCount) item_t(item); + fItemCount++; + } else { + if ((result = _Resize(fItemCount + 1))) + new(fItems + (fItemCount - 1)) item_t(item); + } + return result; +} + +// These don't use the copy constructor! +/* +// AddList +template +bool +List::AddList(list_t *list, int32 index) +{ + bool result = (list && index >= 0 && index <= fItemCount); + if (result && list->fItemCount > 0) { + int32 count = list->fItemCount; + result = _Resize(fItemCount + count); + if (result) { + _MoveItems(fItems + index, count, fItemCount - index - count); + memcpy(fItems + index, list->fItems, + list->fItemCount * sizeof(item_t)); + } + } + return result; +} + +// AddList +template +bool +List::AddList(list_t *list) +{ + bool result = (list); + if (result && list->fItemCount > 0) { + int32 index = fItemCount; + int32 count = list->fItemCount; + result = _Resize(fItemCount + count); + if (result) { + memcpy(fItems + index, list->fItems, + list->fItemCount * sizeof(item_t)); + } + } + return result; +} +*/ + +// RemoveItem +template +bool +List::RemoveItem(const item_t &item) +{ + int32 index = IndexOf(item); + bool result = (index >= 0); + if (result) + RemoveItem(index); + return result; +} + +// RemoveItem +template +bool +List::RemoveItem(int32 index) +{ + if (index >= 0 && index < fItemCount) { + fItems[index].~item_t(); + _MoveItems(fItems + index + 1, -1, fItemCount - index - 1); + _Resize(fItemCount - 1); + return true; + } + return false; +} + +// ReplaceItem +template +bool +List::ReplaceItem(int32 index, const item_t &item) +{ + if (index >= 0 && index < fItemCount) { + fItems[index] = item; + return true; + } + return false; +} + +// MoveItem +template +bool +List::MoveItem(int32 oldIndex, int32 newIndex) +{ + if (oldIndex >= 0 && oldIndex < fItemCount + && newIndex >= 0 && newIndex <= fItemCount) { + if (oldIndex < newIndex - 1) { + item_t item = fItems[oldIndex]; + _MoveItems(fItems + oldIndex + 1, -1, newIndex - oldIndex - 1); + fItems[newIndex] = item; + } else if (oldIndex > newIndex) { + item_t item = fItems[oldIndex]; + _MoveItems(fItems + newIndex, 1, oldIndex - newIndex); + fItems[newIndex] = item; + } + return true; + } + return false; +} + +// MakeEmpty +template +void +List::MakeEmpty() +{ + for (int32 i = 0; i < fItemCount; i++) + fItems[i].~item_t(); + _Resize(0); +} + +// CountItems +template +int32 +List::CountItems() const +{ + return fItemCount; +} + +// IsEmpty +template +bool +List::IsEmpty() const +{ + return (fItemCount == 0); +} + +// ItemAt +template +const List::item_t & +List::ItemAt(int32 index) const +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + return sDefaultItem; +} + +// ItemAt +template +List::item_t & +List::ItemAt(int32 index) +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + return sDefaultItem; +} + +// Items +template +const List::item_t * +List::Items() const +{ + return fItems; +} + +// IndexOf +template +int32 +List::IndexOf(const item_t &item) const +{ + for (int32 i = 0; i < fItemCount; i++) { + if (fItems[i] == item) + return i; + } + return -1; +} + +// HasItem +template +bool +List::HasItem(const item_t &item) const +{ + return (IndexOf(item) >= 0); +} + +// _Resize +template +bool +List::_Resize(size_t count) +{ + bool result = true; + // calculate the new capacity + int32 newSize = count; + if (newSize <= 0) + newSize = 1; + newSize = ((newSize - 1) / fChunkSize + 1) * fChunkSize; + // resize if necessary + if ((size_t)newSize != fCapacity) { + item_t* newItems + = (item_t*)realloc(fItems, newSize * sizeof(item_t)); + if (newItems) { + fItems = newItems; + fCapacity = newSize; + } else + result = false; + } + if (result) + fItemCount = count; + return result; +} + +#endif // LIST_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Locking.h b/src/add-ons/kernel/file_systems/ramfs/Locking.h new file mode 100644 index 0000000000..e0253774b6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Locking.h @@ -0,0 +1,14 @@ +// Locking.h + +#ifndef LOCKING_H +#define LOCKING_H + +#include "AutoLocker.h" + +class Volume; + +// instantiations +typedef AutoLocker > VolumeReadLocker; +typedef AutoLocker > VolumeWriteLocker; + +#endif LOCKING_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Misc.h b/src/add-ons/kernel/file_systems/ramfs/Misc.h new file mode 100644 index 0000000000..17540d54c6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Misc.h @@ -0,0 +1,107 @@ +// Misc.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef MISC_H +#define MISC_H + +#include + +#include "String.h" + +// min and max +// We don't want to include otherwise we also get +// and other undesired things. +template +static inline C min(const C &a, const C &b) { return (a < b ? a : b); } +template +static inline C max(const C &a, const C &b) { return (a > b ? a : b); } + +// find last (most significant) set bit +static inline +int +fls(uint32 value) +{ + if (!value) + return -1; + int index = 0; +#define HAND_OPTIMIZED_FLS 1 +#if !HAND_OPTIMIZED_FLS +// This is the algorithm in its pure form. + const uint32 masks[] = { + 0xffff0000, + 0xff00ff00, + 0xf0f0f0f0, + 0xcccccccc, + 0xaaaaaaaa, + }; + int range = 16; + for (int i = 0; i < 5; i++) { + if (value & masks[i]) { + index += range; + value &= masks[i]; + } + range /= 2; + } +#else // HAND_OPTIMIZED_FLS +// This is how the compiler should optimize it for us: Unroll the loop and +// inline the masks. + // 0: 0xffff0000 + if (value & 0xffff0000) { + index += 16; + value &= 0xffff0000; + } + // 1: 0xff00ff00 + if (value & 0xff00ff00) { + index += 8; + value &= 0xff00ff00; + } + // 2: 0xf0f0f0f0 + if (value & 0xf0f0f0f0) { + index += 4; + value &= 0xf0f0f0f0; + } + // 3: 0xcccccccc + if (value & 0xcccccccc) { + index += 2; + value &= 0xcccccccc; + } + // 4: 0xaaaaaaaa + if (value & 0xaaaaaaaa) + index++; +#endif // HAND_OPTIMIZED_FLS + return index; +} + +// node_child_hash +static inline +uint32 +node_child_hash(uint64 id, const char *name) +{ + return uint32(id & 0xffffffff) ^ string_hash(name); +} + +#endif // MISC_H diff --git a/src/add-ons/kernel/file_systems/ramfs/NameIndex.cpp b/src/add-ons/kernel/file_systems/ramfs/NameIndex.cpp new file mode 100644 index 0000000000..5cec5ef519 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/NameIndex.cpp @@ -0,0 +1,348 @@ +// NameIndex.cpp + +#include + +#include "Debug.h" +#include "Entry.h" +#include "IndexImpl.h" +#include "NameIndex.h" +#include "ramfs.h" +#include "Volume.h" + +// NameIndexPrimaryKey +class NameIndexPrimaryKey { +public: + NameIndexPrimaryKey(const Entry *entry, + const char *name = NULL) + : entry(entry), name(name ? name : entry->GetName()) {} + NameIndexPrimaryKey(const char *name) + : entry(NULL), name(name) {} + + const Entry *entry; + const char *name; +}; + +// NameIndexGetPrimaryKey +class NameIndexGetPrimaryKey { +public: + inline NameIndexPrimaryKey operator()(const Entry *a) + { + return NameIndexPrimaryKey(a); + } + + inline NameIndexPrimaryKey operator()(const Entry *a) const + { + return NameIndexPrimaryKey(a); + } +}; + + +// NameIndexPrimaryKeyCompare +class NameIndexPrimaryKeyCompare +{ +public: + inline int operator()(const NameIndexPrimaryKey &a, + const NameIndexPrimaryKey &b) const + { + if (a.entry != NULL && a.entry == b.entry) + return 0; + return strcmp(a.name, b.name); + } +}; + + +// EntryTree + +typedef TwoKeyAVLTree + _EntryTree; + +class NameIndex::EntryTree : public _EntryTree {}; + + +// NameIndexEntryIterator +class NameIndexEntryIterator : public AbstractIndexEntryIterator, + public EntryListener { +public: + NameIndexEntryIterator(); + virtual ~NameIndexEntryIterator(); + + virtual Entry *GetCurrent(); + virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength); + virtual Entry *GetPrevious(); + virtual Entry *GetNext(); + + virtual status_t Suspend(); + virtual status_t Resume(); + + bool SetTo(NameIndex *index, const char *name, bool ignoreValue = false); + + virtual void EntryRemoved(Entry *entry); + +private: + friend class NameIndex; + + typedef AbstractIndexEntryIterator BaseClass; + +private: + NameIndex *fIndex; + NameIndex::EntryTree::Iterator fIterator; + bool fSuspended; + bool fIsNext; +}; + + +// NameIndex + +// constructor +NameIndex::NameIndex(Volume *volume) + : Index(volume, "name", B_STRING_TYPE, false), + fEntries(new(nothrow) EntryTree) +{ + if (fInitStatus == B_OK && !fEntries) + fInitStatus = B_NO_MEMORY; + if (fInitStatus == B_OK) { + fInitStatus = fVolume->AddEntryListener(this, + NULL, ENTRY_LISTEN_ANY_ENTRY | ENTRY_LISTEN_ALL); + } +} + +// destructor +NameIndex::~NameIndex() +{ + if (fVolume) + fVolume->RemoveEntryListener(this, NULL); + if (fEntries) + delete fEntries; + // Actually we would need to maintain a list of iterators and unset the + // still existing iterators here. But since the name index is deleted + // when the volume is unmounted, there shouldn't be any iterators left + // anymore. +} + +// CountEntries +int32 +NameIndex::CountEntries() const +{ + return fEntries->CountItems(); +} + +// Changed +status_t +NameIndex::Changed(Entry *entry, const char *oldName) +{ + status_t error = B_BAD_VALUE; + if (entry && oldName) { + EntryTree::Iterator it; + Entry **foundEntry + = fEntries->Find(NameIndexPrimaryKey(entry, oldName), entry, &it); + if (foundEntry && *foundEntry == entry) { + fEntries->Remove(it); + error = fEntries->Insert(entry); + + // udpate live queries + _UpdateLiveQueries(entry, oldName, entry->GetName()); + } + } + return error; +} + +// EntryAdded +void +NameIndex::EntryAdded(Entry *entry) +{ + if (entry) { + fEntries->Insert(entry); + + // udpate live queries + _UpdateLiveQueries(entry, NULL, entry->GetName()); + } +} + +// EntryRemoved +void +NameIndex::EntryRemoved(Entry *entry) +{ + if (entry) { + fEntries->Remove(entry, entry); + + // udpate live queries + _UpdateLiveQueries(entry, entry->GetName(), NULL); + } +} + +// InternalGetIterator +AbstractIndexEntryIterator * +NameIndex::InternalGetIterator() +{ + NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator; + if (iterator) { + if (!iterator->SetTo(this, NULL, true)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// InternalFind +AbstractIndexEntryIterator * +NameIndex::InternalFind(const uint8 *key, size_t length) +{ + if (!key || length == 0) + return NULL; + + // if the key is not null-terminated, copy it + uint8 clonedKey[kMaxIndexKeyLength]; + if (key[length - 1] != '\0') { + if (length >= kMaxIndexKeyLength) + length = kMaxIndexKeyLength - 1; + + memcpy(clonedKey, key, length); + clonedKey[length] = '\0'; + length++; + key = clonedKey; + } + + NameIndexEntryIterator *iterator = new(nothrow) NameIndexEntryIterator; + if (iterator) { + if (!iterator->SetTo(this, (const char *)key)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// _UpdateLiveQueries +void +NameIndex::_UpdateLiveQueries(Entry* entry, const char* oldName, + const char* newName) +{ + fVolume->UpdateLiveQueries(entry, entry->GetNode(), GetName(), + GetType(), (const uint8*)oldName, (oldName ? strlen(oldName) : 0), + (const uint8*)newName, (newName ? strlen(newName) : 0)); +} + + +// NameIndexEntryIterator + +// constructor +NameIndexEntryIterator::NameIndexEntryIterator() + : AbstractIndexEntryIterator(), + fIndex(NULL), + fIterator(), + fSuspended(false), + fIsNext(false) +{ +} + +// destructor +NameIndexEntryIterator::~NameIndexEntryIterator() +{ + SetTo(NULL, NULL); +} + +// GetCurrent +Entry * +NameIndexEntryIterator::GetCurrent() +{ + return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL); +} + +// GetCurrent +Entry * +NameIndexEntryIterator::GetCurrent(uint8 *buffer, size_t *keyLength) +{ + Entry *entry = GetCurrent(); + if (entry) { + strncpy((char*)buffer, entry->GetName(), kMaxIndexKeyLength); + *keyLength = strlen(entry->GetName()); + } + return entry; +} + +// GetPrevious +Entry * +NameIndexEntryIterator::GetPrevious() +{ + if (fSuspended) + return NULL; + if (!(fIterator.GetCurrent() && fIsNext)) + fIterator.GetPrevious(); + fIsNext = false; + return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL); +} + +// GetNext +Entry * +NameIndexEntryIterator::GetNext() +{ + if (fSuspended) + return NULL; + if (!(fIterator.GetCurrent() && fIsNext)) + fIterator.GetNext(); + fIsNext = false; + return (fIndex && fIterator.GetCurrent() ? *fIterator.GetCurrent() : NULL); +} + +// Suspend +status_t +NameIndexEntryIterator::Suspend() +{ + status_t error = (!fSuspended ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (fIterator.GetCurrent()) { + error = fIndex->GetVolume()->AddEntryListener(this, + *fIterator.GetCurrent(), ENTRY_LISTEN_REMOVED); + } + if (error == B_OK) + fSuspended = true; + } + return error; +} + +// Resume +status_t +NameIndexEntryIterator::Resume() +{ + status_t error = (fSuspended ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (fIterator.GetCurrent()) { + error = fIndex->GetVolume()->RemoveEntryListener(this, + *fIterator.GetCurrent()); + } + if (error == B_OK) + fSuspended = false; + } + return error; +} + +// SetTo +bool +NameIndexEntryIterator::SetTo(NameIndex *index, const char *name, + bool ignoreValue) +{ + Resume(); + fIndex = index; + fSuspended = false; + fIsNext = false; + if (fIndex) { + if (ignoreValue) { + fIndex->fEntries->GetIterator(&fIterator); + return fIterator.GetCurrent(); + } + return fIndex->fEntries->FindFirst(name, &fIterator); + } + return false; +} + +// EntryRemoved +void +NameIndexEntryIterator::EntryRemoved(Entry */*entry*/) +{ + Resume(); + fIsNext = GetNext(); + Suspend(); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/NameIndex.h b/src/add-ons/kernel/file_systems/ramfs/NameIndex.h new file mode 100644 index 0000000000..3503b7c961 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/NameIndex.h @@ -0,0 +1,42 @@ +// NameIndex.h + +#ifndef NAME_INDEX_H +#define NAME_INDEX_H + +#include "EntryListener.h" +#include "Index.h" +#include "TwoKeyAVLTree.h" + +class NameIndexEntryIterator; + +// NameIndex +class NameIndex : public Index, private EntryListener { +public: + NameIndex(Volume *volume); + virtual ~NameIndex(); + + virtual int32 CountEntries() const; + + virtual status_t Changed(Entry *entry, const char *oldName); + +private: + virtual void EntryAdded(Entry *entry); + virtual void EntryRemoved(Entry *entry); + +protected: + virtual AbstractIndexEntryIterator *InternalGetIterator(); + virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key, + size_t length); + +private: + class EntryTree; + friend class NameIndexEntryIterator; + + void _UpdateLiveQueries(Entry* entry, const char* oldName, + const char* newName); + +private: + EntryTree *fEntries; +}; + +#endif // NAME_INDEX_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Node.cpp b/src/add-ons/kernel/file_systems/ramfs/Node.cpp new file mode 100644 index 0000000000..33bd1dcdbd --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Node.cpp @@ -0,0 +1,361 @@ +// Node.cpp + +#include "AllocationInfo.h" +#include "Debug.h" +#include "EntryIterator.h" +#include "LastModifiedIndex.h" +#include "Node.h" +#include "Volume.h" + +// is_user_in_group +inline static +bool +is_user_in_group(gid_t gid) +{ +// Either I miss something, or we don't have getgroups() in the kernel. :-( +/* + gid_t groups[NGROUPS_MAX]; + int groupCount = getgroups(NGROUPS_MAX, groups); + for (int i = 0; i < groupCount; i++) { + if (gid == groups[i]) + return true; + } +*/ + return (gid == getegid()); +} + + +// constructor +Node::Node(Volume *volume, uint8 type) + : fVolume(volume), + fID(fVolume->NextNodeID()), + fRefCount(0), + fMode(0), + fUID(0), + fGID(0), + fATime(0), + fMTime(0), + fCTime(0), + fCrTime(0), + fModified(false), + fIsKnownToVFS(false), + // attribute management + fAttributes(), + // referrers + fReferrers() +{ + // set file type + switch (type) { + case NODE_TYPE_DIRECTORY: + fMode = S_IFDIR; + break; + case NODE_TYPE_FILE: + fMode = S_IFREG; + break; + case NODE_TYPE_SYMLINK: + fMode = S_IFLNK; + break; + } + // set defaults for time + fATime = fMTime = fCTime = fCrTime = time(NULL); +} + +// destructor +Node::~Node() +{ + // delete all attributes + while (Attribute *attribute = fAttributes.GetFirst()) { + status_t error = DeleteAttribute(attribute); + if (error != B_OK) { + FATAL(("Node::~Node(): Failed to delete attribute!\n")); + break; + } + } +} + +// InitCheck +status_t +Node::InitCheck() const +{ + return (fVolume && fID >= 0 ? B_OK : B_NO_INIT); +} + +// AddReference +status_t +Node::AddReference() +{ + if (++fRefCount == 1) { + status_t error = GetVolume()->NewVNode(this); + if (error != B_OK) { + fRefCount--; + return error; + } + + fIsKnownToVFS = true; + } + + return B_OK; +} + +// RemoveReference +void +Node::RemoveReference() +{ + if (--fRefCount == 0) { + GetVolume()->RemoveVNode(this); + fRefCount++; + } +} + +// Link +status_t +Node::Link(Entry *entry) +{ +PRINT(("Node[%Ld]::Link(): %ld ->...\n", fID, fRefCount)); + fReferrers.Insert(entry); + + status_t error = AddReference(); + if (error != B_OK) + fReferrers.Remove(entry); + + return error; +} + +// Unlink +status_t +Node::Unlink(Entry *entry) +{ +PRINT(("Node[%Ld]::Unlink(): %ld ->...\n", fID, fRefCount)); + RemoveReference(); + fReferrers.Remove(entry); + + return B_OK; +} + +// SetMTime +void +Node::SetMTime(time_t mTime) +{ + time_t oldMTime = fMTime; + fATime = fMTime = mTime; + if (oldMTime != fMTime) { + if (LastModifiedIndex *index = fVolume->GetLastModifiedIndex()) + index->Changed(this, oldMTime); + } +} + +// CheckPermissions +status_t +Node::CheckPermissions(int mode) const +{ + int userPermissions = (fMode & S_IRWXU) >> 6; + int groupPermissions = (fMode & S_IRWXG) >> 3; + int otherPermissions = fMode & S_IRWXO; + // get the permissions for this uid/gid + int permissions = 0; + uid_t uid = geteuid(); + // user is root + if (uid == 0) { + // root has always read/write permission, but at least one of the + // X bits must be set for execute permission + permissions = userPermissions | groupPermissions | otherPermissions + | ACCESS_R | ACCESS_W; + // user is node owner + } else if (uid == fUID) + permissions = userPermissions; + // user is in owning group + else if (is_user_in_group(fGID)) + permissions = groupPermissions; + // user is one of the others + else + permissions = otherPermissions; + // do the check + return ((mode & ~permissions) ? B_NOT_ALLOWED : B_OK); +} + +// CreateAttribute +status_t +Node::CreateAttribute(const char *name, Attribute **_attribute) +{ + status_t error = (name && _attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // create attribute + Attribute *attribute = new(nothrow) Attribute(fVolume, NULL, name); + if (attribute) { + error = attribute->InitCheck(); + if (error == B_OK) { + // add attribute to node + error = AddAttribute(attribute); + if (error == B_OK) + *_attribute = attribute; + } + if (error != B_OK) + delete attribute; + } else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// DeleteAttribute +status_t +Node::DeleteAttribute(Attribute *attribute) +{ + status_t error = RemoveAttribute(attribute); + if (error == B_OK) + delete attribute; + return error; +} + +// AddAttribute +status_t +Node::AddAttribute(Attribute *attribute) +{ + status_t error = (attribute && !attribute->GetNode() ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + error = GetVolume()->NodeAttributeAdded(GetID(), attribute); + if (error == B_OK) { + fAttributes.Insert(attribute); + attribute->SetNode(this); + MarkModified(); + } + } + return error; +} + +// RemoveAttribute +status_t +Node::RemoveAttribute(Attribute *attribute) +{ + status_t error = (attribute && attribute->GetNode() == this + ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + // move all iterators pointing to the attribute to the next attribute + if (GetVolume()->IteratorLock()) { + // set the iterators' current entry + Attribute *nextAttr = fAttributes.GetNext(attribute); + DLList *iterators + = attribute->GetAttributeIteratorList(); + for (AttributeIterator *iterator = iterators->GetFirst(); + iterator; + iterator = iterators->GetNext(iterator)) { + iterator->SetCurrent(nextAttr, true); + } + // Move the iterators from one list to the other, or just remove + // them, if there is no next attribute. + if (nextAttr) { + DLList *nextIterators + = nextAttr->GetAttributeIteratorList(); + nextIterators->MoveFrom(iterators); + } else + iterators->RemoveAll(); + GetVolume()->IteratorUnlock(); + } else + error = B_ERROR; + // remove the attribute + if (error == B_OK) { + error = GetVolume()->NodeAttributeRemoved(GetID(), attribute); + if (error == B_OK) { + fAttributes.Remove(attribute); + attribute->SetNode(NULL); + MarkModified(); + } + } + } + return error; +} + +// FindAttribute +status_t +Node::FindAttribute(const char *name, Attribute **_attribute) const +{ + status_t error = (name && _attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { +/* + Attribute *attribute = NULL; + while (GetNextAttribute(&attribute) == B_OK) { + if (!strcmp(attribute->GetName(), name)) { + *_attribute = attribute; + return B_OK; + } + } + error = B_ENTRY_NOT_FOUND; +*/ + error = GetVolume()->FindNodeAttribute(GetID(), name, _attribute); + } + return error; +} + +// GetPreviousAttribute +status_t +Node::GetPreviousAttribute(Attribute **attribute) const +{ + status_t error = (attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (!*attribute) + *attribute = fAttributes.GetLast(); + else if ((*attribute)->GetNode() == this) + *attribute = fAttributes.GetPrevious(*attribute); + else + error = B_BAD_VALUE; + if (error == B_OK && !*attribute) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// GetNextAttribute +status_t +Node::GetNextAttribute(Attribute **attribute) const +{ + status_t error = (attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (!*attribute) + *attribute = fAttributes.GetFirst(); + else if ((*attribute)->GetNode() == this) + *attribute = fAttributes.GetNext(*attribute); + else + error = B_BAD_VALUE; + if (error == B_OK && !*attribute) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// GetFirstReferrer +Entry * +Node::GetFirstReferrer() const +{ + return fReferrers.GetHead(); +} + +// GetLastReferrer +Entry * +Node::GetLastReferrer() const +{ + return fReferrers.GetTail(); +} + +// GetPreviousReferrer +Entry * +Node::GetPreviousReferrer(Entry *entry) const +{ + return (entry ? fReferrers.GetPrevious(entry) : NULL ); +} + +// GetNextReferrer +Entry * +Node::GetNextReferrer(Entry *entry) const +{ + return (entry ? fReferrers.GetNext(entry) : NULL ); +} + +// GetAllocationInfo +void +Node::GetAllocationInfo(AllocationInfo &info) +{ + Attribute *attribute = NULL; + while (GetNextAttribute(&attribute) == B_OK) + attribute->GetAllocationInfo(info); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/Node.h b/src/add-ons/kernel/file_systems/ramfs/Node.h new file mode 100644 index 0000000000..39528a49df --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Node.h @@ -0,0 +1,175 @@ +// Node.h + +#ifndef NODE_H +#define NODE_H + +#include + +#include "Attribute.h" +#include "Entry.h" +#include "fsproto.h" +#include "String.h" + +class AllocationInfo; +class AttributeIterator; +class Directory; +class Volume; + +// node type +enum { + NODE_TYPE_DIRECTORY, + NODE_TYPE_FILE, + NODE_TYPE_SYMLINK, +}; + +// access modes +enum { + ACCESS_R = S_IROTH, + ACCESS_W = S_IWOTH, + ACCESS_X = S_IXOTH, +}; + +class Node : public DLListLinkImpl { +public: + Node(Volume *volume, uint8 type); + virtual ~Node(); + + virtual status_t InitCheck() const; + + inline void SetVolume(Volume *volume) { fVolume = volume; } + inline Volume *GetVolume() const { return fVolume; } + + inline vnode_id GetID() const { return fID; } + + status_t AddReference(); + void RemoveReference(); + int32 GetRefCount() { return fRefCount; } + + virtual status_t Link(Entry *entry); + virtual status_t Unlink(Entry *entry); + + inline bool IsDirectory() const { return S_ISDIR(fMode); } + inline bool IsFile() const { return S_ISREG(fMode); } + inline bool IsSymLink() const { return S_ISLNK(fMode); } + + virtual status_t SetSize(off_t newSize) = 0; + virtual off_t GetSize() const = 0; + + // stat data + + inline void SetMode(mode_t mode) + { fMode = fMode & ~S_IUMSK | mode & S_IUMSK; } + inline mode_t GetMode() const { return fMode; } + + inline void SetUID(uid_t uid) { fUID = uid; } + inline uid_t GetUID() const { return fUID; } + + inline void SetGID(uid_t gid) { fGID = gid; } + inline uid_t GetGID() const { return fGID; } + + inline void SetATime(time_t aTime) { fATime = aTime; } + inline time_t GetATime() const { return fATime; } + + void SetMTime(time_t mTime); + inline time_t GetMTime() const { return fMTime; } + + inline void SetCTime(time_t cTime) { fCTime = cTime; } + inline time_t GetCTime() const { return fCTime; } + + inline void SetCrTime(time_t crTime) { fCrTime = crTime; } + inline time_t GetCrTime() const { return fCrTime; } + + inline void MarkModified() { fModified = true; } + inline void MarkUnmodified(); + inline void SetModified(bool modified) { fModified = modified; } + inline bool IsModified() const { return fModified; } + + status_t CheckPermissions(int mode) const; + + bool IsKnownToVFS() const { return fIsKnownToVFS; } + + // attributes + status_t CreateAttribute(const char *name, Attribute **attribute); + status_t DeleteAttribute(Attribute *attribute); + status_t AddAttribute(Attribute *attribute); + status_t RemoveAttribute(Attribute *attribute); + + status_t FindAttribute(const char *name, Attribute **attribute) const; + + status_t GetPreviousAttribute(Attribute **attribute) const; + status_t GetNextAttribute(Attribute **attribute) const; + + Entry *GetFirstReferrer() const; + Entry *GetLastReferrer() const; + Entry *GetPreviousReferrer(Entry *entry) const; + Entry *GetNextReferrer(Entry *entry) const; + + // debugging + virtual void GetAllocationInfo(AllocationInfo &info); + +private: + Volume *fVolume; + vnode_id fID; + int32 fRefCount; + mode_t fMode; + uid_t fUID; + uid_t fGID; + time_t fATime; + time_t fMTime; + time_t fCTime; + time_t fCrTime; + bool fModified; + bool fIsKnownToVFS; + + // attribute management + DLList fAttributes; + +protected: + // entries referring to this node + DLList fReferrers; +}; + +// MarkUnmodified +inline +void +Node::MarkUnmodified() +{ + if (fModified) { + fCTime = time(NULL); + SetMTime(fCTime); + fModified = false; + } +} + +// open_mode_to_access +inline static +int +open_mode_to_access(int openMode) +{ + switch (openMode & O_RWMASK) { + case O_RDONLY: + return ACCESS_R; + case O_WRONLY: + return ACCESS_W; + case O_RDWR: + return ACCESS_R | ACCESS_W; + } + return 0; +} + + +// NodeMTimeUpdater +class NodeMTimeUpdater { +public: + NodeMTimeUpdater(Node *node) : fNode(node) {} + ~NodeMTimeUpdater() + { + if (fNode && fNode->IsModified()) + fNode->MarkUnmodified(); + } + +private: + Node *fNode; +}; + +#endif // NODE_H diff --git a/src/add-ons/kernel/file_systems/ramfs/NodeChildTable.h b/src/add-ons/kernel/file_systems/ramfs/NodeChildTable.h new file mode 100644 index 0000000000..e129948431 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/NodeChildTable.h @@ -0,0 +1,245 @@ +// NodeChildTable.h + +#ifndef NODE_CHILD_TABLE_H +#define NODE_CHILD_TABLE_H + +#include "AllocationInfo.h" +#include "Debug.h" +#include "Misc.h" +#include "Node.h" +#include "OpenHashTable.h" + +// NodeChildHashElement +template +class NodeChildHashElement : public OpenHashElement { +private: + typedef NodeChildHashElement Element; +public: + + NodeChildHashElement() : OpenHashElement(), fID(-1), fChild(NULL) + { + fNext = -1; + } + + static inline uint32 HashFor(vnode_id id, const char *name) + { + return node_child_hash(id, name); + } + + static inline uint32 HashFor(ParentNode *parent, NodeChild *child) + { + return node_child_hash(parent->GetID(), child->GetName()); + } + + inline uint32 Hash() const + { + return HashFor(fID, fChild->GetName()); + } + + inline bool Equals(vnode_id id, const char *name) + { + return (fID == id && !strcmp(fChild->GetName(), name)); + } + + inline bool operator==(const OpenHashElement &_element) const + { + const Element &element = static_cast(_element); + return Equals(element.fID, element.fChild->GetName()); + } + + inline void Adopt(Element &element) + { + fID = element.fID; + fChild = element.fChild; + } + + vnode_id fID; + NodeChild *fChild; +}; + +// NodeChildTable +template +class NodeChildTable { +public: + NodeChildTable(); + ~NodeChildTable(); + + status_t InitCheck() const; + + status_t AddNodeChild(ParentNode *node, NodeChild *child); + status_t AddNodeChild(vnode_id, NodeChild *child); + status_t RemoveNodeChild(ParentNode *node, NodeChild *child); + status_t RemoveNodeChild(vnode_id id, NodeChild *child); + status_t RemoveNodeChild(vnode_id id, const char *name); + NodeChild *GetNodeChild(vnode_id id, const char *name); + +protected: + typedef NodeChildHashElement Element; + +private: + Element *_FindElement(vnode_id id, const char *name) const; + +protected: + OpenHashElementArray fElementArray; + OpenHashTable > fTable; +}; + +// define convenient instantiation types + +// DirectoryEntryTable +class DirectoryEntryTable : public NodeChildTable { +public: + DirectoryEntryTable() {} + ~DirectoryEntryTable() {} + + void GetAllocationInfo(AllocationInfo &info) + { + info.AddDirectoryEntryTableAllocation(fTable.ArraySize(), + fTable.VectorSize(), + sizeof(Element), + fTable.CountElements()); + } +}; + +// NodeAttributeTable +class NodeAttributeTable : public NodeChildTable { +public: + NodeAttributeTable() {} + ~NodeAttributeTable() {} + + void GetAllocationInfo(AllocationInfo &info) + { + info.AddNodeAttributeTableAllocation(fTable.ArraySize(), + fTable.VectorSize(), + sizeof(Element), + fTable.CountElements()); + } +}; + + +// NodeChildTable implementation + +// constructor +template +NodeChildTable::NodeChildTable() + : fElementArray(1000), + fTable(1000, &fElementArray) +{ +} + +// destructor +template +NodeChildTable::~NodeChildTable() +{ +} + +// InitCheck +template +status_t +NodeChildTable::InitCheck() const +{ + RETURN_ERROR(fTable.InitCheck() && fElementArray.InitCheck() + ? B_OK : B_NO_MEMORY); +} + +// AddNodeChild +template +status_t +NodeChildTable::AddNodeChild(ParentNode *node, + NodeChild *child) +{ + status_t error = (node && child ? B_OK : B_BAD_VALUE); + if (error == B_OK) + error = AddNodeChild(node->GetID(), child); + return error; +} + +// AddNodeChild +template +status_t +NodeChildTable::AddNodeChild(vnode_id id, + NodeChild *child) +{ + status_t error = (child ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + Element *element = fTable.Add(Element::HashFor(id, child->GetName())); + if (element) { + element->fID = id; + element->fChild = child; + } else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// RemoveNodeChild +template +status_t +NodeChildTable::RemoveNodeChild(ParentNode *node, + NodeChild *child) +{ + status_t error = (node && child ? B_OK : B_BAD_VALUE); + if (error == B_OK) + error = RemoveNodeChild(node->GetID(), child->GetName()); + return error; +} + +// RemoveNodeChild +template +status_t +NodeChildTable::RemoveNodeChild(vnode_id id, + NodeChild *child) +{ + status_t error = (child ? B_OK : B_BAD_VALUE); + if (error == B_OK) + error = RemoveNodeChild(id, child->GetName()); + return error; +} + +// RemoveNodeChild +template +status_t +NodeChildTable::RemoveNodeChild(vnode_id id, + const char *name) +{ + status_t error = B_OK; + if (Element *element = _FindElement(id, name)) + fTable.Remove(element); + else + error = B_ERROR; + return error; +} + +// GetNodeChild +template +NodeChild * +NodeChildTable::GetNodeChild(vnode_id id, + const char *name) +{ + NodeChild *child = NULL; + if (Element *element = _FindElement(id, name)) + child = element->fChild; + return child; +} + +// _FindElement +template +NodeChildTable::Element * +NodeChildTable::_FindElement(vnode_id id, + const char *name) const +{ + Element *element = fTable.FindFirst(Element::HashFor(id, name)); + while (element && !element->Equals(id, name)) { + if (element->fNext >= 0) + element = fTable.ElementAt(element->fNext); + else + element = NULL; + } + return element; +} + + +// undefine the PRINT from +//#undef PRINT + +#endif // NODE_CHILD_TABLE_H diff --git a/src/add-ons/kernel/file_systems/ramfs/NodeListener.cpp b/src/add-ons/kernel/file_systems/ramfs/NodeListener.cpp new file mode 100644 index 0000000000..e81b70c392 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/NodeListener.cpp @@ -0,0 +1,26 @@ +// NodeListener.cpp + +#include "NodeListener.h" + +// constructor +NodeListener::NodeListener() +{ +} + +// destructor +NodeListener::~NodeListener() +{ +} + +// NodeAdded +void +NodeListener::NodeAdded(Node */*node*/) +{ +} + +// NodeRemoved +void +NodeListener::NodeRemoved(Node */*node*/) +{ +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/NodeListener.h b/src/add-ons/kernel/file_systems/ramfs/NodeListener.h new file mode 100644 index 0000000000..af30afcbb8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/NodeListener.h @@ -0,0 +1,25 @@ +// NodeListener.h + +#ifndef NODE_LISTENER_H +#define NODE_LISTENER_H + +class Node; + +// listening flags +enum { + NODE_LISTEN_ANY_NODE = 0x01, + NODE_LISTEN_ADDED = 0x02, + NODE_LISTEN_REMOVED = 0x04, + NODE_LISTEN_ALL = NODE_LISTEN_ADDED | NODE_LISTEN_REMOVED, +}; + +class NodeListener { +public: + NodeListener(); + virtual ~NodeListener(); + + virtual void NodeAdded(Node *node); + virtual void NodeRemoved(Node *node); +}; + +#endif // NODE_LISTENER_H diff --git a/src/add-ons/kernel/file_systems/ramfs/NodeTable.cpp b/src/add-ons/kernel/file_systems/ramfs/NodeTable.cpp new file mode 100644 index 0000000000..479fbb2644 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/NodeTable.cpp @@ -0,0 +1,97 @@ +// NodeTable.cpp + +#include "Debug.h" +#include "NodeTable.h" + +// constructor +NodeTable::NodeTable() + : fElementArray(1000), + fNodes(1000, &fElementArray) +{ +} + +// destructor +NodeTable::~NodeTable() +{ +} + +// InitCheck +status_t +NodeTable::InitCheck() const +{ + RETURN_ERROR(fNodes.InitCheck() && fElementArray.InitCheck() + ? B_OK : B_NO_MEMORY); +} + +// AddNode +status_t +NodeTable::AddNode(Node *node) +{ + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + NodeHashElement *element + = fNodes.Add(NodeHashElement::HashForID(node)); + if (element) + element->fNode = node; + else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// RemoveNode +status_t +NodeTable::RemoveNode(Node *node) +{ + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) + error = RemoveNode(node->GetID()); + return error; +} + +// RemoveNode +status_t +NodeTable::RemoveNode(vnode_id id) +{ + status_t error = B_OK; + if (NodeHashElement *element = _FindElement(id)) + fNodes.Remove(element); + else + error = B_ERROR; + return error; +} + +// GetNode +Node * +NodeTable::GetNode(vnode_id id) +{ + Node *node = NULL; + if (NodeHashElement *element = _FindElement(id)) + node = element->fNode; + return node; +} + +// GetAllocationInfo +void +NodeTable::GetAllocationInfo(AllocationInfo &info) +{ + info.AddNodeTableAllocation(fNodes.ArraySize(), fNodes.VectorSize(), + sizeof(NodeHashElement), + fNodes.CountElements()); +} + +// _FindElement +NodeHashElement * +NodeTable::_FindElement(vnode_id id) const +{ + NodeHashElement *element + = fNodes.FindFirst(NodeHashElement::HashForID(id)); + while (element && element->fNode->GetID() != id) { + if (element->fNext >= 0) + element = fNodes.ElementAt(element->fNext); + else + element = NULL; + } + return element; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/NodeTable.h b/src/add-ons/kernel/file_systems/ramfs/NodeTable.h new file mode 100644 index 0000000000..381a14b118 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/NodeTable.h @@ -0,0 +1,74 @@ +// NodeTable.h + +#ifndef NODE_TABLE_H +#define NODE_TABLE_H + +#include "AllocationInfo.h" +#include "Node.h" +#include "OpenHashTable.h" + +// NodeHashElement +class NodeHashElement : public OpenHashElement { +public: + NodeHashElement() : OpenHashElement(), fNode(NULL) + { + fNext = -1; + } + + static inline uint32 HashForID(vnode_id id) + { + return uint32(id & 0xffffffff); + } + + static inline uint32 HashForID(Node *node) + { + return HashForID(node->GetID()); + } + + inline uint32 Hash() const + { + return HashForID(fNode); + } + + inline bool operator==(const OpenHashElement &element) const + { + return (static_cast(element).fNode == fNode); + } + + inline void Adopt(NodeHashElement &element) + { + fNode = element.fNode; + } + + Node *fNode; +}; + +// NodeTable +class NodeTable { +public: + NodeTable(); + ~NodeTable(); + + status_t InitCheck() const; + + status_t AddNode(Node *node); + status_t RemoveNode(Node *node); + status_t RemoveNode(vnode_id id); + Node *GetNode(vnode_id id); + + // debugging + void GetAllocationInfo(AllocationInfo &info); + +private: + NodeHashElement *_FindElement(vnode_id id) const; + +private: + OpenHashElementArray fElementArray; + OpenHashTable > + fNodes; +}; + +// undefine the PRINT from +//#undef PRINT + +#endif // NODE_TABLE_H diff --git a/src/add-ons/kernel/file_systems/ramfs/OpenHashTable.h b/src/add-ons/kernel/file_systems/ramfs/OpenHashTable.h new file mode 100644 index 0000000000..b7740bf227 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/OpenHashTable.h @@ -0,0 +1,496 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + +// bonefish: +// * removed need for exceptions +// * fixed warnings +// * implemented rehashing +// * hash array and element vector use areas for allocations +// TODO: +// * shrinking of element vectors + +// Hash table with open addresssing + +#ifndef __OPEN_HASH_TABLE__ +#define __OPEN_HASH_TABLE__ + +#include +#include + +#include "AreaUtils.h" +#include "Misc.h" + +// don't include +#define ASSERT(E) (void)0 +#define TRESPASS() (void)0 + +//namespace BPrivate { + +template +class ElementVector { + // element vector for OpenHashTable needs to implement this + // interface +public: + Element &At(int32 index); + Element *Add(); + int32 IndexOf(const Element &) const; + void Remove(int32 index); +}; + +class OpenHashElement { +public: + uint32 Hash() const; + bool operator==(const OpenHashElement &) const; + void Adopt(OpenHashElement &); + // low overhead copy, original element is in undefined state + // after call (calls Adopt on BString members, etc.) + int32 fNext; +}; + +const uint32 kPrimes [] = { + 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, + 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, + 134217689, 268435399, 536870909, 1073741789, 2147483647, 0 +}; + +template > +class OpenHashTable { +public: + OpenHashTable(int32 minSize, ElementVec *elementVector = 0, + float maxLoadFactor = 0.8); + // it is up to the subclass of OpenHashTable to supply + // elementVector + ~OpenHashTable(); + + bool InitCheck() const; + + void SetElementVector(ElementVec *elementVector); + + Element *FindFirst(uint32 elementHash) const; + Element *Add(uint32 elementHash); + + void Remove(Element *); + + // when calling Add, any outstanding element pointer may become + // invalid; to deal with this, get the element index and restore + // it after the add + int32 ElementIndex(const Element *) const; + Element *ElementAt(int32 index) const; + + int32 ArraySize() const; + int32 VectorSize() const; + int32 CountElements() const; + +protected: + static int32 OptimalSize(int32 minSize); + +private: + bool _RehashIfNeeded(); + bool _Rehash(); + + int32 fArraySize; + int32 fInitialSize; + int32 fElementCount; + int32 *fHashArray; + ElementVec *fElementVector; + float fMaxLoadFactor; +}; + +template +class OpenHashElementArray : public ElementVector { + // this is a straightforward implementation of an element vector + // deleting is handled by linking deleted elements into a free list + // the vector never shrinks +public: + OpenHashElementArray(int32 initialSize); + ~OpenHashElementArray(); + + bool InitCheck() const; + + Element &At(int32 index); + const Element &At(int32 index) const; + Element *Add(const Element &); + Element *Add(); + void Remove(int32 index); + int32 IndexOf(const Element &) const; + int32 Size() const; + +private: + Element *fData; + int32 fSize; + int32 fNextFree; + int32 fNextDeleted; +}; + + +//----------------------------------- + +template +OpenHashTable::OpenHashTable(int32 minSize, + ElementVec *elementVector, float maxLoadFactor) + : fArraySize(OptimalSize(minSize)), + fInitialSize(fArraySize), + fElementCount(0), + fElementVector(elementVector), + fMaxLoadFactor(maxLoadFactor) +{ + // sanity check the maximal load factor + if (fMaxLoadFactor < 0.5) + fMaxLoadFactor = 0.5; + // allocate and init the array + fHashArray = (int32*)AreaUtils::calloc(fArraySize, sizeof(int32)); + if (fHashArray) { + for (int32 index = 0; index < fArraySize; index++) + fHashArray[index] = -1; + } +} + +template +OpenHashTable::~OpenHashTable() +{ + AreaUtils::free(fHashArray); +} + +template +bool +OpenHashTable::InitCheck() const +{ + return (fHashArray && fElementVector); +} + +template +int32 +OpenHashTable::OptimalSize(int32 minSize) +{ + for (int32 index = 0; ; index++) + if (!kPrimes[index] || kPrimes[index] >= (uint32)minSize) + return (int32)kPrimes[index]; + + return 0; +} + +template +Element * +OpenHashTable::FindFirst(uint32 hash) const +{ + ASSERT(fElementVector); + hash %= fArraySize; + if (fHashArray[hash] < 0) + return 0; + + return &fElementVector->At(fHashArray[hash]); +} + +template +int32 +OpenHashTable::ElementIndex(const Element *element) const +{ + return fElementVector->IndexOf(*element); +} + +template +Element * +OpenHashTable::ElementAt(int32 index) const +{ + return &fElementVector->At(index); +} + +template +int32 +OpenHashTable::ArraySize() const +{ + return fArraySize; +} + +template +int32 +OpenHashTable::VectorSize() const +{ + return fElementVector->Size(); +} + +template +int32 +OpenHashTable::CountElements() const +{ + return fElementCount; +} + + +template +Element * +OpenHashTable::Add(uint32 hash) +{ + ASSERT(fElementVector); + _RehashIfNeeded(); + hash %= fArraySize; + Element *result = fElementVector->Add(); + if (result) { + result->fNext = fHashArray[hash]; + fHashArray[hash] = fElementVector->IndexOf(*result); + fElementCount++; + } + return result; +} + +template +void +OpenHashTable::Remove(Element *element) +{ + _RehashIfNeeded(); + uint32 hash = element->Hash() % fArraySize; + int32 next = fHashArray[hash]; + ASSERT(next >= 0); + + if (&fElementVector->At(next) == element) { + fHashArray[hash] = element->fNext; + fElementVector->Remove(next); + fElementCount--; + return; + } + + for (int32 index = next; index >= 0; ) { + // look for an existing match in table + next = fElementVector->At(index).fNext; + if (next < 0) { + TRESPASS(); + return; + } + + if (&fElementVector->At(next) == element) { + fElementVector->At(index).fNext = element->fNext; + fElementVector->Remove(next); + fElementCount--; + return; + } + index = next; + } +} + +template +void +OpenHashTable::SetElementVector(ElementVec *elementVector) +{ + fElementVector = elementVector; +} + +// _RehashIfNeeded +template +bool +OpenHashTable::_RehashIfNeeded() +{ + // The load factor range [fMaxLoadFactor / 3, fMaxLoadFactor] is fine, + // I think. After rehashing the load factor will be about + // fMaxLoadFactor * 2 / 3, respectively fMaxLoadFactor / 2. + float loadFactor = (float)fElementCount / (float)fArraySize; + if (loadFactor > fMaxLoadFactor + || (fArraySize > fInitialSize && loadFactor < fMaxLoadFactor / 3)) { + return _Rehash(); + } + return true; +} + +// _Rehash +template +bool +OpenHashTable::_Rehash() +{ + bool result = true; + int32 newSize = max(fInitialSize, + int32(fElementCount * 1.73 * fMaxLoadFactor)); + newSize = OptimalSize(newSize); + if (newSize != fArraySize) { +PRINT(("_Rehash(): %lu -> %lu (currently %lu entries)\n", fArraySize, newSize, +fElementCount)); + // allocate a new array + int32 *newHashArray + = (int32*)AreaUtils::calloc(newSize, sizeof(int32)); + if (newHashArray) { + // init the new hash array + for (int32 index = 0; index < newSize; index++) + newHashArray[index] = -1; + // iterate through all elements and put them into the new + // hash array + for (int i = 0; i < fArraySize; i++) { + int32 index = fHashArray[i]; + while (index >= 0) { + // insert the element in the new array + Element &element = fElementVector->At(index); + int32 next = element.fNext; + uint32 hash = (element.Hash() % newSize); + element.fNext = newHashArray[hash]; + newHashArray[hash] = index; + // next element in old list + index = next; + } + } + // delete the old array and set the new one + AreaUtils::free(fHashArray); + fHashArray = newHashArray; + fArraySize = newSize; + } else + result = false; + } + return result; +} + + +template +OpenHashElementArray::OpenHashElementArray(int32 initialSize) + : fSize(initialSize), + fNextFree(0), + fNextDeleted(-1) +{ + fData = (Element*)AreaUtils::calloc((size_t)initialSize, sizeof(Element)); +} + +template +OpenHashElementArray::~OpenHashElementArray() +{ + AreaUtils::free(fData); +} + +template +bool +OpenHashElementArray::InitCheck() const +{ + return fData; +} + +template +Element & +OpenHashElementArray::At(int32 index) +{ + ASSERT(index < fSize); + return fData[index]; +} + +template +const Element & +OpenHashElementArray::At(int32 index) const +{ + ASSERT(index < fSize); + return fData[index]; +} + +template +int32 +OpenHashElementArray::IndexOf(const Element &element) const +{ + int32 result = &element - fData; + if (result < 0 || result > fSize) + return -1; + + return result; +} + +template +int32 +OpenHashElementArray::Size() const +{ + return fSize; +} + + +template +Element * +OpenHashElementArray::Add(const Element &newElement) +{ + Element *element = Add(); + if (element) + element.Adopt(newElement); + return element; +} + +#if DEBUG +const int32 kGrowChunk = 10; +#else +const int32 kGrowChunk = 1024; +#endif + +template +Element * +OpenHashElementArray::Add() +{ + int32 index = fNextFree; + if (fNextDeleted >= 0) { + index = fNextDeleted; + fNextDeleted = At(index).fNext; + } else if (fNextFree >= fSize - 1) { + int32 newSize = fSize + kGrowChunk; +/* + Element *newData = (Element *)calloc((size_t)newSize , sizeof(Element)); + if (!newData) + return NULL; + memcpy(newData, fData, fSize * sizeof(Element)); + free(fData); +*/ + Element *newData = (Element*)AreaUtils::realloc(fData, + (size_t)newSize * sizeof(Element)); + if (!newData) + return NULL; + + fData = newData; + fSize = newSize; + index = fNextFree; + fNextFree++; + } else + fNextFree++; + + new (&At(index)) Element; + // call placement new to initialize the element properly + ASSERT(At(index).fNext == -1); + + return &At(index); +} + +template +void +OpenHashElementArray::Remove(int32 index) +{ + // delete by chaining empty elements in a single linked + // list, reusing the next field + ASSERT(index < fSize); + At(index).~Element(); + // call the destructor explicitly to destroy the element + // properly + At(index).fNext = fNextDeleted; + fNextDeleted = index; +} + +//} // namespace BPrivate + +//using namespace BPrivate; + +#endif diff --git a/src/add-ons/kernel/file_systems/ramfs/Query.cpp b/src/add-ons/kernel/file_systems/ramfs/Query.cpp new file mode 100644 index 0000000000..0acde066c6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Query.cpp @@ -0,0 +1,1760 @@ +/* Query - query parsing and evaluation + * + * The pattern matching is roughly based on code originally written + * by J. Kercheval, and on code written by Kenneth Almquist, though + * it shares no code. + * + * Copyright 2001-2006, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ + +// Adjusted by Ingo Weinhold for usage in RAM FS. + + +#include "Query.h" +#include "Debug.h" +#include "Directory.h" +#include "Entry.h" +#include "Misc.h" +#include "Node.h" +#include "Volume.h" +#include "Index.h" + +#include +#include +#include +#include + +#include +#include +#include + + +// IndexWrapper + +// constructor +IndexWrapper::IndexWrapper(Volume *volume) + : fVolume(volume), + fIndex(NULL) +{ +} + +// SetTo +status_t +IndexWrapper::SetTo(const char *name) +{ + fIndex = NULL; + if (fVolume) + fIndex = fVolume->FindIndex(name); + return (fIndex ? B_OK : B_ENTRY_NOT_FOUND); +} + +// Unset +void +IndexWrapper::Unset() +{ + fIndex = NULL; +} + +// Type +uint32 +IndexWrapper::Type() const +{ + return (fIndex ? fIndex->GetType() : 0); +} + +// GetSize +off_t +IndexWrapper::GetSize() const +{ + // Compute a fake "index size" based on the number of entries + // (1024 + 16 * entry count), so we don't need to adjust the code using it. + return 1024LL + (fIndex ? fIndex->CountEntries() : 0) * 16LL; +} + +// KeySize +int32 +IndexWrapper::KeySize() const +{ + return (fIndex ? fIndex->GetKeyLength() : 0); +} + + +// IndexIterator + +// constructor +IndexIterator::IndexIterator(IndexWrapper *indexWrapper) + : fIndexWrapper(indexWrapper), + fIterator(), + fInitialized(false) +{ +} + +// Find +status_t +IndexIterator::Find(const uint8 *const key, size_t keyLength) +{ + status_t error = B_ENTRY_NOT_FOUND; + if (fIndexWrapper && fIndexWrapper->fIndex) { + // TODO: We actually don't want an exact Find() here, but rather a + // FindClose(). + fInitialized = fIndexWrapper->fIndex->Find(key, keyLength, &fIterator); + if (fInitialized) + error = B_OK; + } + return error; +} + +// GetNextEntry +status_t +IndexIterator::GetNextEntry(uint8 *buffer, uint16 *_keyLength, + size_t /*bufferSize*/, Entry **_entry) +{ + status_t error = B_ENTRY_NOT_FOUND; + if (fIndexWrapper && fIndexWrapper->fIndex) { + // init iterator, if not done yet + if (!fInitialized) { + fIndexWrapper->fIndex->GetIterator(&fIterator); + fInitialized = true; + } + + // get key + size_t keyLength; + if (Entry *entry = fIterator.GetCurrent(buffer, &keyLength)) { + *_keyLength = keyLength; + *_entry = entry; + error = B_OK; + } + + // get next entry + fIterator.GetNext(); + } + return error; +} + + +// compare_integral +template +static inline +int +compare_integral(const Key &a, const Key &b) +{ + if (a < b) + return -1; + else if (a > b) + return 1; + return 0; +} + +// compare_keys +static +int +compare_keys(const uint8 *key1, size_t length1, const uint8 *key2, + size_t length2, uint32 type) +{ + switch (type) { + case B_INT32_TYPE: + return compare_integral(*(int32*)key1, *(int32*)key2); + case B_UINT32_TYPE: + return compare_integral(*(uint32*)key1, *(uint32*)key2); + case B_INT64_TYPE: + return compare_integral(*(int64*)key1, *(int64*)key2); + case B_UINT64_TYPE: + return compare_integral(*(uint64*)key1, *(uint64*)key2); + case B_FLOAT_TYPE: + return compare_integral(*(float*)key1, *(float*)key2); + case B_DOUBLE_TYPE: + return compare_integral(*(double*)key1, *(double*)key2); + case B_STRING_TYPE: + { + int result = strncmp((const char*)key1, (const char*)key2, + min(length1, length2)); + if (result == 0) { + result = compare_integral(strnlen((const char*)key1, length1), + strnlen((const char*)key2, length2)); + } + return result; + } + } + return -1; +} + +// compareKeys +static inline +int +compareKeys(uint32 type, const uint8 *key1, size_t length1, const uint8 *key2, + size_t length2) +{ + return compare_keys(key1, length1, key2, length2, type); +} + + + + + +// The parser has a very static design, but it will do what is required. +// +// ParseOr(), ParseAnd(), ParseEquation() are guarantying the operator +// precedence, that is =,!=,>,<,>=,<= .. && .. ||. +// Apparently, the "!" (not) can only be used with brackets. +// +// If you think that there are too few NULL pointer checks in some places +// of the code, just read the beginning of the query constructor. +// The API is not fully available, just the Query and the Expression class +// are. + + +enum ops { + OP_NONE, + + OP_AND, + OP_OR, + + OP_EQUATION, + + OP_EQUAL, + OP_UNEQUAL, + OP_GREATER_THAN, + OP_LESS_THAN, + OP_GREATER_THAN_OR_EQUAL, + OP_LESS_THAN_OR_EQUAL, +}; + +enum match { + NO_MATCH = 0, + MATCH_OK = 1, + + MATCH_BAD_PATTERN = -2, + MATCH_INVALID_CHARACTER +}; + +// return values from isValidPattern() +enum { + PATTERN_INVALID_ESCAPE = -3, + PATTERN_INVALID_RANGE, + PATTERN_INVALID_SET +}; + +union value { + int64 Int64; + uint64 Uint64; + int32 Int32; + uint32 Uint32; + float Float; + double Double; + char CString[kMaxIndexKeyLength]; +}; + +// B_MIME_STRING_TYPE is defined in storage/Mime.h, but we +// don't need the whole file here; the type can't change anyway +#ifndef _MIME_H +# define B_MIME_STRING_TYPE 'MIMS' +#endif + +class Term { + public: + Term(int8 op) : fOp(op), fParent(NULL) {} + virtual ~Term() {} + + int8 Op() const { return fOp; } + + void SetParent(Term *parent) { fParent = parent; } + Term *Parent() const { return fParent; } + + virtual status_t Match(Entry *entry, Node* node, + const char *attribute = NULL, int32 type = 0, + const uint8 *key = NULL, size_t size = 0) = 0; + virtual void Complement() = 0; + + virtual void CalculateScore(IndexWrapper &index) = 0; + virtual int32 Score() const = 0; + + virtual status_t InitCheck() = 0; + + virtual bool NeedsEntry() = 0; + +#ifdef DEBUG + virtual void PrintToStream() = 0; +#endif + + protected: + int8 fOp; + Term *fParent; +}; + +// Although an Equation object is quite independent from the volume on which +// the query is run, there are some dependencies that are produced while +// querying: +// The type/size of the value, the score, and if it has an index or not. +// So you could run more than one query on the same volume, but it might return +// wrong values when it runs concurrently on another volume. +// That's not an issue right now, because we run single-threaded and don't use +// queries more than once. + +class Equation : public Term { + public: + Equation(char **expr); + virtual ~Equation(); + + virtual status_t InitCheck(); + + status_t ParseQuotedString(char **_start, char **_end); + char *CopyString(char *start, char *end); + + virtual status_t Match(Entry *entry, Node* node, + const char *attribute = NULL, int32 type = 0, + const uint8 *key = NULL, size_t size = 0); + virtual void Complement(); + + status_t PrepareQuery(Volume *volume, IndexWrapper &index, IndexIterator **iterator, + bool queryNonIndexed); + status_t GetNextMatching(Volume *volume, IndexIterator *iterator, + struct dirent *dirent, size_t bufferSize); + + virtual void CalculateScore(IndexWrapper &index); + virtual int32 Score() const { return fScore; } + + virtual bool NeedsEntry(); + +#ifdef DEBUG + virtual void PrintToStream(); +#endif + + private: + Equation(const Equation &); + Equation &operator=(const Equation &); + // no implementation + + status_t ConvertValue(type_code type); + bool CompareTo(const uint8 *value, uint16 size); + uint8 *Value() const { return (uint8 *)&fValue; } + status_t MatchEmptyString(); + + char *fAttribute; + char *fString; + union value fValue; + type_code fType; + size_t fSize; + bool fIsPattern; + + int32 fScore; + bool fHasIndex; +}; + +class Operator : public Term { + public: + Operator(Term *,int8,Term *); + virtual ~Operator(); + + Term *Left() const { return fLeft; } + Term *Right() const { return fRight; } + + virtual status_t Match(Entry *entry, Node* node, + const char *attribute = NULL, int32 type = 0, + const uint8 *key = NULL, size_t size = 0); + virtual void Complement(); + + virtual void CalculateScore(IndexWrapper &index); + virtual int32 Score() const; + + virtual status_t InitCheck(); + + virtual bool NeedsEntry(); + + //Term *Copy() const; +#ifdef DEBUG + virtual void PrintToStream(); +#endif + + private: + Operator(const Operator &); + Operator &operator=(const Operator &); + // no implementation + + Term *fLeft,*fRight; +}; + + +//--------------------------------- + + +void +skipWhitespace(char **expr, int32 skip = 0) +{ + char *string = (*expr) + skip; + while (*string == ' ' || *string == '\t') string++; + *expr = string; +} + + +void +skipWhitespaceReverse(char **expr,char *stop) +{ + char *string = *expr; + while (string > stop && (*string == ' ' || *string == '\t')) string--; + *expr = string; +} + + +// #pragma mark - + + +uint32 +utf8ToUnicode(char **string) +{ + uint8 *bytes = (uint8 *)*string; + int32 length; + uint8 mask = 0x1f; + + switch (bytes[0] & 0xf0) { + case 0xc0: + case 0xd0: length = 2; break; + case 0xe0: length = 3; break; + case 0xf0: + mask = 0x0f; + length = 4; + break; + default: + // valid 1-byte character + // and invalid characters + (*string)++; + return bytes[0]; + } + uint32 c = bytes[0] & mask; + int32 i = 1; + for (;i < length && (bytes[i] & 0x80) > 0;i++) + c = (c << 6) | (bytes[i] & 0x3f); + + if (i < length) { + // invalid character + (*string)++; + return (uint32)bytes[0]; + } + *string += length; + return c; +} + + +int32 +getFirstPatternSymbol(char *string) +{ + char c; + + for (int32 index = 0;(c = *string++);index++) { + if (c == '*' || c == '?' || c == '[') + return index; + } + return -1; +} + + +bool +isPattern(char *string) +{ + return getFirstPatternSymbol(string) >= 0 ? true : false; +} + + +status_t +isValidPattern(char *pattern) +{ + while (*pattern) { + switch (*pattern++) { + case '\\': + // the escape character must not be at the end of the pattern + if (!*pattern++) + return PATTERN_INVALID_ESCAPE; + break; + + case '[': + if (pattern[0] == ']' || !pattern[0]) + return PATTERN_INVALID_SET; + + while (*pattern != ']') { + if (*pattern == '\\' && !*++pattern) + return PATTERN_INVALID_ESCAPE; + + if (!*pattern) + return PATTERN_INVALID_SET; + + if (pattern[0] == '-' && pattern[1] == '-') + return PATTERN_INVALID_RANGE; + + pattern++; + } + break; + } + } + return B_OK; +} + + +/** Matches the string against the given wildcard pattern. + * Returns either MATCH_OK, or NO_MATCH when everything went fine, + * or values < 0 (see enum at the top of Query.cpp) if an error + * occurs + */ + +status_t +matchString(char *pattern, char *string) +{ + while (*pattern) { + // end of string == valid end of pattern? + if (!string[0]) { + while (pattern[0] == '*') + pattern++; + return !pattern[0] ? MATCH_OK : NO_MATCH; + } + + switch (*pattern++) { + case '?': + { + // match exactly one UTF-8 character; we are + // not interested in the result + utf8ToUnicode(&string); + break; + } + + case '*': + { + // compact pattern + while (true) { + if (pattern[0] == '?') { + if (!*++string) + return NO_MATCH; + } else if (pattern[0] != '*') + break; + + pattern++; + } + + // if the pattern is done, we have matched the string + if (!pattern[0]) + return MATCH_OK; + + while(true) { + // we have removed all occurences of '*' and '?' + if (pattern[0] == string[0] + || pattern[0] == '[' + || pattern[0] == '\\') { + status_t status = matchString(pattern,string); + if (status < B_OK || status == MATCH_OK) + return status; + } + + // we could be nice here and just jump to the next + // UTF-8 character - but we wouldn't gain that much + // and it'd be slower (since we're checking for + // equality before entering the recursion) + if (!*++string) + return NO_MATCH; + } + break; + } + + case '[': + { + bool invert = false; + if (pattern[0] == '^' || pattern[0] == '!') { + invert = true; + pattern++; + } + + if (!pattern[0] || pattern[0] == ']') + return MATCH_BAD_PATTERN; + + uint32 c = utf8ToUnicode(&string); + bool matched = false; + + while (pattern[0] != ']') { + if (!pattern[0]) + return MATCH_BAD_PATTERN; + + if (pattern[0] == '\\') + pattern++; + + uint32 first = utf8ToUnicode(&pattern); + + // Does this character match, or is this a range? + if (first == c) { + matched = true; + break; + } else if (pattern[0] == '-' && pattern[1] != ']' && pattern[1]) { + pattern++; + + if (pattern[0] == '\\') { + pattern++; + if (!pattern[0]) + return MATCH_BAD_PATTERN; + } + uint32 last = utf8ToUnicode(&pattern); + + if (c >= first && c <= last) { + matched = true; + break; + } + } + } + + if (invert) + matched = !matched; + + if (matched) { + while (pattern[0] != ']') { + if (!pattern[0]) + return MATCH_BAD_PATTERN; + pattern++; + } + pattern++; + break; + } + return NO_MATCH; + } + + case '\\': + if (!pattern[0]) + return MATCH_BAD_PATTERN; + // supposed to fall through + default: + if (pattern[-1] != string[0]) + return NO_MATCH; + string++; + break; + } + } + + if (string[0]) + return NO_MATCH; + + return MATCH_OK; +} + + +// #pragma mark - + + +Equation::Equation(char **expr) + : Term(OP_EQUATION), + fAttribute(NULL), + fString(NULL), + fType(0), + fIsPattern(false) +{ + char *string = *expr; + char *start = string; + char *end = NULL; + + // Since the equation is the integral part of any query, we're just parsing + // the whole thing here. + // The whitespace at the start is already removed in Expression::ParseEquation() + + if (*start == '"' || *start == '\'') { + // string is quoted (start has to be on the beginning of a string) + if (ParseQuotedString(&start, &end) < B_OK) + return; + + // set string to a valid start of the equation symbol + string = end + 2; + skipWhitespace(&string); + if (*string != '=' && *string != '<' && *string != '>' && *string != '!') { + *expr = string; + return; + } + } else { + // search the (in)equation for the actual equation symbol (and for other operators + // in case the equation is malformed) + while (*string && *string != '=' && *string != '<' && *string != '>' && *string != '!' + && *string != '&' && *string != '|') + string++; + + // get the attribute string (and trim whitespace), in case + // the string was not quoted + end = string - 1; + skipWhitespaceReverse(&end, start); + } + + // attribute string is empty (which is not allowed) + if (start > end) + return; + + // at this point, "start" points to the beginning of the string, "end" points + // to the last character of the string, and "string" points to the first + // character of the equation symbol + + // test for the right symbol (as this doesn't need any memory) + switch (*string) { + case '=': + fOp = OP_EQUAL; + break; + case '>': + fOp = *(string + 1) == '=' ? OP_GREATER_THAN_OR_EQUAL : OP_GREATER_THAN; + break; + case '<': + fOp = *(string + 1) == '=' ? OP_LESS_THAN_OR_EQUAL : OP_LESS_THAN; + break; + case '!': + if (*(string + 1) != '=') + return; + fOp = OP_UNEQUAL; + break; + + // any invalid characters will be rejected + default: + *expr = string; + return; + } + // lets change "start" to point to the first character after the symbol + if (*(string + 1) == '=') + string++; + string++; + skipWhitespace(&string); + + // allocate & copy the attribute string + + fAttribute = CopyString(start, end); + if (fAttribute == NULL) + return; + + start = string; + if (*start == '"' || *start == '\'') { + // string is quoted (start has to be on the beginning of a string) + if (ParseQuotedString(&start, &end) < B_OK) + return; + + string = end + 2; + skipWhitespace(&string); + } else { + while (*string && *string != '&' && *string != '|' && *string != ')') + string++; + + end = string - 1; + skipWhitespaceReverse(&end, start); + } + + // at this point, "start" will point to the first character of the value, + // "end" will point to its last character, and "start" to the first non- + // whitespace character after the value string + + fString = CopyString(start, end); + if (fString == NULL) + return; + + // patterns are only allowed for these operations (and strings) + if (fOp == OP_EQUAL || fOp == OP_UNEQUAL) { + fIsPattern = isPattern(fString); + if (fIsPattern && isValidPattern(fString) < B_OK) { + // we only want to have valid patterns; setting fString + // to NULL will cause InitCheck() to fail + free(fString); + fString = NULL; + } + } + + *expr = string; +} + + +Equation::~Equation() +{ + if (fAttribute != NULL) + free(fAttribute); + if (fString != NULL) + free(fString); +} + + +status_t +Equation::InitCheck() +{ + if (fAttribute == NULL + || fString == NULL + || fOp == OP_NONE) + return B_BAD_VALUE; + + return B_OK; +} + + +status_t +Equation::ParseQuotedString(char **_start, char **_end) +{ + char *start = *_start; + char quote = *start++; + char *end = start; + + for (;*end && *end != quote;end++) { + if (*end == '\\') + end++; + } + if (*end == '\0') + return B_BAD_VALUE; + + *_start = start; + *_end = end - 1; + + return B_OK; +} + + +char * +Equation::CopyString(char *start, char *end) +{ + // end points to the last character of the string - and the length + // also has to include the null-termination + int32 length = end + 2 - start; + // just to make sure; since that's the max. attribute name length and + // the max. string in an index, it make sense to have it that way + if (length > (int32)kMaxIndexKeyLength || length <= 0) + return NULL; + + char *copy = (char *)malloc(length); + if (copy == NULL) + return NULL; + + memcpy(copy,start,length - 1); + copy[length - 1] = '\0'; + + return copy; +} + + +status_t +Equation::ConvertValue(type_code type) +{ + // Has the type already been converted? + if (type == fType) + return B_OK; + + char *string = fString; + + switch (type) { + case B_MIME_STRING_TYPE: + type = B_STRING_TYPE; + // supposed to fall through + case B_STRING_TYPE: + strncpy(fValue.CString, string, kMaxIndexKeyLength); + fValue.CString[kMaxIndexKeyLength - 1] = '\0'; + fSize = strlen(fValue.CString); + break; + case B_INT32_TYPE: + fValue.Int32 = strtol(string, &string, 0); + fSize = sizeof(int32); + break; + case B_UINT32_TYPE: + fValue.Int32 = strtoul(string, &string, 0); + fSize = sizeof(uint32); + break; + case B_INT64_TYPE: + fValue.Int64 = strtoll(string, &string, 0); + fSize = sizeof(int64); + break; + case B_UINT64_TYPE: + fValue.Uint64 = strtoull(string, &string, 0); + fSize = sizeof(uint64); + break; + case B_FLOAT_TYPE: + fValue.Float = strtod(string, &string); + fSize = sizeof(float); + break; + case B_DOUBLE_TYPE: + fValue.Double = strtod(string, &string); + fSize = sizeof(double); + break; + default: + FATAL(("query value conversion to 0x%lx requested!\n", type)); + // should we fail here or just do a safety int32 conversion? + return B_ERROR; + } + + fType = type; + + // patterns are only allowed for string types + if (fType != B_STRING_TYPE && fIsPattern) + fIsPattern = false; + + return B_OK; +} + + +/** Returns true when the key matches the equation. You have to + * call ConvertValue() before this one. + */ + +bool +Equation::CompareTo(const uint8 *value, uint16 size) +{ + int32 compare; + + // fIsPattern is only true if it's a string type, and fOp OP_EQUAL, or OP_UNEQUAL + if (fIsPattern) { + // we have already validated the pattern, so we don't check for failing + // here - if something is broken, and matchString() returns an error, + // we just don't match + compare = matchString(fValue.CString, (char *)value) == MATCH_OK ? 0 : 1; + } else + compare = compareKeys(fType, value, size, Value(), fSize); + + switch (fOp) { + case OP_EQUAL: + return compare == 0; + case OP_UNEQUAL: + return compare != 0; + case OP_LESS_THAN: + return compare < 0; + case OP_LESS_THAN_OR_EQUAL: + return compare <= 0; + case OP_GREATER_THAN: + return compare > 0; + case OP_GREATER_THAN_OR_EQUAL: + return compare >= 0; + } + FATAL(("Unknown/Unsupported operation: %d\n", fOp)); + return false; +} + + +void +Equation::Complement() +{ + D(if (fOp <= OP_EQUATION || fOp > OP_LESS_THAN_OR_EQUAL) { + FATAL(("op out of range!")); + return; + }); + + int8 complementOp[] = {OP_UNEQUAL, OP_EQUAL, OP_LESS_THAN_OR_EQUAL, + OP_GREATER_THAN_OR_EQUAL, OP_LESS_THAN, OP_GREATER_THAN}; + fOp = complementOp[fOp - OP_EQUAL]; +} + + +status_t +Equation::MatchEmptyString() +{ + // there is no matching attribute, we will just bail out if we + // already know that our value is not of a string type. + // If not, it will be converted to a string - and then be compared with "". + // That's why we have to call ConvertValue() here - but it will be + // a cheap call for the next time + // Should we do this only for OP_UNEQUAL? + if (fType != 0 && fType != B_STRING_TYPE) + return NO_MATCH; + + status_t status = ConvertValue(B_STRING_TYPE); + if (status == B_OK) + status = CompareTo((const uint8 *)"", fSize) ? MATCH_OK : NO_MATCH; + + return status; +} + + +/** Matches the inode's attribute value with the equation. + * Returns MATCH_OK if it matches, NO_MATCH if not, < 0 if something went wrong + */ + +status_t +Equation::Match(Entry *entry, Node* node, const char *attributeName, int32 type, + const uint8 *key, size_t size) +{ + // get a pointer to the attribute in question + union value value; + const uint8 *buffer; + + // first, check if we are matching for a live query and use that value + if (attributeName != NULL && !strcmp(fAttribute, attributeName)) { + if (key == NULL) { + if (type == B_STRING_TYPE) { + // special case: a NULL "name" means the entry has been removed + // or not yet been added -- we refuse to match, whatever the + // pattern + if (!strcmp(fAttribute, "name")) + return NO_MATCH; + + return MatchEmptyString(); + } + + return NO_MATCH; + } + buffer = const_cast(key); + } else if (!strcmp(fAttribute, "name")) { + // if not, check for "fake" attributes, "name", "size", "last_modified", + if (!entry) + return B_ERROR; + buffer = (uint8 *)entry->GetName(); + if (buffer == NULL) + return B_ERROR; + + type = B_STRING_TYPE; + size = strlen((const char *)buffer); + } else if (!strcmp(fAttribute,"size")) { + value.Int64 = node->GetSize(); + buffer = (uint8 *)&value; + type = B_INT64_TYPE; + } else if (!strcmp(fAttribute,"last_modified")) { + value.Int32 = node->GetMTime(); + buffer = (uint8 *)&value; + type = B_INT32_TYPE; + } else { + // then for attributes + Attribute *attribute = NULL; + + if (node->FindAttribute(fAttribute, &attribute) == B_OK) { + attribute->GetKey(&buffer, &size); + type = attribute->GetType(); + } else + return MatchEmptyString(); + } + // prepare own value for use, if it is possible to convert it + status_t status = ConvertValue(type); + if (status == B_OK) + status = CompareTo(buffer, size) ? MATCH_OK : NO_MATCH; + + RETURN_ERROR(status); +} + + +void +Equation::CalculateScore(IndexWrapper &index) +{ + // As always, these values could be tuned and refined. + // And the code could also need some real world testing :-) + + // do we have to operate on a "foreign" index? + if (fOp == OP_UNEQUAL || index.SetTo(fAttribute) < B_OK) { + fScore = 0; + return; + } + + // if we have a pattern, how much does it help our search? + if (fIsPattern) + fScore = getFirstPatternSymbol(fString) << 3; + else { + // Score by operator + if (fOp == OP_EQUAL) + // higher than pattern="255 chars+*" + fScore = 2048; + else + // the pattern search is regarded cheaper when you have at + // least one character to set your index to + fScore = 5; + } + + // take index size into account (1024 is the current node size + // in our B+trees) + // 2048 * 2048 == 4194304 is the maximum score (for an empty + // tree, since the header + 1 node are already 2048 bytes) + fScore = fScore * ((2048 * 1024LL) / index.GetSize()); +} + + +status_t +Equation::PrepareQuery(Volume */*volume*/, IndexWrapper &index, IndexIterator **iterator, bool queryNonIndexed) +{ + status_t status = index.SetTo(fAttribute); + + // if we should query attributes without an index, we can just proceed here + if (status < B_OK && !queryNonIndexed) + return B_ENTRY_NOT_FOUND; + + type_code type; + + // special case for OP_UNEQUAL - it will always operate through the whole index + // but we need the call to the original index to get the correct type + if (status < B_OK || fOp == OP_UNEQUAL) { + // Try to get an index that holds all files (name) + // Also sets the default type for all attributes without index + // to string. + type = status < B_OK ? B_STRING_TYPE : index.Type(); + + if (index.SetTo("name") < B_OK) + return B_ENTRY_NOT_FOUND; + + fHasIndex = false; + } else { + fHasIndex = true; + type = index.Type(); + } + + if (ConvertValue(type) < B_OK) + return B_BAD_VALUE; + + *iterator = new IndexIterator(&index); + if (*iterator == NULL) + return B_NO_MEMORY; + + if ((fOp == OP_EQUAL || fOp == OP_GREATER_THAN || fOp == OP_GREATER_THAN_OR_EQUAL + || fIsPattern) + && fHasIndex) { + // set iterator to the exact position + + int32 keySize = index.KeySize(); + + // at this point, fIsPattern is only true if it's a string type, and fOp + // is either OP_EQUAL or OP_UNEQUAL + if (fIsPattern) { + // let's see if we can use the beginning of the key for positioning + // the iterator and adjust the key size; if not, just leave the + // iterator at the start and return success + keySize = getFirstPatternSymbol(fString); + if (keySize <= 0) + return B_OK; + } + + if (keySize == 0) { + // B_STRING_TYPE doesn't have a fixed length, so it was set + // to 0 before - we compute the correct value here + if (fType == B_STRING_TYPE) { + keySize = strlen(fValue.CString); + + // The empty string is a special case - we normally don't check + // for the trailing null byte, in the case for the empty string + // we do it explicitly, because there can't be keys in the B+tree + // with a length of zero + if (keySize == 0) + keySize = 1; + } else + RETURN_ERROR(B_ENTRY_NOT_FOUND); + } + + status = (*iterator)->Find(Value(), keySize); + if (fOp == OP_EQUAL && !fIsPattern) + return status; + else if (status == B_ENTRY_NOT_FOUND + && (fIsPattern || fOp == OP_GREATER_THAN || fOp == OP_GREATER_THAN_OR_EQUAL)) + return B_OK; + + RETURN_ERROR(status); + } + + return B_OK; +} + + +status_t +Equation::GetNextMatching(Volume *volume, IndexIterator *iterator, + struct dirent *dirent, size_t bufferSize) +{ + while (true) { + union value indexValue; + uint16 keyLength; + Entry *entry = NULL; + + status_t status = iterator->GetNextEntry((uint8*)&indexValue, &keyLength, + (uint16)sizeof(indexValue), &entry); + if (status < B_OK) + return status; + + // only compare against the index entry when this is the correct + // index for the equation + if (fHasIndex && !CompareTo((uint8 *)&indexValue, keyLength)) { + // They aren't equal? let the operation decide what to do + // Since we always start at the beginning of the index (or the correct + // position), only some needs to be stopped if the entry doesn't fit. + if (fOp == OP_LESS_THAN + || fOp == OP_LESS_THAN_OR_EQUAL + || (fOp == OP_EQUAL && !fIsPattern)) + return B_ENTRY_NOT_FOUND; + + continue; + } + + // ToDo: check user permissions here - but which one?! + // we could filter out all those where we don't have + // read access... (we should check for every parent + // directory if the X_OK is allowed) + // Although it's quite expensive to open all parents, + // it's likely that the application that runs the + // query will do something similar (and we don't have + // to do it for root, either). + + // go up in the tree until a &&-operator is found, and check if the + // inode matches with the rest of the expression - we don't have to + // check ||-operators for that + Term *term = this; + status = MATCH_OK; + + if (!fHasIndex) + status = Match(entry, entry->GetNode()); + + while (term != NULL && status == MATCH_OK) { + Operator *parent = (Operator *)term->Parent(); + if (parent == NULL) + break; + + if (parent->Op() == OP_AND) { + // choose the other child of the parent + Term *other = parent->Right(); + if (other == term) + other = parent->Left(); + + if (other == NULL) { + FATAL(("&&-operator has only one child... (parent = %p)\n", parent)); + break; + } + status = other->Match(entry, entry->GetNode()); + if (status < 0) { + REPORT_ERROR(status); + status = NO_MATCH; + } + } + term = (Term *)parent; + } + + if (status == MATCH_OK) { + size_t nameLen = strlen(entry->GetName()); + + // check, whether the entry fits into the buffer, + // and fill it in + size_t length = (dirent->d_name + nameLen + 1) - (char*)dirent; + if (length > bufferSize) + RETURN_ERROR(B_BUFFER_OVERFLOW); + + dirent->d_dev = volume->GetID(); + dirent->d_ino = entry->GetNode()->GetID(); + dirent->d_pdev = volume->GetID(); + dirent->d_pino = entry->GetParent()->GetID(); + + memcpy(dirent->d_name, entry->GetName(), nameLen); + dirent->d_name[nameLen] = '\0'; + dirent->d_reclen = length; + } + + if (status == MATCH_OK) + return B_OK; + } + RETURN_ERROR(B_ERROR); +} + + +bool +Equation::NeedsEntry() +{ + return strcmp(fAttribute, "name") == 0; +} + + +// #pragma mark - + + +Operator::Operator(Term *left, int8 op, Term *right) + : Term(op), + fLeft(left), + fRight(right) +{ + if (left) + left->SetParent(this); + if (right) + right->SetParent(this); +} + + +Operator::~Operator() +{ + delete fLeft; + delete fRight; +} + + +status_t +Operator::Match(Entry *entry, Node* node, const char *attribute, + int32 type, const uint8 *key, size_t size) +{ + if (fOp == OP_AND) { + status_t status = fLeft->Match(entry, node, attribute, type, key, size); + if (status != MATCH_OK) + return status; + + return fRight->Match(entry, node, attribute, type, key, size); + } else { + // choose the term with the better score for OP_OR + if (fRight->Score() > fLeft->Score()) { + status_t status = fRight->Match(entry, node, attribute, type, key, + size); + if (status != NO_MATCH) + return status; + } + return fLeft->Match(entry, node, attribute, type, key, size); + } +} + + +void +Operator::Complement() +{ + if (fOp == OP_AND) + fOp = OP_OR; + else + fOp = OP_AND; + + fLeft->Complement(); + fRight->Complement(); +} + + +void +Operator::CalculateScore(IndexWrapper &index) +{ + fLeft->CalculateScore(index); + fRight->CalculateScore(index); +} + + +int32 +Operator::Score() const +{ + if (fOp == OP_AND) { + // return the one with the better score + if (fRight->Score() > fLeft->Score()) + return fRight->Score(); + + return fLeft->Score(); + } + + // for OP_OR, be honest, and return the one with the worse score + if (fRight->Score() < fLeft->Score()) + return fRight->Score(); + + return fLeft->Score(); +} + + +status_t +Operator::InitCheck() +{ + if (fOp != OP_AND && fOp != OP_OR + || fLeft == NULL || fLeft->InitCheck() < B_OK + || fRight == NULL || fRight->InitCheck() < B_OK) + return B_ERROR; + + return B_OK; +} + + +bool +Operator::NeedsEntry() +{ + return ((fLeft && fLeft->NeedsEntry()) || (fRight && fRight->NeedsEntry())); +} + + +#if 0 +Term * +Operator::Copy() const +{ + if (fEquation != NULL) { + Equation *equation = new Equation(*fEquation); + if (equation == NULL) + return NULL; + + Term *term = new Term(equation); + if (term == NULL) + delete equation; + + return term; + } + + Term *left = NULL, *right = NULL; + + if (fLeft != NULL && (left = fLeft->Copy()) == NULL) + return NULL; + if (fRight != NULL && (right = fRight->Copy()) == NULL) { + delete left; + return NULL; + } + + Term *term = new Term(left,fOp,right); + if (term == NULL) { + delete left; + delete right; + return NULL; + } + return term; +} +#endif + + +// #pragma mark - + +#ifdef DEBUG +void +Operator::PrintToStream() +{ + D(__out("( ")); + if (fLeft != NULL) + fLeft->PrintToStream(); + + char *op; + switch (fOp) { + case OP_OR: op = "OR"; break; + case OP_AND: op = "AND"; break; + default: op = "?"; break; + } + D(__out(" %s ",op)); + + if (fRight != NULL) + fRight->PrintToStream(); + + D(__out(" )")); +} + + +void +Equation::PrintToStream() +{ + char *symbol = "???"; + switch (fOp) { + case OP_EQUAL: symbol = "=="; break; + case OP_UNEQUAL: symbol = "!="; break; + case OP_GREATER_THAN: symbol = ">"; break; + case OP_GREATER_THAN_OR_EQUAL: symbol = ">="; break; + case OP_LESS_THAN: symbol = "<"; break; + case OP_LESS_THAN_OR_EQUAL: symbol = "<="; break; + } + D(__out("[\"%s\" %s \"%s\"]", fAttribute, symbol, fString)); +} + +#endif /* DEBUG */ + +// #pragma mark - + + +Expression::Expression(char *expr) +{ + if (expr == NULL) + return; + + fTerm = ParseOr(&expr); + if (fTerm != NULL && fTerm->InitCheck() < B_OK) { + FATAL(("Corrupt tree in expression!\n")); + delete fTerm; + fTerm = NULL; + } + D(if (fTerm != NULL) { + fTerm->PrintToStream(); + D(__out("\n")); + if (*expr != '\0') + PRINT(("Unexpected end of string: \"%s\"!\n", expr)); + }); + fPosition = expr; +} + + +Expression::~Expression() +{ + delete fTerm; +} + + +Term * +Expression::ParseEquation(char **expr) +{ + skipWhitespace(expr); + + bool not = false; + if (**expr == '!') { + skipWhitespace(expr, 1); + if (**expr != '(') + return NULL; + + not = true; + } + + if (**expr == ')') { + // shouldn't be handled here + return NULL; + } else if (**expr == '(') { + skipWhitespace(expr, 1); + + Term *term = ParseOr(expr); + + skipWhitespace(expr); + + if (**expr != ')') { + delete term; + return NULL; + } + + // If the term is negated, we just complement the tree, to get + // rid of the not, a.k.a. DeMorgan's Law. + if (not) + term->Complement(); + + skipWhitespace(expr, 1); + + return term; + } + + Equation *equation = new Equation(expr); + if (equation == NULL || equation->InitCheck() < B_OK) { + delete equation; + return NULL; + } + return equation; +} + + +Term * +Expression::ParseAnd(char **expr) +{ + Term *left = ParseEquation(expr); + if (left == NULL) + return NULL; + + while (IsOperator(expr,'&')) { + Term *right = ParseAnd(expr); + Term *newParent = NULL; + + if (right == NULL || (newParent = new Operator(left, OP_AND, right)) == NULL) { + delete left; + delete right; + + return NULL; + } + left = newParent; + } + + return left; +} + + +Term * +Expression::ParseOr(char **expr) +{ + Term *left = ParseAnd(expr); + if (left == NULL) + return NULL; + + while (IsOperator(expr,'|')) { + Term *right = ParseAnd(expr); + Term *newParent = NULL; + + if (right == NULL || (newParent = new Operator(left, OP_OR, right)) == NULL) { + delete left; + delete right; + + return NULL; + } + left = newParent; + } + + return left; +} + + +bool +Expression::IsOperator(char **expr, char op) +{ + char *string = *expr; + + if (*string == op && *(string + 1) == op) { + *expr += 2; + return true; + } + return false; +} + + +status_t +Expression::InitCheck() +{ + if (fTerm == NULL) + return B_BAD_VALUE; + + return B_OK; +} + + +// #pragma mark - + + +Query::Query(Volume *volume, Expression *expression, uint32 flags) + : + fVolume(volume), + fExpression(expression), + fCurrent(NULL), + fIterator(NULL), + fIndex(volume), + fFlags(flags), + fPort(-1), + fNeedsEntry(false) +{ + // if the expression has a valid root pointer, the whole tree has + // already passed the sanity check, so that we don't have to check + // every pointer + if (volume == NULL || expression == NULL || expression->Root() == NULL) + return; + + // create index on the stack and delete it afterwards + fExpression->Root()->CalculateScore(fIndex); + fIndex.Unset(); + + fNeedsEntry = fExpression->Root()->NeedsEntry(); + + Rewind(); + + if (fFlags & B_LIVE_QUERY) + volume->AddQuery(this); +} + + +Query::~Query() +{ + if (fFlags & B_LIVE_QUERY) + fVolume->RemoveQuery(this); +} + + +status_t +Query::Rewind() +{ + // free previous stuff + + fStack.MakeEmpty(); + + delete fIterator; + fIterator = NULL; + fCurrent = NULL; + + // put the whole expression on the stack + + Stack stack; + stack.Push(fExpression->Root()); + + Term *term; + while (stack.Pop(&term)) { + if (term->Op() < OP_EQUATION) { + Operator *op = (Operator *)term; + + if (op->Op() == OP_OR) { + stack.Push(op->Left()); + stack.Push(op->Right()); + } else { + // For OP_AND, we can use the scoring system to decide which path to add + if (op->Right()->Score() > op->Left()->Score()) + stack.Push(op->Right()); + else + stack.Push(op->Left()); + } + } else if (term->Op() == OP_EQUATION || fStack.Push((Equation *)term) < B_OK) + FATAL(("Unknown term on stack or stack error")); + } + + return B_OK; +} + + +status_t +Query::GetNextEntry(struct dirent *dirent, size_t size) +{ + // If we don't have an equation to use yet/anymore, get a new one + // from the stack + while (true) { + if (fIterator == NULL) { + if (!fStack.Pop(&fCurrent) + || fCurrent == NULL + || fCurrent->PrepareQuery(fVolume, fIndex, &fIterator, + fFlags & B_QUERY_NON_INDEXED) < B_OK) + return B_ENTRY_NOT_FOUND; + } + if (fCurrent == NULL) + RETURN_ERROR(B_ERROR); + + status_t status = fCurrent->GetNextMatching(fVolume, fIterator, dirent, size); + if (status < B_OK) { + delete fIterator; + fIterator = NULL; + fCurrent = NULL; + } else { + // only return if we have another entry + return B_OK; + } + } +} + + +void +Query::SetLiveMode(port_id port, int32 token) +{ + fPort = port; + fToken = token; + + if ((fFlags & B_LIVE_QUERY) == 0) { + // you can decide at any point to set the live query mode, + // only live queries have to be updated by attribute changes + fFlags |= B_LIVE_QUERY; + fVolume->AddQuery(this); + } +} + + +void +Query::LiveUpdate(Entry *entry, Node* node, const char *attribute, int32 type, + const uint8 *oldKey, size_t oldLength, const uint8 *newKey, + size_t newLength) +{ +PRINT(("%p->Query::LiveUpdate(%p, %p, \"%s\", 0x%lx, %p, %lu, %p, %lu)\n", +this, entry, node, attribute, type, oldKey, oldLength, newKey, newLength)); + if (fPort < 0 || fExpression == NULL || node == NULL || attribute == NULL) + return; + + // ToDo: check if the attribute is part of the query at all... + + // If no entry has been supplied, but the we need one for the evaluation + // (i.e. the "name" attribute is used), we invoke ourselves for all entries + // referring to the given node. + if (!entry && fNeedsEntry) { + entry = node->GetFirstReferrer(); + while (entry) { + LiveUpdate(entry, node, attribute, type, oldKey, oldLength, newKey, + newLength); + entry = node->GetNextReferrer(entry); + } + return; + } + + status_t oldStatus = fExpression->Root()->Match(entry, node, attribute, + type, oldKey, oldLength); + status_t newStatus = fExpression->Root()->Match(entry, node, attribute, + type, newKey, newLength); +PRINT((" oldStatus: 0x%lx, newStatus: 0x%lx\n", oldStatus, newStatus)); + + int32 op; + if (oldStatus == MATCH_OK && newStatus == MATCH_OK) { + // only send out a notification if the name was changed + if (oldKey == NULL || strcmp(attribute,"name")) + return; + + if (entry) { + // entry should actually always be given, when the changed + // attribute is the entry name +PRINT(("send_notification(): old: B_ENTRY_REMOVED\n")); + send_notification(fPort, fToken, B_QUERY_UPDATE, B_ENTRY_REMOVED, + fVolume->GetID(), 0, entry->GetParent()->GetID(), 0, + entry->GetNode()->GetID(), (const char *)oldKey); + } + op = B_ENTRY_CREATED; + } else if (oldStatus != MATCH_OK && newStatus != MATCH_OK) { + // nothing has changed + return; + } else if (oldStatus == MATCH_OK && newStatus != MATCH_OK) + op = B_ENTRY_REMOVED; + else + op = B_ENTRY_CREATED; + + // We send a notification for the given entry, if any, or otherwise for + // all entries referring to the node; + if (entry) { +PRINT(("send_notification(): new: %s\n", (op == B_ENTRY_REMOVED ? "B_ENTRY_REMOVED" : "B_ENTRY_CREATED"))); + send_notification(fPort, fToken, B_QUERY_UPDATE, op, fVolume->GetID(), + 0, entry->GetParent()->GetID(), 0, entry->GetNode()->GetID(), + entry->GetName()); + } else { + entry = node->GetFirstReferrer(); + while (entry) { + send_notification(fPort, fToken, B_QUERY_UPDATE, op, + fVolume->GetID(), 0, entry->GetParent()->GetID(), 0, + entry->GetNode()->GetID(), entry->GetName()); + entry = node->GetNextReferrer(entry); + } + } +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/Query.h b/src/add-ons/kernel/file_systems/ramfs/Query.h new file mode 100644 index 0000000000..7d909d47f5 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Query.h @@ -0,0 +1,127 @@ +/* Query - query parsing and evaluation + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + * + * Adjusted by Ingo Weinhold for usage in RAM FS. + */ +#ifndef QUERY_H +#define QUERY_H + + +#include +#include + +#include "DLList.h" +#include "Index.h" +#include "Stack.h" +#include "ramfs.h" + +class Entry; +class Equation; +class IndexIterator; +class Node; +class Query; +class Term; +class Volume; + + +#define B_QUERY_NON_INDEXED 0x00000002 + + +// Wraps the RAM FS Index to provide the interface required by the Query +// implementation. At least most of it. +// +// IndexWrapper +class IndexWrapper { +public: + IndexWrapper(Volume *volume); + + status_t SetTo(const char *name); + void Unset(); + + uint32 Type() const; + off_t GetSize() const; + int32 KeySize() const; + +private: + friend class IndexIterator; + + Volume *fVolume; + Index *fIndex; +}; + +// IndexIterator +class IndexIterator { +public: + IndexIterator(IndexWrapper *indexWrapper); + + status_t Find(const uint8 *const key, size_t keyLength); + status_t GetNextEntry(uint8 *buffer, uint16 *keyLength, size_t bufferSize, + Entry **entry); + +private: + IndexWrapper *fIndexWrapper; + IndexEntryIterator fIterator; + bool fInitialized; +}; + + +class Expression { + public: + Expression(char *expr); + ~Expression(); + + status_t InitCheck(); + const char *Position() const { return fPosition; } + Term *Root() const { return fTerm; } + + protected: + Term *ParseOr(char **expr); + Term *ParseAnd(char **expr); + Term *ParseEquation(char **expr); + + bool IsOperator(char **expr,char op); + + private: + Expression(const Expression &); + Expression &operator=(const Expression &); + // no implementation + + char *fPosition; + Term *fTerm; +}; + +class Query : public DLListLinkImpl { + public: + Query(Volume *volume, Expression *expression, uint32 flags); + ~Query(); + + status_t Rewind(); + status_t GetNextEntry(struct dirent *, size_t size); + + void SetLiveMode(port_id port, int32 token); + void LiveUpdate(Entry *entry, Node* node, const char *attribute, + int32 type, const uint8 *oldKey, size_t oldLength, + const uint8 *newKey, size_t newLength); + + Expression *GetExpression() const { return fExpression; } + + private: +// void SendNotification(Entry* entry) + + private: + Volume *fVolume; + Expression *fExpression; + Equation *fCurrent; + IndexIterator *fIterator; + IndexWrapper fIndex; + Stack fStack; + + uint32 fFlags; + port_id fPort; + int32 fToken; + bool fNeedsEntry; +}; + +#endif /* QUERY_H */ diff --git a/src/add-ons/kernel/file_systems/ramfs/SLList.h b/src/add-ons/kernel/file_systems/ramfs/SLList.h new file mode 100644 index 0000000000..f979a442af --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/SLList.h @@ -0,0 +1,288 @@ +// SLList.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef SL_LIST_H +#define SL_LIST_H + +#include + +// SLListStandardNode +template +struct SLListStandardNode { + SLListStandardNode(const Value &a) + : value(a), + next(NULL) + { + } + + Value value; + SLListStandardNode *next; +}; + +// SLListStandardNodeAllocator +template +class SLListStandardNodeAllocator +{ +public: + inline Node *Allocate(const Value &a) const + { + return new(nothrow) SLListStandardNode(a); + } + + inline void Free(Node *node) const + { + delete node; + } +}; + +// SLListValueNodeAllocator +template +class SLListValueNodeAllocator +{ +public: + inline Node *Allocate(const Value &a) const + { + return a; + } + + inline void Free(Node *node) const + { + } +}; + +// SLListStandardGetValue +template +class SLListStandardGetValue +{ +public: + inline Value &operator()(Node *node) const + { + return node->value; + } +}; + +// SLListValueNodeGetValue +template +class SLListValueNodeGetValue +{ +public: + inline Value &operator()(Node *node) const + { + return *node; + } +}; + +// for convenience +#define SL_LIST_TEMPLATE_LIST template +#define SL_LIST_CLASS_NAME SLList + +// SLList +template, + typename NodeAllocator = SLListStandardNodeAllocator, + typename GetValue = SLListStandardGetValue > +class SLList { +public: + class Iterator; + +public: + SLList(); + SLList(const NodeAllocator &nodeAllocator, const GetValue &getValue); + ~SLList(); + + bool Insert(const Value &value, Iterator *iterator = NULL); + bool Remove(const Value &value); + void Remove(Iterator &iterator); + void RemoveAll(); + + bool Find(const Value &value, Iterator *iterator = NULL) const; + void GetIterator(Iterator *iterator) const; + +private: + friend class Iterator; + + Node *fHead; + NodeAllocator fNodeAllocator; + GetValue fGetValue; +}; + +// Iterator +SL_LIST_TEMPLATE_LIST +class SL_LIST_CLASS_NAME::Iterator { +public: + Iterator() : fList(NULL), fCurrent(NULL) {} + ~Iterator() {} + + inline Value *GetCurrent() + { + return (fList && fCurrent ? &fList->fGetValue(fCurrent) : NULL); + } + + inline Value *GetNext() + { + if (fCurrent) + fCurrent = fCurrent->next; + return GetCurrent(); + } + + inline void Remove() + { + if (fList) + fList->Remove(*this); + } + +private: + friend class SL_LIST_CLASS_NAME; + + inline void _SetTo(SL_LIST_CLASS_NAME *list, Node *previous, Node *node) + { + fList = list; + fPrevious = previous; + fCurrent = node; + } + + inline SL_LIST_CLASS_NAME *_GetList() const { return fList; } + inline Node *_GetPreviousNode() const { return fPrevious; } + inline Node *_GetCurrentNode() const { return fCurrent; } + +private: + SL_LIST_CLASS_NAME *fList; + Node *fPrevious; + Node *fCurrent; +}; + +// constructor +SL_LIST_TEMPLATE_LIST +SL_LIST_CLASS_NAME::SLList() + : fHead(NULL)/*, + fNodeAllocator(), + fGetValue()*/ +{ +} + +// constructor +SL_LIST_TEMPLATE_LIST +SL_LIST_CLASS_NAME::SLList(const NodeAllocator &nodeAllocator, + const GetValue &getValue) + : fHead(NULL), + fNodeAllocator(nodeAllocator), + fGetValue(getValue) +{ +} + +// destructor +SL_LIST_TEMPLATE_LIST +SL_LIST_CLASS_NAME::~SLList() +{ + RemoveAll(); +} + +// Insert +SL_LIST_TEMPLATE_LIST +bool +SL_LIST_CLASS_NAME::Insert(const Value &value, Iterator *iterator) +{ + Node *node = fNodeAllocator.Allocate(value); + if (node) { + node->next = fHead; + fHead = node; + if (iterator) + iterator->_SetTo(this, NULL, node); + } + return node; +} + +// Remove +SL_LIST_TEMPLATE_LIST +bool +SL_LIST_CLASS_NAME::Remove(const Value &value) +{ + Iterator iterator; + bool result = Find(value, &iterator); + if (result) + iterator.Remove(); + return result; +} + +// Remove +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::Remove(Iterator &iterator) +{ + Node *node = iterator._GetCurrentNode(); + if (iterator._GetList() == this && node) { + Node *previous = iterator._GetPreviousNode(); + iterator._SetTo(this, previous, node->next); + if (previous) + previous->next = node->next; + else + fHead = node->next; + fNodeAllocator.Free(node); + } +} + +// RemoveAll +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::RemoveAll() +{ + for (Node *node = fHead; node; ) { + Node *next = node->next; + fNodeAllocator.Free(node); + node = next; + } + fHead = NULL; +} + +// Find +SL_LIST_TEMPLATE_LIST +bool +SL_LIST_CLASS_NAME::Find(const Value &value, Iterator *iterator) const +{ + Node *node = fHead; + Node *previous = NULL; + while (node && fGetValue(node) != value) { + previous = node; + node = node->next; + } + if (node && iterator) { + iterator->_SetTo(const_cast(this), previous, + node); + } + return node; +} + +// GetIterator +SL_LIST_TEMPLATE_LIST +void +SL_LIST_CLASS_NAME::GetIterator(Iterator *iterator) const +{ + if (iterator) + iterator->_SetTo(const_cast(this), NULL, fHead); +} + +#endif // SL_LIST_H diff --git a/src/add-ons/kernel/file_systems/ramfs/SizeIndex.cpp b/src/add-ons/kernel/file_systems/ramfs/SizeIndex.cpp new file mode 100644 index 0000000000..f59068a5ce --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/SizeIndex.cpp @@ -0,0 +1,371 @@ +// SizeIndex.cpp + +#include + +#include "Entry.h" +#include "EntryListener.h" +#include "IndexImpl.h" +#include "Node.h" +#include "NodeListener.h" +#include "SizeIndex.h" +#include "Volume.h" + +// SizeIndexPrimaryKey +class SizeIndexPrimaryKey { +public: + SizeIndexPrimaryKey(Node *node, off_t size) + : node(node), size(size) {} + SizeIndexPrimaryKey(Node *node) + : node(node), size(node->GetSize()) {} + SizeIndexPrimaryKey(off_t size) + : node(NULL), size(size) {} + + Node *node; + off_t size; +}; + +// SizeIndexGetPrimaryKey +class SizeIndexGetPrimaryKey { +public: + inline SizeIndexPrimaryKey operator()(Node *a) + { + return SizeIndexPrimaryKey(a); + } + + inline SizeIndexPrimaryKey operator()(Node *a) const + { + return SizeIndexPrimaryKey(a); + } +}; + +// SizeIndexPrimaryKeyCompare +class SizeIndexPrimaryKeyCompare +{ +public: + inline int operator()(const SizeIndexPrimaryKey &a, + const SizeIndexPrimaryKey &b) const + { + if (a.node != NULL && a.node == b.node) + return 0; + if (a.size < b.size) + return -1; + if (a.size > b.size) + return 1; + return 0; + } +}; + + +// NodeTree +typedef TwoKeyAVLTree + _NodeTree; +class SizeIndex::NodeTree : public _NodeTree {}; + + +// IteratorList +class SizeIndex::IteratorList : public DLList {}; + + +// Iterator +class SizeIndex::Iterator + : public NodeEntryIterator, + public DLListLinkImpl, public EntryListener, + public NodeListener { +public: + Iterator(); + virtual ~Iterator(); + + virtual Entry *GetCurrent(); + virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength); + + virtual status_t Suspend(); + virtual status_t Resume(); + + bool SetTo(SizeIndex *index, off_t size, bool ignoreValue = false); + void Unset(); + + virtual void EntryRemoved(Entry *entry); + virtual void NodeRemoved(Node *node); + +private: + typedef NodeEntryIterator BaseClass; + +private: + SizeIndex *fIndex; +}; + + +// SizeIndex + +// constructor +SizeIndex::SizeIndex(Volume *volume) + : Index(volume, "size", B_INT64_TYPE, true, sizeof(off_t)), + fNodes(new(nothrow) NodeTree), + fIterators(new(nothrow) IteratorList) +{ + if (fInitStatus == B_OK && (!fNodes || !fIterators)) + fInitStatus = B_NO_MEMORY; + if (fInitStatus == B_OK) { + fInitStatus = fVolume->AddNodeListener(this, + NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL); + } +} + +// destructor +SizeIndex::~SizeIndex() +{ + if (fVolume) + fVolume->RemoveNodeListener(this, NULL); + if (fIterators) { + // unset the iterators + for (Iterator *iterator = fIterators->GetFirst(); + iterator; + iterator = fIterators->GetNext(iterator)) { + iterator->SetTo(NULL, 0); + } + delete fIterators; + } + if (fNodes) + delete fNodes; +} + +// CountEntries +int32 +SizeIndex::CountEntries() const +{ + return fNodes->CountItems(); +} + +// Changed +status_t +SizeIndex::Changed(Node *node, off_t oldSize) +{ + status_t error = B_BAD_VALUE; + if (node) { + NodeTree::Iterator it; + Node **foundNode = fNodes->Find(SizeIndexPrimaryKey(node, oldSize), + node, &it); + if (foundNode && *foundNode == node) { + // update the iterators + for (Iterator *iterator = fIterators->GetFirst(); + iterator; + iterator = fIterators->GetNext(iterator)) { + if (iterator->GetCurrentNode() == node) + iterator->NodeRemoved(node); + } + + // remove and re-insert the node + fNodes->Remove(it); + error = fNodes->Insert(node); + + // udpate live queries + off_t newSize = node->GetSize(); + fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(), + (const uint8*)&oldSize, sizeof(oldSize), (const uint8*)&newSize, + sizeof(newSize)); + } + } + return error; +} + +// NodeAdded +void +SizeIndex::NodeAdded(Node *node) +{ + if (node) + fNodes->Insert(node); +} + +// NodeRemoved +void +SizeIndex::NodeRemoved(Node *node) +{ + if (node) + fNodes->Remove(node, node); +} + +// InternalGetIterator +AbstractIndexEntryIterator * +SizeIndex::InternalGetIterator() +{ + Iterator *iterator = new(nothrow) Iterator; + if (iterator) { + if (!iterator->SetTo(this, 0, true)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// InternalFind +AbstractIndexEntryIterator * +SizeIndex::InternalFind(const uint8 *key, size_t length) +{ + if (!key || length != sizeof(off_t)) + return NULL; + Iterator *iterator = new(nothrow) Iterator; + if (iterator) { + if (!iterator->SetTo(this, *(const off_t*)key)) { + delete iterator; + iterator = NULL; + } + } + return iterator; +} + +// _AddIterator +void +SizeIndex::_AddIterator(Iterator *iterator) +{ + fIterators->Insert(iterator); +} + +// _RemoveIterator +void +SizeIndex::_RemoveIterator(Iterator *iterator) +{ + fIterators->Remove(iterator); +} + + +// Iterator + +// constructor +SizeIndex::Iterator::Iterator() + : BaseClass(), + fIndex(NULL) +{ +} + +// destructor +SizeIndex::Iterator::~Iterator() +{ + SetTo(NULL, 0); +} + +// GetCurrent +Entry * +SizeIndex::Iterator::GetCurrent() +{ + return BaseClass::GetCurrent(); +} + +// GetCurrent +Entry * +SizeIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength) +{ + Entry *entry = GetCurrent(); + if (entry) { + *(off_t*)buffer = entry->GetNode()->GetSize(); + *keyLength = sizeof(size_t); + } + return entry; +} + +// Suspend +status_t +SizeIndex::Iterator::Suspend() +{ + status_t error = BaseClass::Suspend(); + if (error == B_OK) { + if (fNode) { + error = fIndex->GetVolume()->AddNodeListener(this, fNode, + NODE_LISTEN_REMOVED); + if (error == B_OK && fEntry) { + error = fIndex->GetVolume()->AddEntryListener(this, fEntry, + ENTRY_LISTEN_REMOVED); + if (error != B_OK) + fIndex->GetVolume()->RemoveNodeListener(this, fNode); + } + if (error != B_OK) + BaseClass::Resume(); + } + } + return error; +} + +// Resume +status_t +SizeIndex::Iterator::Resume() +{ + status_t error = BaseClass::Resume(); + if (error == B_OK) { + if (fEntry) + error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry); + if (fNode) { + if (error == B_OK) + error = fIndex->GetVolume()->RemoveNodeListener(this, fNode); + else + fIndex->GetVolume()->RemoveNodeListener(this, fNode); + } + } + return error; +} + +// SetTo +bool +SizeIndex::Iterator::SetTo(SizeIndex *index, off_t size, bool ignoreValue) +{ + Resume(); + Unset(); + // set the new values + fIndex = index; + if (fIndex) + fIndex->_AddIterator(this); + fInitialized = fIndex; + // get the node's first entry + if (fIndex) { + // get the first node + bool found = true; + if (ignoreValue) + fIndex->fNodes->GetIterator(&fIterator); + else + found = fIndex->fNodes->FindFirst(size, &fIterator); + // get the first entry + if (found) { + if (Node **nodeP = fIterator.GetCurrent()) { + fNode = *nodeP; + fEntry = fNode->GetFirstReferrer(); + if (!fEntry) + BaseClass::GetNext(); + if (!ignoreValue && fNode && fNode->GetSize() != size) + Unset(); + } + } + } + return fEntry; +} + +// Unset +void +SizeIndex::Iterator::Unset() +{ + if (fIndex) { + fIndex->_RemoveIterator(this); + fIndex = NULL; + } + BaseClass::Unset(); +} + +// EntryRemoved +void +SizeIndex::Iterator::EntryRemoved(Entry */*entry*/) +{ + Resume(); + fIsNext = BaseClass::GetNext(); + Suspend(); +} + +// NodeRemoved +void +SizeIndex::Iterator::NodeRemoved(Node */*node*/) +{ + Resume(); + fEntry = NULL; + fIsNext = BaseClass::GetNext(); + Suspend(); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/SizeIndex.h b/src/add-ons/kernel/file_systems/ramfs/SizeIndex.h new file mode 100644 index 0000000000..bc0560cd1d --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/SizeIndex.h @@ -0,0 +1,44 @@ +// SizeIndex.h + +#ifndef SIZE_INDEX_H +#define SIZE_INDEX_H + +#include "Index.h" +#include "NodeListener.h" +#include "TwoKeyAVLTree.h" + +// SizeIndex +class SizeIndex : public Index, private NodeListener { +public: + SizeIndex(Volume *volume); + virtual ~SizeIndex(); + + virtual int32 CountEntries() const; + + virtual status_t Changed(Node *node, off_t oldSize); + +private: + virtual void NodeAdded(Node *node); + virtual void NodeRemoved(Node *node); + +protected: + virtual AbstractIndexEntryIterator *InternalGetIterator(); + virtual AbstractIndexEntryIterator *InternalFind(const uint8 *key, + size_t length); + +private: + class Iterator; + class IteratorList; + class NodeTree; + friend class Iterator; + +private: + void _AddIterator(Iterator *iterator); + void _RemoveIterator(Iterator *iterator); + +private: + NodeTree *fNodes; + IteratorList *fIterators; +}; + +#endif // SIZE_INDEX_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Stack.h b/src/add-ons/kernel/file_systems/ramfs/Stack.h new file mode 100644 index 0000000000..dc832aabf1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Stack.h @@ -0,0 +1,78 @@ +/* Stack - a template stack class (plus some handy methods) + * + * Copyright 2001-2005, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ +#ifndef KERNEL_UTIL_STACK_H +#define KERNEL_UTIL_STACK_H + + +#include + + +template class Stack { + public: + Stack() + : + fArray(NULL), + fUsed(0), + fMax(0) + { + } + + ~Stack() + { + free(fArray); + } + + bool IsEmpty() const + { + return fUsed == 0; + } + + void MakeEmpty() + { + // could also free the memory + fUsed = 0; + } + + status_t Push(T value) + { + if (fUsed >= fMax) { + fMax += 16; + T *newArray = (T *)realloc(fArray, fMax * sizeof(T)); + if (newArray == NULL) + return B_NO_MEMORY; + + fArray = newArray; + } + fArray[fUsed++] = value; + return B_OK; + } + + bool Pop(T *value) + { + if (fUsed == 0) + return false; + + *value = fArray[--fUsed]; + return true; + } + + T *Array() + { + return fArray; + } + + int32 CountItems() const + { + return fUsed; + } + + private: + T *fArray; + int32 fUsed; + int32 fMax; +}; + +#endif /* KERNEL_UTIL_STACK_H */ diff --git a/src/add-ons/kernel/file_systems/ramfs/SymLink.cpp b/src/add-ons/kernel/file_systems/ramfs/SymLink.cpp new file mode 100644 index 0000000000..0fcae5ba23 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/SymLink.cpp @@ -0,0 +1,70 @@ +// SymLink.cpp + +#include + +#include "AllocationInfo.h" +#include "Debug.h" +#include "SizeIndex.h" +#include "SymLink.h" +#include "Volume.h" + +// constructor +SymLink::SymLink(Volume *volume) + : Node(volume, NODE_TYPE_SYMLINK), + fLinkedPath() +{ +} + +// destructor +SymLink::~SymLink() +{ +} + +// SetSize +status_t +SymLink::SetSize(off_t newSize) +{ + status_t error = (newSize >= 0 && newSize < PATH_MAX ? B_OK : B_BAD_VALUE); + int32 oldSize = GetLinkedPathLength(); + if (error == B_OK && newSize < oldSize) { + fLinkedPath.Truncate(newSize); + MarkModified(); + // update the size index + if (SizeIndex *index = GetVolume()->GetSizeIndex()) + index->Changed(this, oldSize); + } + return error; +} + +// GetSize +off_t +SymLink::GetSize() const +{ + return GetLinkedPathLength(); +} + +// SetLinkedPath +status_t +SymLink::SetLinkedPath(const char *path) +{ + int32 oldLen = GetLinkedPathLength(); + int32 len = strnlen(path, PATH_MAX - 1); + if (fLinkedPath.SetTo(path, len)) { + MarkModified(); + // update the size index, if necessary + if (len != oldLen) { + if (SizeIndex *index = GetVolume()->GetSizeIndex()) + index->Changed(this, oldLen); + } + return B_OK; + } + RETURN_ERROR(B_NO_MEMORY); +} + +// GetAllocationInfo +void +SymLink::GetAllocationInfo(AllocationInfo &info) +{ + info.AddSymLinkAllocation(GetSize()); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/SymLink.h b/src/add-ons/kernel/file_systems/ramfs/SymLink.h new file mode 100644 index 0000000000..d8565f8d23 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/SymLink.h @@ -0,0 +1,28 @@ +// SymLink.h + +#ifndef SYMLINK_H +#define SYMLINK_H + +#include "Node.h" +#include "String.h" + +class SymLink : public Node { +public: + SymLink(Volume *volume); + virtual ~SymLink(); + + virtual status_t SetSize(off_t newSize); + virtual off_t GetSize() const; + + status_t SetLinkedPath(const char *path); + const char *GetLinkedPath() const { return fLinkedPath.GetString(); } + size_t GetLinkedPathLength() const { return fLinkedPath.GetLength(); } + + // debugging + virtual void GetAllocationInfo(AllocationInfo &info); + +private: + String fLinkedPath; +}; + +#endif // SYMLINK_H diff --git a/src/add-ons/kernel/file_systems/ramfs/TwoKeyAVLTree.h b/src/add-ons/kernel/file_systems/ramfs/TwoKeyAVLTree.h new file mode 100644 index 0000000000..0407e37561 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/TwoKeyAVLTree.h @@ -0,0 +1,304 @@ +// TwoKeyAVLTree.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef TWO_KEY_AVL_TREE_H +#define TWO_KEY_AVL_TREE_H + +#include "AVLTree.h" + +// TwoKeyAVLTreeKey +template +class TwoKeyAVLTreeKey { +public: + inline TwoKeyAVLTreeKey(const PrimaryKey &primary, + const SecondaryKey &secondary) + : primary(primary), + secondary(secondary), + use_secondary(true) + { + } + + inline TwoKeyAVLTreeKey(const PrimaryKey *primary) + : primary(primary), + secondary(NULL), + use_secondary(false) + { + } + + PrimaryKey primary; + SecondaryKey secondary; + bool use_secondary; +}; + +// TwoKeyAVLTreeKeyCompare +template +class TwoKeyAVLTreeKeyCompare { +private: + typedef TwoKeyAVLTreeKey Key; + +public: + inline TwoKeyAVLTreeKeyCompare(const PrimaryKeyCompare &primary, + const SecondaryKeyCompare &secondary) + : fPrimaryKeyCompare(primary), fSecondaryKeyCompare(secondary) {} + + inline int operator()(const Key &a, const Key &b) const + { + int result = fPrimaryKeyCompare(a.primary, b.primary); + if (result == 0 && a.use_secondary && b.use_secondary) + result = fSecondaryKeyCompare(a.secondary, b.secondary); + return result; + } + +private: + PrimaryKeyCompare fPrimaryKeyCompare; + SecondaryKeyCompare fSecondaryKeyCompare; +}; + +// TwoKeyAVLTreeGetKey +template +class TwoKeyAVLTreeGetKey +{ +private: + typedef TwoKeyAVLTreeKey Key; + +public: + TwoKeyAVLTreeGetKey(const GetPrimaryKey &getPrimary, + const GetSecondaryKey &getSecondary) + : fGetPrimaryKey(getPrimary), + fGetSecondaryKey(getSecondary) + { + } + + inline Key operator()(const Value &a) const + { + return Key(fGetPrimaryKey(a), fGetSecondaryKey(a)); + } + +private: + GetPrimaryKey fGetPrimaryKey; + GetSecondaryKey fGetSecondaryKey; +}; + +// for convenience +#define TWO_KEY_AVL_TREE_TEMPLATE_LIST template +#define TWO_KEY_AVL_TREE_CLASS_NAME TwoKeyAVLTree + +// TwoKeyAVLTree +template, + typename SecondaryKeyCompare = AVLTreeStandardCompare, + typename GetSecondaryKey = AVLTreeStandardGetKey, + typename NodeAllocator = AVLTreeStandardNodeAllocator, + typename GetValue = AVLTreeStandardGetValue > +class TwoKeyAVLTree : private AVLTree, Node, + TwoKeyAVLTreeKeyCompare, + TwoKeyAVLTreeGetKey, + NodeAllocator, GetValue> { +private: + typedef TwoKeyAVLTreeKey Key; + typedef TwoKeyAVLTreeKeyCompare + KeyCompare; + typedef TwoKeyAVLTreeGetKey + GetKey; + typedef AVLTree BaseClass; + +public: + TwoKeyAVLTree(); + TwoKeyAVLTree(const PrimaryKeyCompare &primaryCompare, + const GetPrimaryKey &getPrimary, + const SecondaryKeyCompare &secondaryCompare, + const GetSecondaryKey &getSecondary, + const NodeAllocator &allocator, + const GetValue &getValue); + ~TwoKeyAVLTree(); + + inline int CountItems() const { return BaseClass::CountItems(); } + + Value *FindFirst(const PrimaryKey &key, Iterator *iterator = NULL); + Value *FindLast(const PrimaryKey &key, Iterator *iterator = NULL); + inline Value *Find(const PrimaryKey &primaryKey, + const SecondaryKey &secondaryKey, + Iterator *iterator = NULL); + + inline void GetIterator(Iterator *iterator, bool reverse = false); + + inline status_t Insert(const Value &value, Iterator *iterator = NULL); + inline status_t Remove(const PrimaryKey &primaryKey, + const SecondaryKey &secondaryKey); + inline void Remove(Iterator &iterator); + +private: + PrimaryKeyCompare fPrimaryKeyCompare; + GetPrimaryKey fGetPrimaryKey; +}; + + +// constructor +TWO_KEY_AVL_TREE_TEMPLATE_LIST +TWO_KEY_AVL_TREE_CLASS_NAME::TwoKeyAVLTree() + : BaseClass(KeyCompare(PrimaryKeyCompare(), SecondaryKeyCompare()), + GetKey(GetPrimaryKey(), GetSecondaryKey()), + NodeAllocator(), GetValue()) +{ +} + +// constructor +TWO_KEY_AVL_TREE_TEMPLATE_LIST +TWO_KEY_AVL_TREE_CLASS_NAME::TwoKeyAVLTree( + const PrimaryKeyCompare &primaryCompare, const GetPrimaryKey &getPrimary, + const SecondaryKeyCompare &secondaryCompare, + const GetSecondaryKey &getSecondary, const NodeAllocator &allocator, + const GetValue &getValue) + : BaseClass(KeyCompare(primaryCompare, secondaryCompare), + GetKey(getPrimary, getSecondary), + allocator, getValue), + fPrimaryKeyCompare(primaryCompare), + fGetPrimaryKey(getPrimary) + +{ +} + +// destructor +TWO_KEY_AVL_TREE_TEMPLATE_LIST +TWO_KEY_AVL_TREE_CLASS_NAME::~TwoKeyAVLTree() +{ +} + +// FindFirst +TWO_KEY_AVL_TREE_TEMPLATE_LIST +Value * +TWO_KEY_AVL_TREE_CLASS_NAME::FindFirst(const PrimaryKey &key, + Iterator *iterator) +{ + Node *node = fRoot; + while (node) { + int cmp = fPrimaryKeyCompare(key, fGetPrimaryKey(fGetValue(node))); + if (cmp == 0) { + // found a matching node, now get the left-most node with that key + while (node->left && fPrimaryKeyCompare(key, + fGetPrimaryKey(fGetValue(node->left))) == 0) { + node = node->left; + } + if (iterator) + _InitIterator(iterator, node); + return &fGetValue(node); + } + if (cmp < 0) + node = node->left; + else + node = node->right; + } + return NULL; +} + +// FindLast +TWO_KEY_AVL_TREE_TEMPLATE_LIST +Value * +TWO_KEY_AVL_TREE_CLASS_NAME::FindLast(const PrimaryKey &key, + Iterator *iterator) +{ + Node *node = fRoot; + while (node) { + int cmp = fPrimaryKeyCompare(key, fGetPrimaryKey(fGetValue(node))); + if (cmp == 0) { + // found a matching node, now get the right-most node with that key + while (node->right && fPrimaryKeyCompare(key, + fGetPrimaryKey(fGetValue(node->right))) == 0) { + node = node->right; + } + if (iterator) + _InitIterator(iterator, node); + return &fGetValue(node); + } + if (cmp < 0) + node = node->left; + else + node = node->right; + } + return NULL; +} + +// Find +TWO_KEY_AVL_TREE_TEMPLATE_LIST +Value * +TWO_KEY_AVL_TREE_CLASS_NAME::Find(const PrimaryKey &primaryKey, + const SecondaryKey &secondaryKey, + Iterator *iterator) +{ + return BaseClass::Find(Key(primaryKey, secondaryKey), iterator); +} + +// GetIterator +TWO_KEY_AVL_TREE_TEMPLATE_LIST +void +TWO_KEY_AVL_TREE_CLASS_NAME::GetIterator(Iterator *iterator, bool reverse) +{ + BaseClass::GetIterator(iterator, reverse); +} + +// Insert +TWO_KEY_AVL_TREE_TEMPLATE_LIST +status_t +TWO_KEY_AVL_TREE_CLASS_NAME::Insert(const Value &value, Iterator *iterator) +{ + return BaseClass::Insert(value, iterator); +} + +// Remove +TWO_KEY_AVL_TREE_TEMPLATE_LIST +status_t +TWO_KEY_AVL_TREE_CLASS_NAME::Remove(const PrimaryKey &primaryKey, + const SecondaryKey &secondaryKey) +{ + return BaseClass::Remove(Key(primaryKey, secondaryKey)); +} + +// Remove +TWO_KEY_AVL_TREE_TEMPLATE_LIST +void +TWO_KEY_AVL_TREE_CLASS_NAME::Remove(Iterator &iterator) +{ + BaseClass::Remove(iterator); +} + +#endif // TWO_KEY_AVL_TREE_H diff --git a/src/add-ons/kernel/file_systems/ramfs/Volume.cpp b/src/add-ons/kernel/file_systems/ramfs/Volume.cpp new file mode 100644 index 0000000000..49741159a7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Volume.cpp @@ -0,0 +1,895 @@ +// Volume.cpp +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can alternatively use *this file* under the terms of the the MIT +// license included in this package. + +#include +#include +#include +#include +#include +#include + +#include "Block.h" +#include "BlockAllocator.h" +#include "Debug.h" +#include "Directory.h" +#include "Entry.h" +#include "EntryListener.h" +#include "IndexDirectory.h" +#include "Locking.h" +#include "Misc.h" +#include "NameIndex.h" +#include "Node.h" +#include "NodeChildTable.h" +#include "NodeListener.h" +#include "NodeTable.h" +#include "TwoKeyAVLTree.h" +#include "Volume.h" + +// default block size +static const off_t kDefaultBlockSize = 4096; + +static const size_t kDefaultAreaSize = kDefaultBlockSize * 128; + +// default volume name +static const char *kDefaultVolumeName = "RAM FS"; + +// NodeListenerGetPrimaryKey +class NodeListenerGetPrimaryKey { +public: + inline Node *operator()(const NodeListenerValue &a) + { + return a.node; + } + + inline Node *operator()(const NodeListenerValue &a) const + { + return a.node; + } +}; + +// NodeListenerGetSecondaryKey +class NodeListenerGetSecondaryKey { +public: + inline NodeListener *operator()(const NodeListenerValue &a) + { + return a.listener; + } + + inline NodeListener *operator()(const NodeListenerValue &a) const + { + return a.listener; + } +}; + +// NodeListenerTree +typedef TwoKeyAVLTree, + NodeListenerGetPrimaryKey, NodeListener*, + AVLTreeStandardNode, + AVLTreeStandardCompare, + NodeListenerGetSecondaryKey > _NodeListenerTree; +class NodeListenerTree : public _NodeListenerTree {}; + +// EntryListenerGetPrimaryKey +class EntryListenerGetPrimaryKey { +public: + inline Entry *operator()(const EntryListenerValue &a) + { + return a.entry; + } + + inline Entry *operator()(const EntryListenerValue &a) const + { + return a.entry; + } +}; + +// EntryListenerGetSecondaryKey +class EntryListenerGetSecondaryKey { +public: + inline EntryListener *operator()(const EntryListenerValue &a) + { + return a.listener; + } + + inline EntryListener *operator()(const EntryListenerValue &a) const + { + return a.listener; + } +}; + +// EntryListenerTree +typedef TwoKeyAVLTree, + EntryListenerGetPrimaryKey, EntryListener*, + AVLTreeStandardNode, + AVLTreeStandardCompare, + EntryListenerGetSecondaryKey > _EntryListenerTree; +class EntryListenerTree : public _EntryListenerTree {}; + + +/*! + \class Volume + \brief Represents a volume. +*/ + +// constructor +Volume::Volume() + : fID(0), + fNextNodeID(kRootParentID + 1), + fNodeTable(NULL), + fDirectoryEntryTable(NULL), + fNodeAttributeTable(NULL), + fIndexDirectory(NULL), + fRootDirectory(NULL), + fName(kDefaultVolumeName), + fLocker("volume"), + fIteratorLocker("iterators"), + fQueryLocker("queries"), + fNodeListeners(NULL), + fAnyNodeListeners(), + fEntryListeners(NULL), + fAnyEntryListeners(), + fBlockAllocator(NULL), + fBlockSize(kDefaultBlockSize), + fAllocatedBlocks(0), + fAccessTime(0), + fMounted(false) +{ +} + +// destructor +Volume::~Volume() +{ + Unmount(); +} + +// Mount +status_t +Volume::Mount(nspace_id id) +{ + Unmount(); + + // check the locker's semaphores + if (fLocker.Sem() < 0) + return fLocker.Sem(); + if (fIteratorLocker.Sem() < 0) + return fIteratorLocker.Sem(); + if (fQueryLocker.Sem() < 0) + return fQueryLocker.Sem(); + + status_t error = B_OK; + fID = id; + // create a block allocator + if (error == B_OK) { + fBlockAllocator = new(nothrow) BlockAllocator(kDefaultAreaSize); + if (fBlockAllocator) + error = fBlockAllocator->InitCheck(); + else + SET_ERROR(error, B_NO_MEMORY); + } + // create the listener trees + if (error == B_OK) { + fNodeListeners = new(nothrow) NodeListenerTree; + if (!fNodeListeners) + error = B_NO_MEMORY; + } + if (error == B_OK) { + fEntryListeners = new(nothrow) EntryListenerTree; + if (!fEntryListeners) + error = B_NO_MEMORY; + } + // create the node table + if (error == B_OK) { + fNodeTable = new(nothrow) NodeTable; + if (fNodeTable) + error = fNodeTable->InitCheck(); + else + SET_ERROR(error, B_NO_MEMORY); + } + // create the directory entry table + if (error == B_OK) { + fDirectoryEntryTable = new(nothrow) DirectoryEntryTable; + if (fDirectoryEntryTable) + error = fDirectoryEntryTable->InitCheck(); + else + SET_ERROR(error, B_NO_MEMORY); + } + // create the node attribute table + if (error == B_OK) { + fNodeAttributeTable = new(nothrow) NodeAttributeTable; + if (fNodeAttributeTable) + error = fNodeAttributeTable->InitCheck(); + else + SET_ERROR(error, B_NO_MEMORY); + } + // create the index directory + if (error == B_OK) { + fIndexDirectory = new(nothrow) IndexDirectory(this); + if (!fIndexDirectory) + SET_ERROR(error, B_NO_MEMORY); + } + // create the root dir + if (error == B_OK) { + fRootDirectory = new(nothrow) Directory(this); + if (fRootDirectory) { + // set permissions: -rwxr-xr-x + fRootDirectory->SetMode( + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + error = fRootDirectory->Link(NULL); + } else + SET_ERROR(error, B_NO_MEMORY); + } + // set mounted flag / cleanup on error + if (error == B_OK) + fMounted = true; + else + Unmount(); + RETURN_ERROR(error); +} + +// Unmount +status_t +Volume::Unmount() +{ + fMounted = false; + // delete the root directory + if (fRootDirectory) { + // deleting the root directory destroys the complete hierarchy + delete fRootDirectory; + fRootDirectory = NULL; + } + // delete the index directory + if (fIndexDirectory) { + delete fIndexDirectory; + fIndexDirectory = NULL; + } + // delete the listener trees + if (fEntryListeners) { + delete fEntryListeners; + fEntryListeners = NULL; + } + if (fNodeListeners) { + delete fNodeListeners; + fNodeListeners = NULL; + } + // delete the tables + if (fNodeAttributeTable) { + delete fNodeAttributeTable; + fNodeAttributeTable = NULL; + } + if (fDirectoryEntryTable) { + delete fDirectoryEntryTable; + fDirectoryEntryTable = NULL; + } + if (fNodeTable) { + delete fNodeTable; + fNodeTable = NULL; + } + // delete the block allocator + if (fBlockAllocator) { + delete fBlockAllocator; + fBlockAllocator = NULL; + } + fID = 0; + return B_OK; +} + +// GetBlockSize +off_t +Volume::GetBlockSize() const +{ + return fBlockSize; +} + +// CountBlocks +off_t +Volume::CountBlocks() const +{ + size_t bytes = 0; + system_info sysInfo; + if (get_system_info(&sysInfo) == B_OK) { + int32 freePages = sysInfo.max_pages - sysInfo.used_pages; + bytes = (uint32)freePages * B_PAGE_SIZE + + fBlockAllocator->GetAvailableBytes(); + } + return bytes / kDefaultBlockSize; +} + +// CountFreeBlocks +off_t +Volume::CountFreeBlocks() const +{ + // TODO:... + return CountBlocks() - fBlockAllocator->GetUsedBytes() / kDefaultBlockSize; +} + +// SetName +status_t +Volume::SetName(const char *name) +{ + status_t error = (name ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + if (!fName.SetTo(name)) + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// GetName +const char * +Volume::GetName() const +{ + return fName.GetString(); +} + +// NewVNode +status_t +Volume::NewVNode(Node *node) +{ + status_t error = NodeAdded(node); + if (error == B_OK) { + error = new_vnode(GetID(), node->GetID(), node); + if (error != B_OK) + NodeRemoved(node); + } + return error; +} + +// GetVNode +status_t +Volume::GetVNode(vnode_id id, Node **node) +{ + return (fMounted ? get_vnode(GetID(), id, (void**)node) : B_BAD_VALUE); +} + +// GetVNode +status_t +Volume::GetVNode(Node *node) +{ + Node *dummy = NULL; + status_t error = (fMounted ? GetVNode(node->GetID(), &dummy) + : B_BAD_VALUE ); + if (error == B_OK && dummy != node) { + FATAL(("Two Nodes have the same ID: %Ld!\n", node->GetID())); + PutVNode(dummy); + error = B_ERROR; + } + return error; +} + +// PutVNode +status_t +Volume::PutVNode(vnode_id id) +{ + return (fMounted ? put_vnode(GetID(), id) : B_BAD_VALUE); +} + +// PutVNode +status_t +Volume::PutVNode(Node *node) +{ + return (fMounted ? put_vnode(GetID(), node->GetID()) : B_BAD_VALUE); +} + +// RemoveVNode +status_t +Volume::RemoveVNode(Node *node) +{ + if (fMounted) + return remove_vnode(GetID(), node->GetID()); + status_t error = NodeRemoved(node); + if (error == B_OK) + delete node; + return error; +} + +// UnremoveVNode +status_t +Volume::UnremoveVNode(Node *node) +{ + return (fMounted ? unremove_vnode(GetID(), node->GetID()) : B_BAD_VALUE); +} + +// NodeAdded +status_t +Volume::NodeAdded(Node *node) +{ + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + error = fNodeTable->AddNode(node); + // notify listeners + if (error == B_OK) { + // listeners interested in that node + NodeListenerTree::Iterator it; + if (fNodeListeners->FindFirst(node, &it)) { + for (NodeListenerValue *value = it.GetCurrent(); + value && value->node == node; + value = it.GetNext()) { + if (value->flags & NODE_LISTEN_ADDED) + value->listener->NodeAdded(node); + } + } + // listeners interested in any node + int32 count = fAnyNodeListeners.CountItems(); + for (int32 i = 0; i < count; i++) { + const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i); + if (value.flags & NODE_LISTEN_ADDED) + value.listener->NodeAdded(node); + } + } + } + return error; +} + +// NodeRemoved +status_t +Volume::NodeRemoved(Node *node) +{ + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + error = fNodeTable->RemoveNode(node); + // notify listeners + if (error == B_OK) { + // listeners interested in that node + NodeListenerTree::Iterator it; + if (fNodeListeners->FindFirst(node, &it)) { + for (NodeListenerValue *value = it.GetCurrent(); + value && value->node == node; + value = it.GetNext()) { + if (value->flags & NODE_LISTEN_REMOVED) + value->listener->NodeRemoved(node); + } + } + // listeners interested in any node + int32 count = fAnyNodeListeners.CountItems(); + for (int32 i = 0; i < count; i++) { + const NodeListenerValue &value = fAnyNodeListeners.ItemAt(i); + if (value.flags & NODE_LISTEN_REMOVED) + value.listener->NodeRemoved(node); + } + } + } + return error; +} + +// FindNode +/*! \brief Finds the node identified by a vnode_id. + + \note The method does not initialize the parent ID for non-directory nodes. + + \param id ID of the node to be found. + \param node pointer to a pre-allocated Node* to be set to the found node. + \return \c B_OK, if everything went fine. +*/ +status_t +Volume::FindNode(vnode_id id, Node **node) +{ + status_t error = (node ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + *node = fNodeTable->GetNode(id); + if (!*node) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// AddNodeListener +status_t +Volume::AddNodeListener(NodeListener *listener, Node *node, uint32 flags) +{ + // check parameters + if (!listener || !node && !(flags & NODE_LISTEN_ANY_NODE) + || !(flags & NODE_LISTEN_ALL)) { + return B_BAD_VALUE; + } + // add the listener to the right container + status_t error = B_OK; + NodeListenerValue value(listener, node, flags); + if (flags & NODE_LISTEN_ANY_NODE) { + if (!fAnyNodeListeners.AddItem(value)) + error = B_NO_MEMORY; + } else + error = fNodeListeners->Insert(value); + return error; +} + +// RemoveNodeListener +status_t +Volume::RemoveNodeListener(NodeListener *listener, Node *node) +{ + if (!listener) + return B_BAD_VALUE; + status_t error = B_OK; + if (node) + error = fNodeListeners->Remove(node, listener); + else { + NodeListenerValue value(listener, node, 0); + if (!fAnyNodeListeners.RemoveItem(value)) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// EntryAdded +status_t +Volume::EntryAdded(vnode_id id, Entry *entry) +{ + status_t error = (entry ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + error = fDirectoryEntryTable->AddNodeChild(id, entry); + if (error == B_OK) { + // notify listeners + // listeners interested in that entry + EntryListenerTree::Iterator it; + if (fEntryListeners->FindFirst(entry, &it)) { + for (EntryListenerValue *value = it.GetCurrent(); + value && value->entry == entry; + value = it.GetNext()) { + if (value->flags & ENTRY_LISTEN_ADDED) + value->listener->EntryAdded(entry); + } + } + // listeners interested in any entry + int32 count = fAnyEntryListeners.CountItems(); + for (int32 i = 0; i < count; i++) { + const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i); + if (value.flags & ENTRY_LISTEN_ADDED) + value.listener->EntryAdded(entry); + } + } + } + return error; +} + +// EntryRemoved +status_t +Volume::EntryRemoved(vnode_id id, Entry *entry) +{ + status_t error = (entry ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + error = fDirectoryEntryTable->RemoveNodeChild(id, entry); + if (error == B_OK) { + // notify listeners + // listeners interested in that entry + EntryListenerTree::Iterator it; + if (fEntryListeners->FindFirst(entry, &it)) { + for (EntryListenerValue *value = it.GetCurrent(); + value && value->entry == entry; + value = it.GetNext()) { + if (value->flags & ENTRY_LISTEN_REMOVED) + value->listener->EntryRemoved(entry); + } + } + // listeners interested in any entry + int32 count = fAnyEntryListeners.CountItems(); + for (int32 i = 0; i < count; i++) { + const EntryListenerValue &value = fAnyEntryListeners.ItemAt(i); + if (value.flags & ENTRY_LISTEN_REMOVED) + value.listener->EntryRemoved(entry); + } + } + } + return error; +} + +// FindEntry +status_t +Volume::FindEntry(vnode_id id, const char *name, Entry **entry) +{ + status_t error = (entry ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + *entry = fDirectoryEntryTable->GetNodeChild(id, name); + if (!*entry) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// AddEntryListener +status_t +Volume::AddEntryListener(EntryListener *listener, Entry *entry, uint32 flags) +{ + // check parameters + if (!listener || !entry && !(flags & ENTRY_LISTEN_ANY_ENTRY) + || !(flags & ENTRY_LISTEN_ALL)) { + return B_BAD_VALUE; + } + // add the listener to the right container + status_t error = B_OK; + EntryListenerValue value(listener, entry, flags); + if (flags & ENTRY_LISTEN_ANY_ENTRY) { + if (!fAnyEntryListeners.AddItem(value)) + error = B_NO_MEMORY; + } else + error = fEntryListeners->Insert(value); + return error; +} + +// RemoveEntryListener +status_t +Volume::RemoveEntryListener(EntryListener *listener, Entry *entry) +{ + if (!listener) + return B_BAD_VALUE; + status_t error = B_OK; + if (entry) + error = fEntryListeners->Remove(entry, listener); + else { + EntryListenerValue value(listener, entry, 0); + if (!fAnyEntryListeners.RemoveItem(value)) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// NodeAttributeAdded +status_t +Volume::NodeAttributeAdded(vnode_id id, Attribute *attribute) +{ + status_t error = (attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + error = fNodeAttributeTable->AddNodeChild(id, attribute); + // notify the respective attribute index + if (error == B_OK) { + if (AttributeIndex *index = FindAttributeIndex( + attribute->GetName(), attribute->GetType())) { + index->Added(attribute); + } + } + } + return error; +} + +// NodeAttributeRemoved +status_t +Volume::NodeAttributeRemoved(vnode_id id, Attribute *attribute) +{ + status_t error = (attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + error = fNodeAttributeTable->RemoveNodeChild(id, attribute); + // notify the respective attribute index + if (error == B_OK) { + if (AttributeIndex *index = FindAttributeIndex( + attribute->GetName(), attribute->GetType())) { + index->Removed(attribute); + } + } + + // update live queries + if (error == B_OK && attribute->GetNode()) { + const uint8* oldKey; + size_t oldLength; + attribute->GetKey(&oldKey, &oldLength); + UpdateLiveQueries(NULL, attribute->GetNode(), attribute->GetName(), + attribute->GetType(), oldKey, oldLength, NULL, 0); + } + } + return error; +} + +// FindNodeAttribute +status_t +Volume::FindNodeAttribute(vnode_id id, const char *name, Attribute **attribute) +{ + status_t error = (attribute ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + *attribute = fNodeAttributeTable->GetNodeChild(id, name); + if (!*attribute) + error = B_ENTRY_NOT_FOUND; + } + return error; +} + +// GetNameIndex +NameIndex * +Volume::GetNameIndex() const +{ + return (fIndexDirectory ? fIndexDirectory->GetNameIndex() : NULL); +} + +// GetLastModifiedIndex +LastModifiedIndex * +Volume::GetLastModifiedIndex() const +{ + return (fIndexDirectory ? fIndexDirectory->GetLastModifiedIndex() : NULL); +} + +// GetSizeIndex +SizeIndex * +Volume::GetSizeIndex() const +{ + return (fIndexDirectory ? fIndexDirectory->GetSizeIndex() : NULL); +} + +// FindIndex +Index * +Volume::FindIndex(const char *name) +{ + return (fIndexDirectory ? fIndexDirectory->FindIndex(name) : NULL); +} + +// FindAttributeIndex +AttributeIndex * +Volume::FindAttributeIndex(const char *name, uint32 type) +{ + return (fIndexDirectory + ? fIndexDirectory->FindAttributeIndex(name, type) : NULL); +} + +// AddQuery +void +Volume::AddQuery(Query *query) +{ + AutoLocker _(fQueryLocker); + + if (query) + fQueries.Insert(query); +} + +// RemoveQuery +void +Volume::RemoveQuery(Query *query) +{ + AutoLocker _(fQueryLocker); + + if (query) + fQueries.Remove(query); +} + +// UpdateLiveQueries +void +Volume::UpdateLiveQueries(Entry *entry, Node* node, const char *attribute, + int32 type, const uint8 *oldKey, size_t oldLength, const uint8 *newKey, + size_t newLength) +{ + AutoLocker _(fQueryLocker); + + for (Query* query = fQueries.GetFirst(); + query; + query = fQueries.GetNext(query)) { + query->LiveUpdate(entry, node, attribute, type, oldKey, oldLength, + newKey, newLength); + } +} + +// AllocateBlock +status_t +Volume::AllocateBlock(size_t size, BlockReference **block) +{ + status_t error = (size > 0 && size <= fBlockSize && block + ? B_OK : B_BAD_VALUE); + if (error == B_OK) { + *block = fBlockAllocator->AllocateBlock(size); + if (*block) + fAllocatedBlocks++; + else + SET_ERROR(error, B_NO_MEMORY); + } + return error; +} + +// FreeBlock +void +Volume::FreeBlock(BlockReference *block) +{ + if (block) { + fBlockAllocator->FreeBlock(block); + fAllocatedBlocks--; + } +} + +// ResizeBlock +BlockReference * +Volume::ResizeBlock(BlockReference *block, size_t size) +{ + BlockReference *newBlock = NULL; + if (size <= fBlockSize && block) { + if (size == 0) { + fBlockAllocator->FreeBlock(block); + fAllocatedBlocks--; + } else + newBlock = fBlockAllocator->ResizeBlock(block, size); + } + return newBlock; +} + +// CheckBlock +bool +Volume::CheckBlock(BlockReference *block, size_t size) +{ + return fBlockAllocator->CheckBlock(block, size); +} + +// GetAllocationInfo +void +Volume::GetAllocationInfo(AllocationInfo &info) +{ + // tables + info.AddOtherAllocation(sizeof(NodeTable)); + fNodeTable->GetAllocationInfo(info); + info.AddOtherAllocation(sizeof(DirectoryEntryTable)); + fDirectoryEntryTable->GetAllocationInfo(info); + info.AddOtherAllocation(sizeof(NodeAttributeTable)); + fNodeAttributeTable->GetAllocationInfo(info); + // node hierarchy + fRootDirectory->GetAllocationInfo(info); + // name + info.AddStringAllocation(fName.GetLength()); + // block allocator + info.AddOtherAllocation(sizeof(BlockAllocator)); + fBlockAllocator->GetAllocationInfo(info); +} + +// ReadLock +bool +Volume::ReadLock() +{ + bool alreadyLocked = fLocker.IsLocked(); + if (fLocker.Lock()) { + if (!alreadyLocked) + fAccessTime = system_time(); + return true; + } + return false; +} + +// ReadUnlock +void +Volume::ReadUnlock() +{ + fLocker.Unlock(); +} + +// WriteLock +bool +Volume::WriteLock() +{ + bool alreadyLocked = fLocker.IsLocked(); + if (fLocker.Lock()) { + if (!alreadyLocked) + fAccessTime = system_time(); + return true; + } + return false; +} + +// WriteUnlock +void +Volume::WriteUnlock() +{ + fLocker.Unlock(); +} + +// IteratorLock +bool +Volume::IteratorLock() +{ + return fIteratorLocker.Lock(); +} + +// IteratorUnlock +void +Volume::IteratorUnlock() +{ + fIteratorLocker.Unlock(); +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/Volume.h b/src/add-ons/kernel/file_systems/ramfs/Volume.h new file mode 100644 index 0000000000..b7c7f4005f --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/Volume.h @@ -0,0 +1,201 @@ +// Volume.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can alternatively use *this file* under the terms of the the MIT +// license included in this package. + +#ifndef VOLUME_H +#define VOLUME_H + +#include +#include + +#include "DLList.h" +#include "Entry.h" +#include "List.h" +#include "Locker.h" +#include "Query.h" +#include "String.h" + +class AllocationInfo; +class Block; +class BlockAllocator; +class BlockReference; +class Directory; +class DirectoryEntryTable; +class Entry; +class EntryListener; +class EntryListenerTree; +class Index; +class IndexDirectory; +class LastModifiedIndex; +class NameIndex; +class Node; +class NodeAttributeTable; +class NodeListener; +class NodeListenerTree; +class NodeTable; +class SizeIndex; + +const vnode_id kRootParentID = 0; + +// NodeListenerValue +class NodeListenerValue { +public: + inline NodeListenerValue(int) {} + inline NodeListenerValue(NodeListener *listener, Node *node, uint32 flags) + : listener(listener), node(node), flags(flags) {} + + inline bool operator==(const NodeListenerValue &other) + { return listener == other.listener; } + + NodeListener *listener; + Node *node; + uint32 flags; +}; +typedef List NodeListenerList; + +// EntryListenerValue +class EntryListenerValue { +public: + inline EntryListenerValue(int) {} + inline EntryListenerValue(EntryListener *listener, Entry *entry, + uint32 flags) + : listener(listener), entry(entry), flags(flags) {} + + inline bool operator==(const EntryListenerValue &other) + { return listener == other.listener; } + + EntryListener *listener; + Entry *entry; + uint32 flags; +}; +typedef List EntryListenerList; + +// Volume +class Volume { +public: + Volume(); + ~Volume(); + + status_t Mount(nspace_id nsid); + status_t Unmount(); + + nspace_id GetID() const { return fID; } + + off_t GetBlockSize() const; + off_t CountBlocks() const; + off_t CountFreeBlocks() const; + + status_t SetName(const char *name); + const char *GetName() const; + + Directory *GetRootDirectory() const { return fRootDirectory; } + + status_t NewVNode(Node *node); + status_t GetVNode(vnode_id id, Node **node); + status_t GetVNode(Node *node); + status_t PutVNode(vnode_id id); + status_t PutVNode(Node *node); + status_t RemoveVNode(Node *node); + status_t UnremoveVNode(Node *node); + + // node table and listeners + status_t NodeAdded(Node *node); + status_t NodeRemoved(Node *node); + status_t FindNode(vnode_id id, Node **node); + status_t AddNodeListener(NodeListener *listener, Node *node, + uint32 flags); + status_t RemoveNodeListener(NodeListener *listener, Node *node); + + // entry table and listeners + status_t EntryAdded(vnode_id id, Entry *entry); + status_t EntryRemoved(vnode_id id, Entry *entry); + status_t FindEntry(vnode_id id, const char *name, Entry **entry); + status_t AddEntryListener(EntryListener *listener, Entry *entry, + uint32 flags); + status_t RemoveEntryListener(EntryListener *listener, Entry *entry); + + // node attribute table + status_t NodeAttributeAdded(vnode_id id, Attribute *attribute); + status_t NodeAttributeRemoved(vnode_id id, Attribute *attribute); + status_t FindNodeAttribute(vnode_id id, const char *name, + Attribute **attribute); + + // indices + IndexDirectory *GetIndexDirectory() const { return fIndexDirectory; } + NameIndex *GetNameIndex() const; + LastModifiedIndex *GetLastModifiedIndex() const; + SizeIndex *GetSizeIndex() const; + Index *FindIndex(const char *name); + AttributeIndex *FindAttributeIndex(const char *name, uint32 type); + + // queries + void AddQuery(Query *query); + void RemoveQuery(Query *query); + void UpdateLiveQueries(Entry *entry, Node* node, const char *attribute, + int32 type, const uint8 *oldKey, size_t oldLength, + const uint8 *newKey, size_t newLength); + + vnode_id NextNodeID() { return fNextNodeID++; } + + status_t AllocateBlock(size_t size, BlockReference **block); + void FreeBlock(BlockReference *block); + BlockReference *ResizeBlock(BlockReference *block, size_t size); + // debugging only + bool CheckBlock(BlockReference *block, size_t size = 0); + void GetAllocationInfo(AllocationInfo &info); + + bigtime_t GetAccessTime() const { return fAccessTime; } + + // locking + bool ReadLock(); + void ReadUnlock(); + bool WriteLock(); + void WriteUnlock(); + + bool IteratorLock(); + void IteratorUnlock(); + +private: + typedef DLList QueryList; + + nspace_id fID; + vnode_id fNextNodeID; + NodeTable *fNodeTable; + DirectoryEntryTable *fDirectoryEntryTable; + NodeAttributeTable *fNodeAttributeTable; + IndexDirectory *fIndexDirectory; + Directory *fRootDirectory; + String fName; + Locker fLocker; + Locker fIteratorLocker; + Locker fQueryLocker; + NodeListenerTree *fNodeListeners; + NodeListenerList fAnyNodeListeners; + EntryListenerTree *fEntryListeners; + EntryListenerList fAnyEntryListeners; + QueryList fQueries; + BlockAllocator *fBlockAllocator; + off_t fBlockSize; + off_t fAllocatedBlocks; + bigtime_t fAccessTime; + bool fMounted; +}; + +#endif // VOLUME_H diff --git a/src/add-ons/kernel/file_systems/ramfs/cpp.cpp b/src/add-ons/kernel/file_systems/ramfs/cpp.cpp new file mode 100644 index 0000000000..32aaa3d48d --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/cpp.cpp @@ -0,0 +1,21 @@ +/* cpp - C++ in the kernel +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "cpp.h" + + +//const struct nothrow_t nothrow = {}; + +//extern "C" void __pure_virtual() +//{ + //printf("pure virtual function call"); +//} + +int stderr; + +extern "C" int fprintf() { return 0; } +extern "C" void abort() {} diff --git a/src/add-ons/kernel/file_systems/ramfs/cpp.h b/src/add-ons/kernel/file_systems/ramfs/cpp.h new file mode 100644 index 0000000000..92b770caef --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/cpp.h @@ -0,0 +1,51 @@ +#ifndef CPP_H +#define CPP_H +/* cpp - C++ in the kernel +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + +#ifdef __cplusplus + +#include +#include + + +// Oh no! C++ in the kernel! Are you nuts? +// +// - no exceptions +// - (almost) no virtuals (well, the Query code now uses them) +// - it's basically only the C++ syntax, and type checking +// - since one tend to encapsulate everything in classes, it has a slightly +// higher memory overhead +// - nicer code +// - easier to maintain + + +inline void *operator new(size_t size, const nothrow_t&) throw() +{ + return malloc(size); +} + +inline void *operator new[](size_t size, const nothrow_t&) throw() +{ + return malloc(size); +} + +inline void operator delete(void *ptr) +{ + free(ptr); +} + +inline void operator delete[](void *ptr) +{ + free(ptr); +} + +// now we're using virtuals +extern "C" void __pure_virtual(); + +#endif // __cplusplus + +#endif /* CPP_H */ diff --git a/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp new file mode 100644 index 0000000000..149cf7f206 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp @@ -0,0 +1,2077 @@ +// kernel_interface.cpp +// +// Copyright (c) 2003, Axel Dörfler (axeld@pinc-software.de) +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can alternatively use *this file* under the terms of the the MIT +// license included in this package. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//#include "lock.h" +//#include "cache.h" +#include "fsproto.h" + +#include "AllocationInfo.h" +#include "AttributeIndex.h" +#include "AttributeIterator.h" +#include "AutoDeleter.h" +#include "Debug.h" +#include "Directory.h" +#include "Entry.h" +#include "EntryIterator.h" +#include "File.h" +#include "Index.h" +#include "IndexDirectory.h" +#include "Locking.h" +#include "Misc.h" +#include "Node.h" +#include "Query.h" +#include "ramfs_ioctl.h" +#include "SymLink.h" +#include "Volume.h" + +// BFS returns the length of the entry name in dirent::d_reclen. This is +// not correct, since this field should be set to the length of the complete +// dirent. If set to != 0, KEEP_WRONG_DIRENT_RECLEN emulates the buggy +// bahavior. +#ifndef KEEP_WRONG_DIRENT_RECLEN +#define KEEP_WRONG_DIRENT_RECLEN 0 +#endif + +extern "C" { + +static int ramfs_mount(nspace_id nsid, const char *device, ulong flags, + void *parameters, size_t len, void **data, + vnode_id *rootID); +static int ramfs_unmount(void *ns); +static int ramfs_initialize(const char *deviceName, void *parameters, + size_t len); +static int ramfs_sync(void *_ns); + +static int ramfs_read_vnode(void *ns, vnode_id vnid, char reenter, + void **node); +static int ramfs_write_vnode(void *ns, void *_node, char reenter); +static int ramfs_remove_vnode(void *ns, void *_node, char reenter); +static int ramfs_walk(void *ns, void *_dir, const char *entryName, + char **resolvedPath, vnode_id *vnid); +static int ramfs_access(void *ns, void *_node, int mode); + +static int ramfs_ioctl(void *ns, void *_node, void *_cookie, int cmd, + void *buffer, size_t bufferSize); +static int ramfs_setflags(void *ns, void *_node, void *_cookie, int flags); +static int ramfs_fsync(void *ns, void *_node); +static int ramfs_read_stat(void *ns, void *_node, struct stat *st); +static int ramfs_write_stat(void *ns, void *_node, struct stat *st, long mask); +static int ramfs_create(void *ns, void *dir, const char *name, int openMode, + int mode, vnode_id *vnid, void **cookie); +static int ramfs_open(void *ns, void *_node, int openMode, void **cookie); +static int ramfs_close(void *ns, void *node, void *cookie); +static int ramfs_free_cookie(void *ns, void *node, void *cookie); +static int ramfs_read(void *ns, void *_node, void *cookie, off_t pos, + void *buffer, size_t *bufferSize); +static int ramfs_write(void *ns, void *_node, void *cookie, off_t pos, + const void *buffer, size_t *bufferSize); + +static int ramfs_rename(void *ns, void *_oldDir, const char *oldName, + void *_newDir, const char *newName); +static int ramfs_link(void *ns, void *_dir, const char *name, void *node); +static int ramfs_unlink(void *ns, void *_dir, const char *name); +static int ramfs_rmdir(void *ns, void *_dir, const char *name); +static int ramfs_mkdir(void *ns, void *_dir, const char *name, int mode); +static int ramfs_open_dir(void *ns, void *_node, void **cookie); +static int ramfs_read_dir(void *ns, void *_node, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize); +static int ramfs_rewind_dir(void *ns, void *_node, void *cookie); +static int ramfs_close_dir(void *ns, void *_node, void *cookie); +static int ramfs_free_dir_cookie(void *ns, void *_node, void *cookie); + +static int ramfs_read_fs_stat(void *ns, struct fs_info *info); +static int ramfs_write_fs_stat(void *ns, struct fs_info *info, long mask); + +static int ramfs_symlink(void *ns, void *_dir, const char *name, + const char *path); +static int ramfs_read_link(void *ns, void *_node, char *buffer, + size_t *bufferSize); +// attributes +static int ramfs_open_attrdir(void *ns, void *_node, void **_cookie); +static int ramfs_close_attrdir(void *ns, void *_node, void *_cookie); +static int ramfs_free_attrdir_cookie(void *ns, void *_node, void *_cookie); +static int ramfs_rewind_attrdir(void *ns, void *_node, void *_cookie); +static int ramfs_read_attrdir(void *ns, void *_node, void *_cookie, + long *count, struct dirent *buffer, + size_t bufferSize); +static int ramfs_read_attr(void *ns, void *_node, const char *name, int type, + void *buffer, size_t *bufferSize, off_t pos); +static int ramfs_write_attr(void *ns, void *_node, const char *name, int type, + const void *buffer, size_t *bufferSize, off_t pos); +static int ramfs_remove_attr(void *ns, void *_node, const char *name); +static int ramfs_rename_attr(void *ns, void *_node, const char *oldName, + const char *newName); +static int ramfs_stat_attr(void *ns, void *_node, const char *name, + struct attr_info *attrInfo); +// indices +static int ramfs_open_indexdir(void *ns, void **_cookie); +static int ramfs_close_indexdir(void *ns, void *_cookie); +static int ramfs_free_indexdir_cookie(void *ns, void *_node, void *_cookie); +static int ramfs_rewind_indexdir(void *_ns, void *_cookie); +static int ramfs_read_indexdir(void *ns, void *_cookie, long *count, + struct dirent *buffer, size_t bufferSize); +static int ramfs_create_index(void *ns, const char *name, int type, int flags); +static int ramfs_remove_index(void *ns, const char *name); +static int ramfs_rename_index(void *ns, const char *oldname, + const char *newname); +static int ramfs_stat_index(void *ns, const char *name, + struct index_info *indexInfo); +// queries +int ramfs_open_query(void *ns, const char *queryString, ulong flags, + port_id port, long token, void **cookie); +int ramfs_close_query(void *ns, void *cookie); +int ramfs_free_query_cookie(void *ns, void *node, void *cookie); +int ramfs_read_query(void *ns, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize); + +} // extern "C" + +/* vnode_ops struct. Fill this in to tell the kernel how to call + functions in your driver. +*/ + +vnode_ops fs_entry = { + &ramfs_read_vnode, // read_vnode + &ramfs_write_vnode, // write_vnode + &ramfs_remove_vnode, // remove_vnode + NULL, // secure_vnode (not needed) + &ramfs_walk, // walk + &ramfs_access, // access + &ramfs_create, // create + &ramfs_mkdir, // mkdir + &ramfs_symlink, // symlink + &ramfs_link, // link + &ramfs_rename, // rename + &ramfs_unlink, // unlink + &ramfs_rmdir, // rmdir + &ramfs_read_link, // readlink + &ramfs_open_dir, // opendir + &ramfs_close_dir, // closedir + &ramfs_free_dir_cookie, // free_dircookie + &ramfs_rewind_dir, // rewinddir + &ramfs_read_dir, // readdir + &ramfs_open, // open file + &ramfs_close, // close file + &ramfs_free_cookie, // free cookie + &ramfs_read, // read file + &ramfs_write, // write file + NULL, // readv + NULL, // writev + &ramfs_ioctl, // ioctl + &ramfs_setflags, // setflags file + &ramfs_read_stat, // read stat + &ramfs_write_stat, // write stat + &ramfs_fsync, // fsync + &ramfs_initialize, // initialize + &ramfs_mount, // mount + &ramfs_unmount, // unmount + &ramfs_sync, // sync + &ramfs_read_fs_stat, // read fs stat + &ramfs_write_fs_stat, // write fs stat + NULL, // select + NULL, // deselect + + &ramfs_open_indexdir, // open index dir + &ramfs_close_indexdir, // close index dir + &ramfs_free_indexdir_cookie, // free index dir cookie + &ramfs_rewind_indexdir, // rewind index dir + &ramfs_read_indexdir, // read index dir + &ramfs_create_index, // create index + &ramfs_remove_index, // remove index + &ramfs_rename_index, // rename index + &ramfs_stat_index, // stat index + + &ramfs_open_attrdir, // open attr dir + &ramfs_close_attrdir, // close attr dir + &ramfs_free_attrdir_cookie, // free attr dir cookie + &ramfs_rewind_attrdir, // rewind attr dir + &ramfs_read_attrdir, // read attr dir + &ramfs_write_attr, // write attr + &ramfs_read_attr, // read attr + &ramfs_remove_attr, // remove attr + &ramfs_rename_attr, // rename attr + &ramfs_stat_attr, // stat attr + + &ramfs_open_query, // open query + &ramfs_close_query, // close query + &ramfs_free_query_cookie, // free query cookie + &ramfs_read_query, // read query +}; + +int32 api_version = B_CUR_FS_API_VERSION; + +static char *kFSName = "ramfs"; +static const size_t kOptimalIOSize = 65536; +static const bigtime_t kNotificationInterval = 1000000LL; + +// notify_if_stat_changed +void +notify_if_stat_changed(Volume *volume, Node *node) +{ + if (volume && node && node->IsModified()) { + node->MarkUnmodified(); + notify_listener(B_STAT_CHANGED, volume->GetID(), 0, 0, node->GetID(), + NULL); + } +} + + +// #pragma mark - FS + + +// ramfs_mount +static +int +ramfs_mount(nspace_id nsid, const char */*device*/, ulong flags, + void */*parameters*/, size_t /*len*/, void **data, + vnode_id *rootID) +{ + init_debugging(); + FUNCTION_START(); + // parameters are ignored for now + status_t error = B_OK; + // fail, if read-only mounting is requested + if (flags & B_MOUNT_READ_ONLY) + error = B_BAD_VALUE; + // allocate and init the volume + Volume *volume = NULL; + if (error == B_OK) { + volume = new(nothrow) Volume; + if (!volume) + SET_ERROR(error, B_NO_MEMORY); + if (error == B_OK) + error = volume->Mount(nsid); + } + // set the results + if (error == B_OK) { + *rootID = volume->GetRootDirectory()->GetID(); + *data = volume; + } + // cleanup on failure + if (error != B_OK && volume) + delete volume; + +if (error == B_OK) { +ramfs_create_index(volume, "myIndex", B_STRING_TYPE, 0); +} + + RETURN_ERROR(error); +} + +// ramfs_unmount +static +int +ramfs_unmount(void *ns) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = volume->Unmount(); + if (error == B_OK) + delete volume; + if (error != B_OK) + REPORT_ERROR(error); + exit_debugging(); + return error; +} + +// ramfs_initialize +static +int +ramfs_initialize(const char */*deviceName*/, void */*parameters*/, + size_t /*len*/) +{ + FUNCTION_START(); + return B_ERROR; +} + +// ramfs_sync +static +int +ramfs_sync(void */*_ns*/) +{ + FUNCTION_START(); + return B_OK; +} + + +// #pragma mark - VNodes + + +// ramfs_read_vnode +static +int +ramfs_read_vnode(void *ns, vnode_id vnid, char /*reenter*/, void **node) +{ +// FUNCTION_START(); +FUNCTION(("node: %Ld\n", vnid)); + Volume *volume = (Volume*)ns; + Node *foundNode = NULL; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + error = volume->FindNode(vnid, &foundNode); + if (error == B_OK) + *node = foundNode; + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_write_vnode +static +int +ramfs_write_vnode(void */*ns*/, void *DARG(_node), char /*reenter*/) +{ +// DANGER: If dbg_printf() is used, this thread will enter another FS and +// even perform a write operation. The is dangerous here, since this hook +// may be called out of the other FSs, since, for instance a put_vnode() +// called from another FS may cause the VFS layer to free vnodes and thus +// invoke this hook. +// FUNCTION_START(); +//FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID())); + status_t error = B_OK; + RETURN_ERROR(error); +} + +// ramfs_remove_vnode +static +int +ramfs_remove_vnode(void *ns, void *_node, char /*reenter*/) +{ +FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID())); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeWriteLocker locker = volume) { + volume->NodeRemoved(node); + delete node; + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + + +// #pragma mark - Nodes + + +// ramfs_walk +static +int +ramfs_walk(void *ns, void *_dir, const char *entryName, char **resolvedPath, + vnode_id *vnid) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Directory *dir = dynamic_cast((Node*)_dir); +FUNCTION(("dir: (%Lu), entry: `%s'\n", (dir ? dir->GetID() : -1), entryName)); + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + Node *node = NULL; + // check for non-directories + if (!dir) { + error = B_NOT_A_DIRECTORY; + // special entries: "." and ".." + } else if (!strcmp(entryName, ".")) { + *vnid = dir->GetID(); + if (volume->GetVNode(*vnid, &node) != B_OK) + error = B_BAD_VALUE; + } else if (!strcmp(entryName, "..")) { + Directory *parent = dir->GetParent(); + if (parent && volume->GetVNode(parent->GetID(), &node) == B_OK) + *vnid = node->GetID(); + else + error = B_BAD_VALUE; + // ordinary entries + } else { + // find the entry + error = dir->FindAndGetNode(entryName, &node); +SET_ERROR(error, error); + if (error == B_OK) + *vnid = node->GetID(); + // if it is a symlink, resolve it, if desired + if (error == B_OK && resolvedPath && node->IsSymLink()) { + SymLink *symLink = dynamic_cast(node); + *resolvedPath = strdup(symLink->GetLinkedPath()); + if (!*resolvedPath) + SET_ERROR(error, B_NO_MEMORY); + volume->PutVNode(*vnid); + } + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_ioctl +static +int +ramfs_ioctl(void *ns, void */*_node*/, void */*_cookie*/, int cmd, + void *buffer, size_t /*bufferSize*/) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = B_OK; + switch (cmd) { + case RAMFS_IOCTL_GET_ALLOCATION_INFO: + { + if (buffer) { + if (VolumeReadLocker locker = volume) { + AllocationInfo *info = (AllocationInfo*)buffer; + volume->GetAllocationInfo(*info); + } else + SET_ERROR(error, B_ERROR); + } else + SET_ERROR(error, B_BAD_VALUE); + break; + } + case RAMFS_IOCTL_DUMP_INDEX: + { + if (buffer) { + if (VolumeReadLocker locker = volume) { + const char *name = (const char*)buffer; +PRINT((" RAMFS_IOCTL_DUMP_INDEX, `%s'\n", name)); + IndexDirectory *indexDir = volume->GetIndexDirectory(); + if (indexDir) { + if (Index *index = indexDir->FindIndex(name)) + index->Dump(); + else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ERROR); + } else + SET_ERROR(error, B_BAD_VALUE); + break; + } + default: + error = B_BAD_VALUE; + break; + } + RETURN_ERROR(error); +} + +// ramfs_setflags +static +int +ramfs_setflags(void */*ns*/, void */*_node*/, void */*_cookie*/, int /*flags*/) +{ + FUNCTION_START(); +// TODO:... + return B_OK; +} + +// ramfs_fsync +static +int +ramfs_fsync(void */*ns*/, void */*_node*/) +{ + FUNCTION_START(); + return B_OK; +} + +// ramfs_read_stat +static +int +ramfs_read_stat(void *ns, void *_node, struct stat *st) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; +FUNCTION(("node: %Ld\n", node->GetID())); + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + st->st_dev = volume->GetID(); + st->st_ino = node->GetID(); + st->st_mode = node->GetMode(); + st->st_nlink = node->GetRefCount(); + st->st_uid = node->GetUID(); + st->st_gid = node->GetGID(); + st->st_size = node->GetSize(); + st->st_blksize = kOptimalIOSize; + st->st_atime = node->GetATime(); + st->st_mtime = node->GetMTime(); + st->st_ctime = node->GetCTime(); + st->st_crtime = node->GetCrTime(); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_write_stat +static +int +ramfs_write_stat(void *ns, void *_node, struct stat *st, long mask) +{ + FUNCTION(("mask: %lx\n", mask)); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(node); + // check permissions + error = node->CheckPermissions(ACCESS_W); + // size + if (error == B_OK && (mask & WSTAT_SIZE)) + error = node->SetSize(st->st_size); + if (error == B_OK) { + // permissions + if (mask & WSTAT_MODE) { + node->SetMode(node->GetMode() & ~S_IUMSK + | st->st_mode & S_IUMSK); + } + // UID + if (mask & WSTAT_UID) + node->SetUID(st->st_uid); + // GID + if (mask & WSTAT_GID) + node->SetUID(st->st_gid); + // mtime + if (mask & WSTAT_MTIME) + node->SetMTime(st->st_mtime); + // crtime + if (mask & WSTAT_CRTIME) + node->SetCrTime(st->st_crtime); + } + // notify listeners + if (error == B_OK) + notify_if_stat_changed(volume, node); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + + +// #pragma mark - Files + + +// FileCookie +class FileCookie { +public: + FileCookie(int openMode) : fOpenMode(openMode), fLastNotificationTime(0) {} + + inline int GetOpenMode() { return fOpenMode; } + + inline bigtime_t GetLastNotificationTime() + { return fLastNotificationTime; } + + inline bool NotificationIntervalElapsed(bool set = false) + { + bigtime_t currentTime = system_time(); + bool result = (currentTime - fLastNotificationTime + > kNotificationInterval); + if (set && result) + fLastNotificationTime = currentTime; + return result; + } + +private: + int fOpenMode; + bigtime_t fLastNotificationTime; +}; + +// ramfs_create +static +int +ramfs_create(void *ns, void *_dir, const char *name, int openMode, + int mode, vnode_id *vnid, void **_cookie) +{ +// FUNCTION_START(); + FUNCTION(("name: `%s', open mode: %x, mode: %x\n", name, openMode, mode)); + Volume *volume = (Volume*)ns; + Directory *dir = dynamic_cast((Node*)_dir); + status_t error = B_OK; + // check name + if (!name || *name == '\0') { + SET_ERROR(error, B_BAD_VALUE); + // check directory + } else if (!dir) { + SET_ERROR(error, B_BAD_VALUE); + } else if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(dir); + // directory deleted? + if (is_vnode_removed(volume->GetID(), dir->GetID()) > 0) + SET_ERROR(error, B_NOT_ALLOWED); + // create the file cookie + FileCookie *cookie = NULL; + if (error == B_OK) { + cookie = new(nothrow) FileCookie(openMode); + if (!cookie) + SET_ERROR(error, B_NO_MEMORY); + } + Node *node = NULL; + if (error == B_OK) { + // check if entry does already exist + if (dir->FindNode(name, &node) == B_OK) { + // entry does already exist + // fail, if we shall fail, when the file exists + if (openMode & O_EXCL) { + SET_ERROR(error, B_FILE_EXISTS); + // don't create a file over an existing directory or symlink + } else if (!node->IsFile()) { + SET_ERROR(error, B_NOT_ALLOWED); + // the user must have write permission for an existing entry + } else if ((error = node->CheckPermissions(ACCESS_W)) + == B_OK) { + // truncate, if requested + if (openMode & O_TRUNC) + error = node->SetSize(0); + // we ignore the supplied permissions in this case + // get vnode + if (error == B_OK) { + *vnid = node->GetID(); + error = volume->GetVNode(node->GetID(), &node); + } + } + // the user must have dir write permission to create a new entry + } else if ((error = dir->CheckPermissions(ACCESS_W)) == B_OK) { + // entry doesn't exist: create a file + File *file = NULL; + error = dir->CreateFile(name, &file); + if (error == B_OK) { + node = file; + *vnid = node->GetID(); + // set permissions, owner and group + node->SetMode(mode); + node->SetUID(geteuid()); + node->SetGID(getegid()); + } + } + // set result / cleanup on failure + if (error == B_OK) + *_cookie = cookie; + else if (cookie) + delete cookie; + } + NodeMTimeUpdater mTimeUpdater2(node); + // notify listeners + if (error == B_OK) { + notify_listener(B_ENTRY_CREATED, volume->GetID(), dir->GetID(), 0, + *vnid, name); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_open +static +int +ramfs_open(void *ns, void *_node, int openMode, void **_cookie) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; +FUNCTION(("node: %Ld\n", node->GetID())); + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // directory can be opened read-only + if (node->IsDirectory() && (openMode & O_RWMASK)) + openMode &= ~O_RWMASK; + int accessMode = open_mode_to_access(openMode); + // truncating requires write permission + if (error == B_OK && (openMode & O_TRUNC)) + accessMode |= ACCESS_W; + // check open mode against permissions + if (error == B_OK) + error = node->CheckPermissions(accessMode); + // create the cookie + FileCookie *cookie = NULL; + if (error == B_OK) { + cookie = new(nothrow) FileCookie(openMode); + if (!cookie) + SET_ERROR(error, B_NO_MEMORY); + } + // truncate if requested + if (error == B_OK && (openMode & O_TRUNC)) + error = node->SetSize(0); + NodeMTimeUpdater mTimeUpdater(node); + // set result / cleanup on failure + if (error == B_OK) + *_cookie = cookie; + else if (cookie) + delete cookie; + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_close +static +int +ramfs_close(void *ns, void *_node, void */*_cookie*/) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; +FUNCTION(("node: %Ld\n", node->GetID())); + status_t error = B_OK; + // notify listeners + if (VolumeReadLocker locker = volume) { + notify_if_stat_changed(volume, node); + } else + SET_ERROR(error, B_ERROR); + return B_OK; + +} + +// ramfs_free_cookie +static +int +ramfs_free_cookie(void */*ns*/, void */*node*/, void *_cookie) +{ + FUNCTION_START(); + FileCookie *cookie = (FileCookie*)_cookie; + delete cookie; + return B_OK; +} + +// ramfs_read +static +int +ramfs_read(void *ns, void *_node, void *_cookie, off_t pos, void *buffer, + size_t *bufferSize) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + FileCookie *cookie = (FileCookie*)_cookie; +// FUNCTION(("((%lu, %lu), %Ld, %p, %lu)\n", node->GetDirID(), +// node->GetObjectID(), pos, buffer, *bufferSize)); + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // don't read anything but files + if (!node->IsFile()) + SET_ERROR(error, B_BAD_VALUE); + // check, if reading is allowed + int rwMode = cookie->GetOpenMode() & O_RWMASK; + if (error == B_OK && rwMode != O_RDONLY && rwMode != O_RDWR) + SET_ERROR(error, B_FILE_ERROR); + // read + if (error == B_OK) { + if (File *file = dynamic_cast(node)) + error = file->ReadAt(pos, buffer, *bufferSize, bufferSize); + else { + FATAL(("Node %Ld pretends to be a File, but isn't!\n", + node->GetID())); + error = B_BAD_VALUE; + } + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_write +static +int +ramfs_write(void *ns, void *_node, void *_cookie, off_t pos, + const void *buffer, size_t *bufferSize) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + FileCookie *cookie = (FileCookie*)_cookie; +// FUNCTION(("((%lu, %lu), %Ld, %p, %lu)\n", node->GetDirID(), +// node->GetObjectID(), pos, buffer, *bufferSize)); + status_t error = B_OK; + if (VolumeWriteLocker locker = volume) { + // don't write anything but files + if (!node->IsFile()) + SET_ERROR(error, B_BAD_VALUE); + if (error == B_OK) { + // check, if reading is allowed + int rwMode = cookie->GetOpenMode() & O_RWMASK; + if (error == B_OK && rwMode != O_WRONLY && rwMode != O_RDWR) + SET_ERROR(error, B_FILE_ERROR); + if (error == B_OK) { + // reset the position, if opened in append mode + if (cookie->GetOpenMode() & O_APPEND) + pos = node->GetSize(); + // write + if (File *file = dynamic_cast(node)) { + error = file->WriteAt(pos, buffer, *bufferSize, + bufferSize); + } else { + FATAL(("Node %Ld pretends to be a File, but isn't!\n", + node->GetID())); + error = B_BAD_VALUE; + } + } + } + // notify listeners + if (error == B_OK && cookie->NotificationIntervalElapsed(true)) + notify_if_stat_changed(volume, node); + NodeMTimeUpdater mTimeUpdater(node); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_access +static +int +ramfs_access(void *ns, void *_node, int mode) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + error = node->CheckPermissions(mode); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + + + +// #pragma mark - Directories + + +// ramfs_rename +static +int +ramfs_rename(void *ns, void *_oldDir, const char *oldName, + void *_newDir, const char *newName) +{ + Volume *volume = (Volume*)ns; + Directory *oldDir = dynamic_cast((Node*)_oldDir); + Directory *newDir = dynamic_cast((Node*)_newDir); + status_t error = B_OK; + + // check name + if (!oldName || *oldName == '\0' + || !strcmp(oldName, ".") || !strcmp(oldName, "..") + || !newName || *newName == '\0' + || !strcmp(newName, ".") || !strcmp(newName, "..")) { + SET_ERROR(error, B_BAD_VALUE); + + // check nodes + } else if (!oldDir || !newDir) { + SET_ERROR(error, B_BAD_VALUE); + + // check if the entry isn't actually moved or renamed + } else if (oldDir == newDir && !strcmp(oldName, newName)) { + SET_ERROR(error, B_BAD_VALUE); + } else if (VolumeWriteLocker locker = volume) { +FUNCTION(("old dir: %Ld, old name: `%s', new dir: %Ld, new name: `%s'\n", +oldDir->GetID(), oldName, newDir->GetID(), newName)); + NodeMTimeUpdater mTimeUpdater1(oldDir); + NodeMTimeUpdater mTimeUpdater2(newDir); + + // target directory deleted? + if (is_vnode_removed(volume->GetID(), newDir->GetID()) > 0) + SET_ERROR(error, B_NOT_ALLOWED); + + // check directory write permissions + if (error == B_OK) + error = oldDir->CheckPermissions(ACCESS_W); + if (error == B_OK) + error = newDir->CheckPermissions(ACCESS_W); + + Node *node = NULL; + Entry *entry = NULL; + if (error == B_OK) { + // check if entry exists + if (oldDir->FindAndGetNode(oldName, &node, &entry) != B_OK) { + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else { + if (oldDir != newDir) { + // check whether the entry is a descendent of the target + // directory + for (Directory *parent = newDir; + parent; + parent = parent->GetParent()) { + if (parent == node) { + error = B_BAD_VALUE; + break; + } else if (parent == oldDir) + break; + } + } + } + + // check the target directory situation + Node *clobberNode = NULL; + Entry *clobberEntry = NULL; + if (error == B_OK) { + if (newDir->FindAndGetNode(newName, &clobberNode, + &clobberEntry) == B_OK) { + if (clobberNode->IsDirectory() + && !dynamic_cast(clobberNode)->IsEmpty()) { + SET_ERROR(error, B_NAME_IN_USE); + } + } + } + + // do the job + if (error == B_OK) { + // temporarily acquire an additional reference to make + // sure the node isn't deleted when we remove the entry + error = node->AddReference(); + if (error == B_OK) { + // delete the original entry + error = oldDir->DeleteEntry(entry); + if (error == B_OK) { + // create the new one/relink the target entry + if (clobberEntry) + error = clobberEntry->Link(node); + else + error = newDir->CreateEntry(node, newName); + + if (error == B_OK) { + // send a "removed" notification for the clobbered + // entry + if (clobberEntry) { + notify_listener(B_ENTRY_REMOVED, + volume->GetID(), newDir->GetID(), 0, + clobberNode->GetID(), newName); + } + } else { + // try to recreate the original entry, in case of + // failure + newDir->CreateEntry(node, oldName); + } + } + node->RemoveReference(); + } + } + + // release the entries + if (clobberEntry) + volume->PutVNode(clobberNode); + if (entry) + volume->PutVNode(node); + } + + // notify listeners + if (error == B_OK) { + notify_listener(B_ENTRY_MOVED, volume->GetID(), oldDir->GetID(), + newDir->GetID(), node->GetID(), newName); + } + } else + SET_ERROR(error, B_ERROR); + + RETURN_ERROR(error); +} + +// ramfs_link +static +int +ramfs_link(void *ns, void *_dir, const char *name, void *_node) +{ + FUNCTION(("name: `%s'\n", name)); + Volume *volume = (Volume*)ns; + Directory *dir = dynamic_cast((Node*)_dir); + Node *node = (Node*)_node; + status_t error = B_OK; + // check directory + if (!dir) { + SET_ERROR(error, B_BAD_VALUE); + } else if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(dir); + // directory deleted? + if (is_vnode_removed(volume->GetID(), dir->GetID()) > 0) + SET_ERROR(error, B_NOT_ALLOWED); + // check directory write permissions + error = dir->CheckPermissions(ACCESS_W); + Entry *entry = NULL; + if (error == B_OK) { + // check if entry does already exist + if (dir->FindEntry(name, &entry) == B_OK) { + SET_ERROR(error, B_FILE_EXISTS); + } else { + // entry doesn't exist: create a link + error = dir->CreateEntry(node, name); + } + } + // notify listeners + if (error == B_OK) { + notify_listener(B_ENTRY_CREATED, volume->GetID(), dir->GetID(), 0, + node->GetID(), name); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_unlink +static +int +ramfs_unlink(void *ns, void *_dir, const char *name) +{ + FUNCTION(("name: `%s'\n", name)); + Volume *volume = (Volume*)ns; + Directory *dir = dynamic_cast((Node*)_dir); + status_t error = B_OK; + // check name + if (!name || *name == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) { + SET_ERROR(error, B_BAD_VALUE); + // check node + } else if (!dir) { + SET_ERROR(error, B_BAD_VALUE); + } else if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(dir); + // check directory write permissions + error = dir->CheckPermissions(ACCESS_W); + vnode_id nodeID = -1; + if (error == B_OK) { + // check if entry exists + Node *node = NULL; + Entry *entry = NULL; + if (dir->FindAndGetNode(name, &node, &entry) == B_OK) { + nodeID = node->GetID(); + // unlink the entry, if it isn't a non-empty directory + if (node->IsDirectory() + && !dynamic_cast(node)->IsEmpty()) { + SET_ERROR(error, B_DIRECTORY_NOT_EMPTY); + } else + error = dir->DeleteEntry(entry); + volume->PutVNode(node); + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } + // notify listeners + if (error == B_OK) { + notify_listener(B_ENTRY_REMOVED, volume->GetID(), dir->GetID(), 0, + nodeID, NULL); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_rmdir +static +int +ramfs_rmdir(void *ns, void *_dir, const char *name) +{ + FUNCTION(("name: `%s'\n", name)); + Volume *volume = (Volume*)ns; + Directory *dir = dynamic_cast((Node*)_dir); + status_t error = B_OK; + // check name + if (!name || *name == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) { + SET_ERROR(error, B_BAD_VALUE); + // check node + } else if (!dir) { + SET_ERROR(error, B_BAD_VALUE); + } else if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(dir); + // check directory write permissions + error = dir->CheckPermissions(ACCESS_W); + vnode_id nodeID = -1; + if (error == B_OK) { + // check if entry exists + Node *node = NULL; + Entry *entry = NULL; + if (dir->FindAndGetNode(name, &node, &entry) == B_OK) { + nodeID = node->GetID(); + if (!node->IsDirectory()) { + SET_ERROR(error, B_NOT_A_DIRECTORY); + } else if (!dynamic_cast(node)->IsEmpty()) { + SET_ERROR(error, B_DIRECTORY_NOT_EMPTY); + } else + error = dir->DeleteEntry(entry); + volume->PutVNode(node); + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } + // notify listeners + if (error == B_OK) { + notify_listener(B_ENTRY_REMOVED, volume->GetID(), dir->GetID(), 0, + nodeID, NULL); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// DirectoryCookie +class DirectoryCookie { +public: + DirectoryCookie(Directory *directory = NULL) + : fIterator(directory), + fDotIndex(DOT_INDEX), + // debugging + fIteratorID(atomic_add(&fNextIteratorID, 1)), + fGetNextCounter(0) + { + } + + void Unset() { fIterator.Unset(); } + +// EntryIterator *GetIterator() const { return &fIterator; } + + status_t GetNext(ino_t *nodeID, const char **entryName) + { +fGetNextCounter++; + status_t error = B_OK; + if (fDotIndex == DOT_INDEX) { + // "." + Node *entry = fIterator.GetDirectory(); + *nodeID = entry->GetID(); + *entryName = "."; + fDotIndex++; + } else if (fDotIndex == DOT_DOT_INDEX) { + // ".." + Directory *dir = fIterator.GetDirectory(); + if (dir->GetParent()) + *nodeID = dir->GetParent()->GetID(); + else + *nodeID = dir->GetID(); + *entryName = ".."; + fDotIndex++; + } else { + // ordinary entries + Entry *entry = NULL; + error = fIterator.GetNext(&entry); + if (error == B_OK) { + *nodeID = entry->GetNode()->GetID(); + *entryName = entry->GetName(); + } + } +PRINT(("EntryIterator %ld, GetNext() counter: %ld, entry: %p (%Ld)\n", +fIteratorID, fGetNextCounter, fIterator.GetCurrent(), +(fIterator.GetCurrent() ? fIterator.GetCurrent()->GetNode()->GetID() : -1))); + return error; + } + + status_t Rewind() + { + fDotIndex = DOT_INDEX; + return fIterator.Rewind(); + } + + status_t Suspend() { return fIterator.Suspend(); } + status_t Resume() { return fIterator.Resume(); } + +private: + enum { + DOT_INDEX = 0, + DOT_DOT_INDEX = 1, + ENTRY_INDEX = 2, + }; + +private: + EntryIterator fIterator; + uint32 fDotIndex; + + // debugging + int32 fIteratorID; + int32 fGetNextCounter; + static vint32 fNextIteratorID; +}; +vint32 DirectoryCookie::fNextIteratorID = 0; + +// ramfs_mkdir +static +int +ramfs_mkdir(void *ns, void *_dir, const char *name, int mode) +{ + FUNCTION(("name: `%s', mode: %x\n", name, mode)); + Volume *volume = (Volume*)ns; + Directory *dir = dynamic_cast((Node*)_dir); + status_t error = B_OK; + // check name + if (!name || *name == '\0') { + SET_ERROR(error, B_BAD_VALUE); + // check directory + } else if (!dir) { + SET_ERROR(error, B_BAD_VALUE); + } else if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(dir); + // directory deleted? + if (is_vnode_removed(volume->GetID(), dir->GetID()) > 0) + SET_ERROR(error, B_NOT_ALLOWED); + // check directory write permissions + error = dir->CheckPermissions(ACCESS_W); + Node *node = NULL; + if (error == B_OK) { + // check if entry does already exist + if (dir->FindNode(name, &node) == B_OK) { + SET_ERROR(error, B_FILE_EXISTS); + } else { + // entry doesn't exist: create a directory + Directory *newDir = NULL; + error = dir->CreateDirectory(name, &newDir); + if (error == B_OK) { + node = newDir; + // set permissions, owner and group + node->SetMode(mode); + node->SetUID(geteuid()); + node->SetGID(getegid()); + // put the node + volume->PutVNode(node->GetID()); + } + } + } + NodeMTimeUpdater mTimeUpdater2(node); + // notify listeners + if (error == B_OK) { + notify_listener(B_ENTRY_CREATED, volume->GetID(), dir->GetID(), 0, + node->GetID(), name); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_open_dir +static +int +ramfs_open_dir(void */*ns*/, void *_node, void **_cookie) +{ +// FUNCTION_START(); +// Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; +FUNCTION(("dir: (%Lu)\n", node->GetID())); + // get the Directory + status_t error = (node->IsDirectory() ? B_OK : B_BAD_VALUE); + Directory *dir = NULL; + if (error == B_OK) { + dir = dynamic_cast(node); + if (!dir) { + FATAL(("Node %Ld pretends to be a Directory, but isn't!\n", + node->GetID())); + error = B_BAD_VALUE; + } + } + // create a DirectoryCookie + if (error == B_OK) { + DirectoryCookie *cookie = new(nothrow) DirectoryCookie(dir); + if (cookie) { + error = cookie->Suspend(); + if (error == B_OK) + *_cookie = cookie; + else + delete cookie; + } else + SET_ERROR(error, B_NO_MEMORY); + } + FUNCTION_END(); + RETURN_ERROR(error); +} + +// ramfs_read_dir +static +int +ramfs_read_dir(void *ns, void *DARG(_node), void *_cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; +DARG(Node *node = (Node*)_node; ) +FUNCTION(("dir: (%Lu)\n", node->GetID())); + DirectoryCookie *cookie = (DirectoryCookie*)_cookie; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + error = cookie->Resume(); + if (error == B_OK) { + // read one entry + ino_t nodeID = -1; + const char *name = NULL; + if (cookie->GetNext(&nodeID, &name) == B_OK) { +PRINT((" entry: `%s'\n", name)); + size_t nameLen = strlen(name); + // check, whether the entry fits into the buffer, + // and fill it in + size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; + if (length <= bufferSize) { + buffer->d_dev = volume->GetID(); + buffer->d_ino = nodeID; + memcpy(buffer->d_name, name, nameLen); + buffer->d_name[nameLen] = '\0'; +#if KEEP_WRONG_DIRENT_RECLEN + buffer->d_reclen = nameLen; +#else + buffer->d_reclen = length; +#endif + *count = 1; + } else { + SET_ERROR(error, B_BUFFER_OVERFLOW); + } + } else + *count = 0; + cookie->Suspend(); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_rewind_dir +static +int +ramfs_rewind_dir(void */*ns*/, void */*_node*/, void *_cookie) +{ + FUNCTION_START(); + // No locking needed, since the Directory is guaranteed to live at this + // time and for iterators there is a separate locking. + DirectoryCookie *cookie = (DirectoryCookie*)_cookie; + // no need to Resume(), iterator remains suspended + status_t error = cookie->Rewind(); + RETURN_ERROR(error); +} + +// ramfs_close_dir +static +int +ramfs_close_dir(void */*ns*/, void *DARG(_node), void *_cookie) +{ + FUNCTION_START(); +FUNCTION(("dir: (%Lu)\n", ((Node*)_node)->GetID())); + // No locking needed, since the Directory is guaranteed to live at this + // time and for iterators there is a separate locking. + DirectoryCookie *cookie = (DirectoryCookie*)_cookie; + cookie->Unset(); + return B_OK; +} + +// ramfs_free_dir_cookie +static +int +ramfs_free_dir_cookie(void */*ns*/, void */*_node*/, void *_cookie) +{ + FUNCTION_START(); + DirectoryCookie *cookie = (DirectoryCookie*)_cookie; + delete cookie; + return B_OK; +} + + +// #pragma mark - FS Stats + + +// ramfs_read_fs_stat +static +int +ramfs_read_fs_stat(void *ns, struct fs_info *info) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME + | B_FS_HAS_QUERY; + info->block_size = volume->GetBlockSize(); + info->io_size = kOptimalIOSize; + info->total_blocks = volume->CountBlocks(); + info->free_blocks = volume->CountFreeBlocks(); + info->device_name[0] = '\0'; + strncpy(info->volume_name, volume->GetName(), sizeof(info->volume_name)); + strcpy(info->fsh_name, kFSName); + } else + SET_ERROR(error, B_ERROR); + return B_OK; +} + + +// ramfs_write_fs_stat +static +int +ramfs_write_fs_stat(void *ns, struct fs_info *info, long mask) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = B_OK; + if (VolumeWriteLocker locker = volume) { + if (mask & WFSSTAT_NAME) + error = volume->SetName(info->volume_name); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + + +// #pragma mark - Symlinks + + +// ramfs_symlink +static +int +ramfs_symlink(void *ns, void *_dir, const char *name, const char *path) +{ + FUNCTION(("name: `%s', path: `%s'\n", name, path)); + Volume *volume = (Volume*)ns; + Directory *dir = dynamic_cast((Node*)_dir); + status_t error = B_OK; + // check name + if (!name || *name == '\0') { + SET_ERROR(error, B_BAD_VALUE); + // check directory + } else if (!dir) { + SET_ERROR(error, B_BAD_VALUE); + } else if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(dir); + // directory deleted? + if (is_vnode_removed(volume->GetID(), dir->GetID()) > 0) + SET_ERROR(error, B_NOT_ALLOWED); + // check directory write permissions + error = dir->CheckPermissions(ACCESS_W); + Node *node = NULL; + if (error == B_OK) { + // check if entry does already exist + if (dir->FindNode(name, &node) == B_OK) { + SET_ERROR(error, B_FILE_EXISTS); + } else { + // entry doesn't exist: create a symlink + SymLink *symLink = NULL; + error = dir->CreateSymLink(name, path, &symLink); + if (error == B_OK) { + node = symLink; + // set permissions, owner and group + node->SetMode(S_IRWXU | S_IRWXG | S_IRWXO); + node->SetUID(geteuid()); + node->SetGID(getegid()); + // put the node + volume->PutVNode(node->GetID()); + } + } + } + NodeMTimeUpdater mTimeUpdater2(node); + // notify listeners + if (error == B_OK) { + notify_listener(B_ENTRY_CREATED, volume->GetID(), dir->GetID(), 0, + node->GetID(), name); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_read_link +static +int +ramfs_read_link(void *ns, void *_node, char *buffer, size_t *bufferSize) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // read symlinks only + if (!node->IsSymLink()) + error = B_BAD_VALUE; + if (error == B_OK) { + if (SymLink *symLink = dynamic_cast(node)) { + // copy the link contents + size_t toRead = min(*bufferSize, + symLink->GetLinkedPathLength()); + if (toRead > 0) + memcpy(buffer, symLink->GetLinkedPath(), toRead); + *bufferSize = toRead; + } else { + FATAL(("Node %Ld pretends to be a SymLink, but isn't!\n", + node->GetID())); + error = B_BAD_VALUE; + } + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + + +// #pragma mark - Attributes + + +// ramfs_open_attrdir +static +int +ramfs_open_attrdir(void *ns, void *_node, void **cookie) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // check permissions + error = node->CheckPermissions(ACCESS_R); + // create iterator + AttributeIterator *iterator = NULL; + if (error == B_OK) { + iterator = new(nothrow) AttributeIterator(node); + if (iterator) + error = iterator->Suspend(); + else + SET_ERROR(error, B_NO_MEMORY); + } + // set result / cleanup on failure + if (error == B_OK) + *cookie = iterator; + else + delete iterator; + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_close_attrdir +static +int +ramfs_close_attrdir(void */*ns*/, void */*_node*/, void *cookie) +{ + FUNCTION_START(); + // No locking needed, since the Node is guaranteed to live at this time + // and for iterators there is a separate locking. + AttributeIterator *iterator = (AttributeIterator*)cookie; + iterator->Unset(); + return B_OK; +} + +// ramfs_free_attrdir_cookie +static +int +ramfs_free_attrdir_cookie(void */*ns*/, void */*_node*/, void *cookie) +{ + FUNCTION_START(); + // No locking needed, since the Node is guaranteed to live at this time + // and for iterators there is a separate locking. + AttributeIterator *iterator = (AttributeIterator*)cookie; + delete iterator; + return B_OK; +} + +// ramfs_rewind_attrdir +static +int +ramfs_rewind_attrdir(void */*ns*/, void */*_node*/, void *cookie) +{ + FUNCTION_START(); + // No locking needed, since the Node is guaranteed to live at this time + // and for iterators there is a separate locking. + AttributeIterator *iterator = (AttributeIterator*)cookie; + // no need to Resume(), iterator remains suspended + status_t error = iterator->Rewind(); + RETURN_ERROR(error); +} + +// ramfs_read_attrdir +static +int +ramfs_read_attrdir(void *ns, void */*_node*/, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + AttributeIterator *iterator = (AttributeIterator*)cookie; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + error = iterator->Resume(); + if (error == B_OK) { + // get next attribute + Attribute *attribute = NULL; + if (iterator->GetNext(&attribute) == B_OK) { + const char *name = attribute->GetName(); + size_t nameLen = strlen(name); + // check, whether the entry fits into the buffer, + // and fill it in + size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; + if (length <= bufferSize) { + buffer->d_dev = volume->GetID(); + buffer->d_ino = -1; // attributes don't have a node ID + memcpy(buffer->d_name, name, nameLen); + buffer->d_name[nameLen] = '\0'; +#if KEEP_WRONG_DIRENT_RECLEN + buffer->d_reclen = nameLen; +#else + buffer->d_reclen = length; +#endif + *count = 1; + } else { + SET_ERROR(error, B_BUFFER_OVERFLOW); + } + } else + *count = 0; + iterator->Suspend(); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_write_attr +static +int +ramfs_write_attr(void *ns, void *_node, const char *name, int type, + const void *buffer, size_t *bufferSize, off_t pos) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + // Don't allow writing the reserved attributes. + if (name[0] == '\0' || !strcmp(name, "name") + || !strcmp(name, "last_modified") || !strcmp(name, "size")) { +//FUNCTION(("failed: node: %s, attribute: %s\n", node->GetName(), name)); + error = B_NOT_ALLOWED; + } else if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(node); + // check permissions + error = node->CheckPermissions(ACCESS_W); + // find the attribute or create it, if it doesn't exist yet + Attribute *attribute = NULL; + if (error == B_OK && node->FindAttribute(name, &attribute) != B_OK) + error = node->CreateAttribute(name, &attribute); +REPORT_ERROR(error); + // set the new type and write the data + if (error == B_OK) { + attribute->SetType(type); + error = attribute->WriteAt(pos, buffer, *bufferSize, bufferSize); +REPORT_ERROR(error); + } + // notify listeners + if (error == B_OK) { + notify_listener(B_ATTR_CHANGED, volume->GetID(), 0, 0, + node->GetID(), name); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_read_attr +static +int +ramfs_read_attr(void *ns, void *_node, const char *name, int /*type*/, + void *buffer, size_t *bufferSize, off_t pos) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // check permissions + error = node->CheckPermissions(ACCESS_R); + // find the attribute + Attribute *attribute = NULL; + if (error == B_OK) + error = node->FindAttribute(name, &attribute); + // read + if (error == B_OK) + error = attribute->ReadAt(pos, buffer, *bufferSize, bufferSize); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_remove_attr +static +int +ramfs_remove_attr(void *ns, void *_node, const char *name) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeWriteLocker locker = volume) { + NodeMTimeUpdater mTimeUpdater(node); + // check permissions + error = node->CheckPermissions(ACCESS_W); + // find the attribute + Attribute *attribute = NULL; + if (error == B_OK) + error = node->FindAttribute(name, &attribute); + // delete it + if (error == B_OK) + error = node->DeleteAttribute(attribute); + // notify listeners + if (error == B_OK) { + notify_listener(B_ATTR_CHANGED, volume->GetID(), 0, 0, + node->GetID(), name); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_rename_attr +static +int +ramfs_rename_attr(void */*ns*/, void */*_node*/, const char */*oldName*/, + const char */*newName*/) +{ + // TODO:... + return B_ENTRY_NOT_FOUND; +} + +// ramfs_stat_attr +static +int +ramfs_stat_attr(void *ns, void *_node, const char *name, + struct attr_info *attrInfo) +{ +// FUNCTION_START(); + Volume *volume = (Volume*)ns; + Node *node = (Node*)_node; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // check permissions + error = node->CheckPermissions(ACCESS_R); + // find the attribute + Attribute *attribute = NULL; + if (error == B_OK) + error = node->FindAttribute(name, &attribute); + // read + if (error == B_OK) { + attrInfo->type = attribute->GetType(); + attrInfo->size = attribute->GetSize(); + } + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + + +// #pragma mark - Indices + + +// IndexDirCookie +class IndexDirCookie { +public: + IndexDirCookie() : index_index(0) {} + + int32 index_index; +}; + +// ramfs_open_indexdir +static +int +ramfs_open_indexdir(void *ns, void **_cookie) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // check whether an index directory exists + if (volume->GetIndexDirectory()) { + IndexDirCookie *cookie = new(nothrow) IndexDirCookie; + if (cookie) + *_cookie = cookie; + else + SET_ERROR(error, B_NO_MEMORY); + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_close_indexdir +static +int +ramfs_close_indexdir(void */*ns*/, void */*_cookie*/) +{ + FUNCTION_START(); + return B_OK; +} + +// ramfs_free_indexdir_cookie +static +int +ramfs_free_indexdir_cookie(void */*ns*/, void */*_node*/, void *_cookie) +{ + FUNCTION_START(); + IndexDirCookie *cookie = (IndexDirCookie*)_cookie; + delete cookie; + return B_OK; +} + +// ramfs_rewind_indexdir +static +int +ramfs_rewind_indexdir(void */*_ns*/, void *_cookie) +{ + FUNCTION_START(); + IndexDirCookie *cookie = (IndexDirCookie*)_cookie; + cookie->index_index = 0; + return B_OK; +} + +// ramfs_read_indexdir +static +int +ramfs_read_indexdir(void *ns, void *_cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + IndexDirCookie *cookie = (IndexDirCookie*)_cookie; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // get the next index + Index *index = volume->GetIndexDirectory()->IndexAt( + cookie->index_index++); + if (index) { + const char *name = index->GetName(); + size_t nameLen = strlen(name); + // check, whether the entry fits into the buffer, + // and fill it in + size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; + if (length <= bufferSize) { + buffer->d_dev = volume->GetID(); + buffer->d_ino = -1; // indices don't have a node ID + memcpy(buffer->d_name, name, nameLen); + buffer->d_name[nameLen] = '\0'; +#if KEEP_WRONG_DIRENT_RECLEN + buffer->d_reclen = nameLen; +#else + buffer->d_reclen = length; +#endif + *count = 1; + } else { + SET_ERROR(error, B_BUFFER_OVERFLOW); + } + } else + *count = 0; + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_create_index +static +int +ramfs_create_index(void *ns, const char *name, int type, int /*flags*/) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = B_OK; + // only root is allowed to manipulate the indices + if (geteuid() != 0) { + SET_ERROR(error, B_NOT_ALLOWED); + } else if (VolumeWriteLocker locker = volume) { + // get the index directory + if (IndexDirectory *indexDir = volume->GetIndexDirectory()) { + // check whether an index with that name does already exist + if (indexDir->FindIndex(name)) { + SET_ERROR(error, B_FILE_EXISTS); + } else { + // create the index + AttributeIndex *index; + error = indexDir->CreateIndex(name, type, &index); + } + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_remove_index +static +int +ramfs_remove_index(void *ns, const char *name) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = B_OK; + // only root is allowed to manipulate the indices + if (geteuid() != 0) { + SET_ERROR(error, B_NOT_ALLOWED); + } else if (VolumeWriteLocker locker = volume) { + // get the index directory + if (IndexDirectory *indexDir = volume->GetIndexDirectory()) { + // check whether an index with that name does exist + if (Index *index = indexDir->FindIndex(name)) { + // don't delete a special index + if (indexDir->IsSpecialIndex(index)) { + SET_ERROR(error, B_BAD_VALUE); + } else + indexDir->DeleteIndex(index); + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + +// ramfs_rename_index +static +int +ramfs_rename_index(void */*ns*/, const char */*oldname*/, + const char */*newname*/) +{ + FUNCTION_START(); + return B_ERROR; +} + +// ramfs_stat_index +static +int +ramfs_stat_index(void *ns, const char *name, struct index_info *indexInfo) +{ + FUNCTION_START(); + Volume *volume = (Volume*)ns; + status_t error = B_OK; + if (VolumeReadLocker locker = volume) { + // get the index directory + if (IndexDirectory *indexDir = volume->GetIndexDirectory()) { + // find the index + if (Index *index = indexDir->FindIndex(name)) { + indexInfo->type = index->GetType(); + if (index->HasFixedKeyLength()) + indexInfo->size = index->GetKeyLength(); + else + indexInfo->size = kMaxIndexKeyLength; + indexInfo->modification_time = 0; // TODO: index times + indexInfo->creation_time = 0; // ... + indexInfo->uid = 0; // root owns the indices + indexInfo->gid = 0; // + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ENTRY_NOT_FOUND); + } else + SET_ERROR(error, B_ERROR); + RETURN_ERROR(error); +} + + +// #pragma mark - Queries + +// Query implementation by Axel Dörfler. Slightly adjusted. + +// ramfs_open_query +int +ramfs_open_query(void *ns, const char *queryString, ulong flags, port_id port, + long token, void **cookie) +{ + FUNCTION_START(); + if (ns == NULL || queryString == NULL || cookie == NULL) + RETURN_ERROR(B_BAD_VALUE); + + PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token)); + + Volume *volume = (Volume *)ns; + + // lock the volume + VolumeReadLocker locker(volume); + if (!locker.IsLocked()) + RETURN_ERROR(B_ERROR); + + // parse the query expression + Expression *expression = new Expression((char *)queryString); + if (expression == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter expressionDeleter(expression); + + if (expression->InitCheck() < B_OK) { + WARN(("Could not parse query, stopped at: \"%s\"\n", + expression->Position())); + RETURN_ERROR(B_BAD_VALUE); + } + + // create the query + Query *query = new Query(volume, expression, flags); + if (query == NULL) + RETURN_ERROR(B_NO_MEMORY); + expressionDeleter.Detach(); + // TODO: The Query references an Index, but nothing prevents the Index + // from being deleted, while the Query is in existence. + + if (flags & B_LIVE_QUERY) + query->SetLiveMode(port, token); + + *cookie = (void *)query; + + return B_OK; +} + +// ramfs_close_query +int +ramfs_close_query(void */*ns*/, void */*cookie*/) +{ + FUNCTION_START(); + return B_OK; +} + +// ramfs_free_query_cookie +int +ramfs_free_query_cookie(void *ns, void */*node*/, void *cookie) +{ + FUNCTION_START(); + if (ns == NULL || cookie == NULL) + RETURN_ERROR(B_BAD_VALUE); + + Volume *volume = (Volume *)ns; + + // lock the volume + VolumeReadLocker locker(volume); + if (!locker.IsLocked()) + RETURN_ERROR(B_ERROR); + + Query *query = (Query *)cookie; + Expression *expression = query->GetExpression(); + delete query; + delete expression; + + return B_OK; +} + +// ramfs_read_query +int +ramfs_read_query(void *ns, void *cookie, long *count, + struct dirent *buffer, size_t bufferSize) +{ + FUNCTION_START(); + Query *query = (Query *)cookie; + if (ns == NULL || query == NULL) + RETURN_ERROR(B_BAD_VALUE); + + Volume *volume = (Volume *)ns; + + // lock the volume + VolumeReadLocker locker(volume); + if (!locker.IsLocked()) + RETURN_ERROR(B_ERROR); + + status_t status = query->GetNextEntry(buffer, bufferSize); + if (status == B_OK) + *count = 1; + else if (status == B_ENTRY_NOT_FOUND) + *count = 0; + else + return status; + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/ramfs/makefile b/src/add-ons/kernel/file_systems/ramfs/makefile new file mode 100644 index 0000000000..68b0f0d1b6 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/makefile @@ -0,0 +1,143 @@ +## BeOS Generic Makefile v2.2 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME= ../ramfs + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE= DRIVER + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS= AllocationInfo.cpp AreaUtils.cpp Attribute.cpp AttributeIndex.cpp \ + AttributeIndexImpl.cpp AttributeIterator.cpp \ + BlockAllocator.cpp BlockAllocatorArea.cpp BlockAllocatorAreaBucket.cpp \ + BlockReferenceManager.cpp DataContainer.cpp Debug.cpp \ + Directory.cpp Entry.cpp EntryIterator.cpp EntryListener.cpp File.cpp \ + Index.cpp IndexDirectory.cpp LastModifiedIndex.cpp Locker.cpp \ + NameIndex.cpp Node.cpp \ + NodeListener.cpp \ + NodeTable.cpp Query.cpp SizeIndex.cpp String.cpp SymLink.cpp Volume.cpp \ + cpp.cpp kernel_interface.cpp + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS= /boot/develop/tools/gnupro/lib/gcc-lib/i586-beos/2.9-beos-000224/libgcc.a +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS = + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS = + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= NONE + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +# +# USER - [0/1] userland build +# DEBUG - [0/1] Enable debugging. +# DBG_PRINT - [0/1] Print debug output to file (only if DEBUG). +# DBG_PRINT_FILE - [0/1] Name of debug output file +# (only if DBG_PRINT). +# +DEFINES= USER=0 \ + DEBUG=1 \ + DEBUG_PRINT=1 \ + DEBUG_PRINT_FILE=\"/var/log/ramfs.log\" \ + B_BAD_DATA=B_ERROR + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS = ALL + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS = TRUE + +# specify debug settings +# if TRUE will allow application to be run from a source-level +# debugger. Note that this will disable all optimzation. +DEBUGGER = TRUE + +# specify additional compiler flags for all files +#COMPILER_FLAGS = -include "cpp.h" -fno-exceptions -fno-rtti \ + +COMPILER_FLAGS = -include "cpp.h" -fno-exceptions \ + -Wmissing-prototypes -Woverloaded-virtual \ + -Wpointer-arith -Wcast-align -Wsign-compare + +# specify additional linker flags +LINKER_FLAGS = + +# specify the version of this particular item +# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL") +# This may also be specified in a resource. +APP_VERSION = + +# (for TYPE == DRIVER only) Specify desired location of driver in the /dev +# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will +# instruct the driverinstall rule to place a symlink to your driver's binary in +# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at +# /dev/video/usb when loaded. Default is "misc". +DRIVER_PATH = + +## include the makefile-engine +include $(BUILDHOME)/etc/makefile-engine diff --git a/src/add-ons/kernel/file_systems/ramfs/ramfs.h b/src/add-ons/kernel/file_systems/ramfs/ramfs.h new file mode 100644 index 0000000000..dc2eda9827 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/ramfs.h @@ -0,0 +1,10 @@ +// ramfs.h + +#ifndef RAM_FS_H +#define RAM_FS_H + +#include + +const size_t kMaxIndexKeyLength = 256; + +#endif // RAM_FS_H diff --git a/src/add-ons/kernel/file_systems/ramfs/ramfs_ioctl.h b/src/add-ons/kernel/file_systems/ramfs/ramfs_ioctl.h new file mode 100644 index 0000000000..9b1cf5d512 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ramfs/ramfs_ioctl.h @@ -0,0 +1,15 @@ +// ramfs_ioctl.h + +#ifndef RAMFS_IOCTL_H +#define RAMFS_IOCTL_H + +#include + +#define RAMFS_IOCTL_BASE (B_DEVICE_OP_CODES_END + 10001) + +enum { + RAMFS_IOCTL_GET_ALLOCATION_INFO = RAMFS_IOCTL_BASE, // AllocationInfo* + RAMFS_IOCTL_DUMP_INDEX, // const char* name +}; + +#endif // RAMFS_IOCTL_H