From ce0087be0d06ad6efc332ba0c8146aa29d439b2d Mon Sep 17 00:00:00 2001 From: ngkaho1234 Date: Sun, 4 Oct 2015 18:49:04 +0800 Subject: [PATCH] EA supports code rewritten with the view of providing EA modification supports. --- lwext4/ext4.c | 13 -- lwext4/ext4_rbtree.c | 462 +++++++++++++++++++++++++++++++++++++++++++ lwext4/ext4_rbtree.h | 201 +++++++++++++++++++ lwext4/ext4_types.h | 13 ++ lwext4/ext4_xattr.c | 323 +++++++++++++++++++++--------- lwext4/ext4_xattr.h | 8 - 6 files changed, 909 insertions(+), 111 deletions(-) create mode 100644 lwext4/ext4_rbtree.c create mode 100644 lwext4/ext4_rbtree.h diff --git a/lwext4/ext4.c b/lwext4/ext4.c index 1926484..c6897d9 100644 --- a/lwext4/ext4.c +++ b/lwext4/ext4.c @@ -736,23 +736,10 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags, { int private_ret; struct ext4_xattr_ref xattr_ref; - struct ext4_xattr_entry *found_entry = NULL; - void *out_data = NULL; - size_t out_len = 0; private_ret = ext4_fs_get_xattr_ref(&f->mp->fs, &ref, &xattr_ref); if (private_ret == EOK) { ext4_dmask_set(EXT4_DEBUG_ALL); - private_ret = ext4_xattr_lookup(&xattr_ref, - EXT4_XATTR_INDEX_POSIX_ACL_ACCESS, - "", - 0, - &found_entry, - &out_data, - &out_len); - if (private_ret == EOK) { - private_ret; - } ext4_fs_put_xattr_ref(&xattr_ref); } } diff --git a/lwext4/ext4_rbtree.c b/lwext4/ext4_rbtree.c new file mode 100644 index 0000000..2db59b0 --- /dev/null +++ b/lwext4/ext4_rbtree.c @@ -0,0 +1,462 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + (C) 2002 David Woodhouse + + 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 + + linux/lib/rbtree.c +*/ +#include "ext4_rbtree.h" + +static void __ext4_rb_rotate_left(struct ext4_rb_node *node, struct ext4_rb_root *root) +{ + struct ext4_rb_node *right = node->ext4_rb_right; + struct ext4_rb_node *parent = ext4_rb_parent(node); + + if ((node->ext4_rb_right = right->ext4_rb_left)) + ext4_rb_set_parent(right->ext4_rb_left, node); + right->ext4_rb_left = node; + + ext4_rb_set_parent(right, parent); + + if (parent) { + if (node == parent->ext4_rb_left) + parent->ext4_rb_left = right; + else + parent->ext4_rb_right = right; + } else + root->ext4_rb_node = right; + ext4_rb_set_parent(node, right); +} + +static void __ext4_rb_rotate_right(struct ext4_rb_node *node, struct ext4_rb_root *root) +{ + struct ext4_rb_node *left = node->ext4_rb_left; + struct ext4_rb_node *parent = ext4_rb_parent(node); + + if ((node->ext4_rb_left = left->ext4_rb_right)) + ext4_rb_set_parent(left->ext4_rb_right, node); + left->ext4_rb_right = node; + + ext4_rb_set_parent(left, parent); + + if (parent) { + if (node == parent->ext4_rb_right) + parent->ext4_rb_right = left; + else + parent->ext4_rb_left = left; + } else + root->ext4_rb_node = left; + ext4_rb_set_parent(node, left); +} + +void ext4_rb_insert_color(struct ext4_rb_node *node, struct ext4_rb_root *root) +{ + struct ext4_rb_node *parent, *gparent; + + while ((parent = ext4_rb_parent(node)) && ext4_rb_is_red(parent)) { + gparent = ext4_rb_parent(parent); + + if (parent == gparent->ext4_rb_left) { + { + register struct ext4_rb_node *uncle = + gparent->ext4_rb_right; + if (uncle && ext4_rb_is_red(uncle)) { + ext4_rb_set_black(uncle); + ext4_rb_set_black(parent); + ext4_rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->ext4_rb_right == node) { + register struct ext4_rb_node *tmp; + __ext4_rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + ext4_rb_set_black(parent); + ext4_rb_set_red(gparent); + __ext4_rb_rotate_right(gparent, root); + } else { + { + register struct ext4_rb_node *uncle = + gparent->ext4_rb_left; + if (uncle && ext4_rb_is_red(uncle)) { + ext4_rb_set_black(uncle); + ext4_rb_set_black(parent); + ext4_rb_set_red(gparent); + node = gparent; + continue; + } + } + + if (parent->ext4_rb_left == node) { + register struct ext4_rb_node *tmp; + __ext4_rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + ext4_rb_set_black(parent); + ext4_rb_set_red(gparent); + __ext4_rb_rotate_left(gparent, root); + } + } + + ext4_rb_set_black(root->ext4_rb_node); +} + +static void __ext4_rb_erase_color(struct ext4_rb_node *node, struct ext4_rb_node *parent, + struct ext4_rb_root *root) +{ + struct ext4_rb_node *other; + + while ((!node || ext4_rb_is_black(node)) && node != root->ext4_rb_node) { + if (parent->ext4_rb_left == node) { + other = parent->ext4_rb_right; + if (ext4_rb_is_red(other)) { + ext4_rb_set_black(other); + ext4_rb_set_red(parent); + __ext4_rb_rotate_left(parent, root); + other = parent->ext4_rb_right; + } + if ((!other->ext4_rb_left || + ext4_rb_is_black(other->ext4_rb_left)) && + (!other->ext4_rb_right || + ext4_rb_is_black(other->ext4_rb_right))) { + ext4_rb_set_red(other); + node = parent; + parent = ext4_rb_parent(node); + } else { + if (!other->ext4_rb_right || + ext4_rb_is_black(other->ext4_rb_right)) { + ext4_rb_set_black(other->ext4_rb_left); + ext4_rb_set_red(other); + __ext4_rb_rotate_right(other, root); + other = parent->ext4_rb_right; + } + ext4_rb_set_color(other, ext4_rb_color(parent)); + ext4_rb_set_black(parent); + ext4_rb_set_black(other->ext4_rb_right); + __ext4_rb_rotate_left(parent, root); + node = root->ext4_rb_node; + break; + } + } else { + other = parent->ext4_rb_left; + if (ext4_rb_is_red(other)) { + ext4_rb_set_black(other); + ext4_rb_set_red(parent); + __ext4_rb_rotate_right(parent, root); + other = parent->ext4_rb_left; + } + if ((!other->ext4_rb_left || + ext4_rb_is_black(other->ext4_rb_left)) && + (!other->ext4_rb_right || + ext4_rb_is_black(other->ext4_rb_right))) { + ext4_rb_set_red(other); + node = parent; + parent = ext4_rb_parent(node); + } else { + if (!other->ext4_rb_left || + ext4_rb_is_black(other->ext4_rb_left)) { + ext4_rb_set_black(other->ext4_rb_right); + ext4_rb_set_red(other); + __ext4_rb_rotate_left(other, root); + other = parent->ext4_rb_left; + } + ext4_rb_set_color(other, ext4_rb_color(parent)); + ext4_rb_set_black(parent); + ext4_rb_set_black(other->ext4_rb_left); + __ext4_rb_rotate_right(parent, root); + node = root->ext4_rb_node; + break; + } + } + } + if (node) + ext4_rb_set_black(node); +} + +void __ext4_rb_erase(struct ext4_rb_node *node, struct ext4_rb_root *root) +{ + struct ext4_rb_node *child, *parent; + int color; + + if (!node->ext4_rb_left) + child = node->ext4_rb_right; + else if (!node->ext4_rb_right) + child = node->ext4_rb_left; + else { + struct ext4_rb_node *old = node, *left; + + node = node->ext4_rb_right; + while ((left = node->ext4_rb_left) != NULL) + node = left; + + if (ext4_rb_parent(old)) { + if (ext4_rb_parent(old)->ext4_rb_left == old) + ext4_rb_parent(old)->ext4_rb_left = node; + else + ext4_rb_parent(old)->ext4_rb_right = node; + } else + root->ext4_rb_node = node; + + child = node->ext4_rb_right; + parent = ext4_rb_parent(node); + color = ext4_rb_color(node); + + if (parent == old) { + parent = node; + } else { + if (child) + ext4_rb_set_parent(child, parent); + parent->ext4_rb_left = child; + + node->ext4_rb_right = old->ext4_rb_right; + ext4_rb_set_parent(old->ext4_rb_right, node); + } + + node->ext4_rb_parent_color = old->ext4_rb_parent_color; + node->ext4_rb_left = old->ext4_rb_left; + ext4_rb_set_parent(old->ext4_rb_left, node); + + goto color; + } + + parent = ext4_rb_parent(node); + color = ext4_rb_color(node); + + if (child) + ext4_rb_set_parent(child, parent); + if (parent) { + if (parent->ext4_rb_left == node) + parent->ext4_rb_left = child; + else + parent->ext4_rb_right = child; + } else + root->ext4_rb_node = child; + +color: + if (color == EXT4_RB_BLACK) + __ext4_rb_erase_color(child, parent, root); +} + +static void ext4_rb_augment_path(struct ext4_rb_node *node, ext4_rb_augment_f func, + void *data) +{ + struct ext4_rb_node *parent; + +up: + func(node, data); + parent = ext4_rb_parent(node); + if (!parent) + return; + + if (node == parent->ext4_rb_left && parent->ext4_rb_right) + func(parent->ext4_rb_right, data); + else if (parent->ext4_rb_left) + func(parent->ext4_rb_left, data); + + node = parent; + goto up; +} + +/* + * after inserting @node into the tree, update the tree to account for + * both the new entry and any damage done by rebalance + */ +void ext4_rb_augment_insert(struct ext4_rb_node *node, ext4_rb_augment_f func, + void *data) +{ + if (node->ext4_rb_left) + node = node->ext4_rb_left; + else if (node->ext4_rb_right) + node = node->ext4_rb_right; + + ext4_rb_augment_path(node, func, data); +} + +/* + * before removing the node, find the deepest node on the rebalance path + * that will still be there after @node gets removed + */ +struct ext4_rb_node *ext4_rb_augment_erase_begin(struct ext4_rb_node *node) +{ + struct ext4_rb_node *deepest; + + if (!node->ext4_rb_right && !node->ext4_rb_left) + deepest = ext4_rb_parent(node); + else if (!node->ext4_rb_right) + deepest = node->ext4_rb_left; + else if (!node->ext4_rb_left) + deepest = node->ext4_rb_right; + else { + deepest = ext4_rb_next(node); + if (deepest->ext4_rb_right) + deepest = deepest->ext4_rb_right; + else if (ext4_rb_parent(deepest) != node) + deepest = ext4_rb_parent(deepest); + } + + return deepest; +} + +/* + * after removal, update the tree to account for the removed entry + * and any rebalance damage. + */ +void ext4_rb_augment_erase_end(struct ext4_rb_node *node, ext4_rb_augment_f func, + void *data) +{ + if (node) + ext4_rb_augment_path(node, func, data); +} + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct ext4_rb_node *ext4_rb_first(const struct ext4_rb_root *root) +{ + struct ext4_rb_node *n; + + n = root->ext4_rb_node; + if (!n) + return NULL; + while (n->ext4_rb_left) + n = n->ext4_rb_left; + return n; +} + +struct ext4_rb_node *ext4_rb_last(const struct ext4_rb_root *root) +{ + struct ext4_rb_node *n; + + n = root->ext4_rb_node; + if (!n) + return NULL; + while (n->ext4_rb_right) + n = n->ext4_rb_right; + return n; +} + +struct ext4_rb_node *ext4_rb_next(const struct ext4_rb_node *node) +{ + struct ext4_rb_node *parent; + + if (ext4_rb_parent(node) == node) + return NULL; + + /* If we have a right-hand child, go down and then left as far + as we can. */ + if (node->ext4_rb_right) { + node = node->ext4_rb_right; + while (node->ext4_rb_left) + node = node->ext4_rb_left; + return (struct ext4_rb_node *)node; + } + + /* No right-hand children. Everything down and left is + smaller than us, so any 'next' node must be in the general + direction of our parent. Go up the tree; any time the + ancestor is a right-hand child of its parent, keep going + up. First time it's a left-hand child of its parent, said + parent is our 'next' node. */ + while ((parent = ext4_rb_parent(node)) && node == parent->ext4_rb_right) + node = parent; + + return parent; +} + +struct ext4_rb_node *ext4_rb_prev(const struct ext4_rb_node *node) +{ + struct ext4_rb_node *parent; + + if (ext4_rb_parent(node) == node) + return NULL; + + /* If we have a left-hand child, go down and then right as far + as we can. */ + if (node->ext4_rb_left) { + node = node->ext4_rb_left; + while (node->ext4_rb_right) + node = node->ext4_rb_right; + return (struct ext4_rb_node *)node; + } + + /* No left-hand children. Go up till we find an ancestor which + is a right-hand child of its parent */ + while ((parent = ext4_rb_parent(node)) && node == parent->ext4_rb_left) + node = parent; + + return parent; +} + +void ext4_rb_replace_node(struct ext4_rb_node *victim, struct ext4_rb_node *new, + struct ext4_rb_root *root) +{ + struct ext4_rb_node *parent = ext4_rb_parent(victim); + + /* Set the surrounding nodes to point to the replacement */ + if (parent) { + if (victim == parent->ext4_rb_left) + parent->ext4_rb_left = new; + else + parent->ext4_rb_right = new; + } else { + root->ext4_rb_node = new; + } + if (victim->ext4_rb_left) + ext4_rb_set_parent(victim->ext4_rb_left, new); + if (victim->ext4_rb_right) + ext4_rb_set_parent(victim->ext4_rb_right, new); + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} + +void ext4_rb_erase(struct ext4_rb_node *node, struct ext4_rb_root *root) +{ + __ext4_rb_erase(node, root); +} + +void ext4_rb_insert(struct ext4_rb_root *root, struct ext4_rb_node *node, + int (*cmp)(struct ext4_rb_node *, struct ext4_rb_node *)) +{ + struct ext4_rb_node **new = &(root->ext4_rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + int result = cmp(node, *new); + + parent = *new; + if (result < 0) { + new = &((*new)->ext4_rb_left); + } else if (result > 0) { + new = &((*new)->ext4_rb_right); + } else + return; + } + + /* Add new node and rebalance tree. */ + ext4_rb_link_node(node, parent, new); + ext4_rb_insert_color(node, root); +} diff --git a/lwext4/ext4_rbtree.h b/lwext4/ext4_rbtree.h new file mode 100644 index 0000000..5ce6729 --- /dev/null +++ b/lwext4/ext4_rbtree.h @@ -0,0 +1,201 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + + 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 + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + I know it's not the cleaner way, but in C (not in C++) to get + performances and genericity... + + Some example of insert and search follows here. The search is a plain + normal search over an ordered tree. The insert instead must be implemented + in two steps: First, the code must insert the element in order as a red leaf + in the tree, and then the support library function ext4_rb_insert_color() must + be called. Such function will do the not trivial work to rebalance the + rbtree, if necessary. + +----------------------------------------------------------------------- +static inline struct page * ext4_rb_search_page_cache(struct inode * inode, + unsigned long offset) +{ + struct ext4_rb_node * n = inode->i_ext4_rb_page_cache.ext4_rb_node; + struct page * page; + + while (n) + { + page = ext4_rb_entry(n, struct page, ext4_rb_page_cache); + + if (offset < page->offset) + n = n->ext4_rb_left; + else if (offset > page->offset) + n = n->ext4_rb_right; + else + return page; + } + return NULL; +} + +static inline struct page * __ext4_rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct ext4_rb_node * node) +{ + struct ext4_rb_node ** p = &inode->i_ext4_rb_page_cache.ext4_rb_node; + struct ext4_rb_node * parent = NULL; + struct page * page; + + while (*p) + { + parent = *p; + page = ext4_rb_entry(parent, struct page, ext4_rb_page_cache); + + if (offset < page->offset) + p = &(*p)->ext4_rb_left; + else if (offset > page->offset) + p = &(*p)->ext4_rb_right; + else + return page; + } + + ext4_rb_link_node(node, parent, p); + + return NULL; +} + +static inline struct page * ext4_rb_insert_page_cache(struct inode * inode, + unsigned long offset, + struct ext4_rb_node * node) +{ + struct page * ret; + if ((ret = __ext4_rb_insert_page_cache(inode, offset, node))) + goto out; + ext4_rb_insert_color(node, &inode->i_ext4_rb_page_cache); + out: + return ret; +} +----------------------------------------------------------------------- +*/ + +#ifndef EXT4_RBTREE_H_ +#define EXT4_RBTREE_H_ + +#include +#include + +#ifndef offsetof +#define offsetof(type, member) ((size_t) & ((type *)0)->member) +#endif +#ifndef container_of +#define container_of(ptr, type, member) \ + ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) +#endif + +struct ext4_rb_node +{ + unsigned long ext4_rb_parent_color; +#define EXT4_RB_RED 0 +#define EXT4_RB_BLACK 1 + struct ext4_rb_node *ext4_rb_right; + struct ext4_rb_node *ext4_rb_left; +}; +/* The alignment might seem pointless, but allegedly CRIS needs it */ + +struct ext4_rb_root +{ + struct ext4_rb_node *ext4_rb_node; +}; + +#define ext4_rb_parent(r) ((struct ext4_rb_node *)((r)->ext4_rb_parent_color & ~3)) +#define ext4_rb_color(r) ((r)->ext4_rb_parent_color & 1) +#define ext4_rb_is_red(r) (!ext4_rb_color(r)) +#define ext4_rb_is_black(r) ext4_rb_color(r) +#define ext4_rb_set_red(r) \ + do { \ + (r)->ext4_rb_parent_color &= ~1; \ + } while (0) +#define ext4_rb_set_black(r) \ + do { \ + (r)->ext4_rb_parent_color |= 1; \ + } while (0) + +static inline void ext4_rb_set_parent(struct ext4_rb_node *rb, struct ext4_rb_node *p) +{ + rb->ext4_rb_parent_color = + (rb->ext4_rb_parent_color & 3) | (unsigned long)p; +} +static inline void ext4_rb_set_color(struct ext4_rb_node *rb, int color) +{ + rb->ext4_rb_parent_color = (rb->ext4_rb_parent_color & ~1) | color; +} + +#define EXT4_RB_ROOT \ + (struct ext4_rb_root) \ + { \ + NULL, \ + } +#define ext4_rb_entry(ptr, type, member) container_of(ptr, type, member) + +#define EXT4_RB_EMPTY_ROOT(root) ((root)->ext4_rb_node == NULL) +#define EXT4_RB_EMPTY_NODE(node) (ext4_rb_parent(node) == node) +#define EXT4_RB_CLEAR_NODE(node) (ext4_rb_set_parent(node, node)) + +static inline void ext4_rb_init_node(struct ext4_rb_node *rb) +{ + rb->ext4_rb_parent_color = 0; + rb->ext4_rb_right = NULL; + rb->ext4_rb_left = NULL; + EXT4_RB_CLEAR_NODE(rb); +} + +extern void ext4_rb_insert_color(struct ext4_rb_node *, struct ext4_rb_root *); +extern void ext4_rb_erase(struct ext4_rb_node *, struct ext4_rb_root *); + +typedef void (*ext4_rb_augment_f)(struct ext4_rb_node *node, void *data); + +extern void ext4_rb_augment_insert(struct ext4_rb_node *node, ext4_rb_augment_f func, + void *data); +extern struct ext4_rb_node *ext4_rb_augment_erase_begin(struct ext4_rb_node *node); +extern void ext4_rb_augment_erase_end(struct ext4_rb_node *node, ext4_rb_augment_f func, + void *data); + +/* Find logical next and previous nodes in a tree */ +extern struct ext4_rb_node *ext4_rb_next(const struct ext4_rb_node *); +extern struct ext4_rb_node *ext4_rb_prev(const struct ext4_rb_node *); +extern struct ext4_rb_node *ext4_rb_first(const struct ext4_rb_root *); +extern struct ext4_rb_node *ext4_rb_last(const struct ext4_rb_root *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void ext4_rb_replace_node(struct ext4_rb_node *victim, struct ext4_rb_node *new, + struct ext4_rb_root *root); + +static inline void ext4_rb_link_node(struct ext4_rb_node *node, + struct ext4_rb_node *parent, + struct ext4_rb_node **ext4_rb_link) +{ + node->ext4_rb_parent_color = (unsigned long)parent; + node->ext4_rb_left = node->ext4_rb_right = NULL; + + *ext4_rb_link = node; +} + +extern void ext4_rb_erase(struct ext4_rb_node *, struct ext4_rb_root *); + +extern void ext4_rb_insert(struct ext4_rb_root *, struct ext4_rb_node *, + int (*)(struct ext4_rb_node *, struct ext4_rb_node *)); + +#endif /* EXT4_RBTREE_H_ */ diff --git a/lwext4/ext4_types.h b/lwext4/ext4_types.h index 549ad24..62d0fb6 100644 --- a/lwext4/ext4_types.h +++ b/lwext4/ext4_types.h @@ -44,6 +44,7 @@ #include "ext4_config.h" #include "ext4_blockdev.h" +#include "ext4_rbtree.h" #include @@ -670,10 +671,22 @@ struct ext4_xattr_entry { uint32_t e_hash; /* hash value of name and value */ } __attribute__((packed)); +struct ext4_xattr_item { + uint8_t name_index; + char *name; + size_t name_len; + void *data; + size_t data_size; + + struct ext4_rb_node node; +}; + struct ext4_xattr_ref { bool block_loaded; struct ext4_block block; struct ext4_inode_ref *inode_ref; + struct ext4_rb_root root; + bool dirty; struct ext4_fs *fs; }; diff --git a/lwext4/ext4_xattr.c b/lwext4/ext4_xattr.c index 9c458eb..ad1f9f3 100644 --- a/lwext4/ext4_xattr.c +++ b/lwext4/ext4_xattr.c @@ -7,12 +7,154 @@ #include "ext4_debug.h" #include "ext4_block_group.h" #include "ext4_balloc.h" -#include "ext4_bitmap.h" #include "ext4_inode.h" -#include "ext4_ialloc.h" #include "ext4_extent.h" #include +#include + +static int ext4_xattr_item_cmp(struct ext4_rb_node *a_, + struct ext4_rb_node *b_) +{ + struct ext4_xattr_item *a, *b; + int result; + a = container_of(a_, struct ext4_xattr_item, node); + b = container_of(b_, struct ext4_xattr_item, node); + result = a->name_index - b->name_index; + if (result) + return result; + + result = a->name_len - b->name_len; + if (result) + return result; + + return memcmp(a->name, b->name, a->name_len); +} + +static struct ext4_xattr_item * +ext4_xattr_item_lookup(struct ext4_rb_root *root, + uint8_t name_index, + char *name, + size_t name_len) +{ + struct ext4_rb_node *new = root->ext4_rb_node; + + /* Figure out where to put new node */ + while (new) { + struct ext4_xattr_item *item = + container_of(new, struct ext4_xattr_item, node); + int result = name_index - item->name_index; + if (!result) { + result = name_len - item->name_len; + if (!result) + result = memcmp(name, + item->name, name_len); + + } + + if (result < 0) + new = new->ext4_rb_left; + else if (result > 0) + new = new->ext4_rb_right; + else + return item; + + } + + return NULL; +} + +static void +ext4_xattr_item_insert(struct ext4_rb_root *root, + struct ext4_xattr_item *item) +{ + ext4_rb_insert(root, &item->node, ext4_xattr_item_cmp); +} + +static void +ext4_xattr_item_remove(struct ext4_rb_root *root, + struct ext4_xattr_item *item) +{ + ext4_rb_erase(&item->node, root); +} + + +static struct ext4_xattr_item * +ext4_xattr_item_alloc(uint8_t name_index, + char *name, + size_t name_len) +{ + struct ext4_xattr_item *item; + item = malloc(sizeof(struct ext4_xattr_item) + + name_len); + if (!item) + return NULL; + + item->name_index = name_index; + item->name = (char *)(item + 1); + item->name_len = name_len; + item->data = NULL; + item->data_size = 0; + + memset(&item->node, 0, sizeof(item->node)); + memcpy(item->name, name, name_len); + + return item; +} + +static int +ext4_xattr_item_alloc_data(struct ext4_xattr_item *item, + void *orig_data, + size_t data_size) +{ + void *data = NULL; + ext4_assert(!item->data); + data = malloc(data_size); + if (!data) + return ENOMEM; + + if (orig_data) + memcpy(data, orig_data, data_size); + + item->data = data; + item->data_size = data_size; + return EOK; +} + +static void +ext4_xattr_item_free_data(struct ext4_xattr_item *item) +{ + ext4_assert(item->data); + free(item->data); + item->data = NULL; + item->data_size = 0; +} + +static int +ext4_xattr_item_resize_data(struct ext4_xattr_item *item, + size_t new_data_size) +{ + if (new_data_size != item->data_size) { + void *new_data; + new_data = realloc(item->data, new_data_size); + if (!new_data) + return ENOMEM; + + item->data = new_data; + item->data_size = new_data_size; + } + return EOK; +} + +static void +ext4_xattr_item_free(struct ext4_xattr_item *item) +{ + if (item->data) + ext4_xattr_item_free_data(item); + + free(item); +} + static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref, struct ext4_xattr_entry *entry, @@ -46,16 +188,10 @@ static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref, return ret; } -static int ext4_xattr_block_lookup(struct ext4_xattr_ref *xattr_ref, - uint8_t name_index, - char *name, - size_t name_len, - struct ext4_xattr_entry **found_entry, - void **out_data, - size_t *out_len) +static int ext4_xattr_block_fetch(struct ext4_xattr_ref *xattr_ref) { + int ret = EOK; size_t size_rem; - bool entry_found = false; void *data; struct ext4_xattr_entry *entry = NULL; @@ -66,49 +202,45 @@ static int ext4_xattr_block_lookup(struct ext4_xattr_ref *xattr_ref, for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry), size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) { - int diff = -1; - if (name_index == entry->e_name_index && - name_len == entry->e_name_len) { - char *e_name = (char *)(entry + 1); - diff = memcmp(e_name, - name, - name_len); - } - if (!diff) { - entry_found = true; - break; - } - } - if (!entry_found) - return ENOENT; + struct ext4_xattr_item *item; + char *e_name = (char *)(entry + 1); - data = ext4_xattr_entry_data(xattr_ref, entry, - false); - if (!data) - return EIO; + data = ext4_xattr_entry_data(xattr_ref, entry, + false); + if (!data) { + ret = EIO; + goto Finish; + } - if (found_entry) - *found_entry = entry; + item = ext4_xattr_item_alloc(entry->e_name_index, + e_name, + (size_t)entry->e_name_len); + if (!item) { + ret = ENOMEM; + goto Finish; + } + if (ext4_xattr_item_alloc_data(item, + data, + to_le32(entry->e_value_size) + != EOK)) { + ext4_xattr_item_free(item); + ret = ENOMEM; + goto Finish; + } + ext4_xattr_item_insert(&xattr_ref->root, + item); - if (out_data && out_len) { - *out_data = data; - *out_len = to_le32(entry->e_value_size); } - return EOK; +Finish: + return ret; } -static int ext4_xattr_inode_lookup(struct ext4_xattr_ref *xattr_ref, - uint8_t name_index, - char *name, - size_t name_len, - struct ext4_xattr_entry **found_entry, - void **out_data, - size_t *out_len) +static int ext4_xattr_inode_fetch(struct ext4_xattr_ref *xattr_ref) { void *data; size_t size_rem; - bool entry_found = false; + int ret = EOK; struct ext4_xattr_ibody_header *header = NULL; struct ext4_xattr_entry *entry = NULL; uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, @@ -123,73 +255,72 @@ static int ext4_xattr_inode_lookup(struct ext4_xattr_ref *xattr_ref, for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry), size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) { - int diff = -1; - if (name_index == entry->e_name_index && - name_len == entry->e_name_len) { - char *e_name = (char *)(entry + 1); - diff = memcmp(e_name, - name, - name_len); - } - if (!diff) { - entry_found = true; - break; + struct ext4_xattr_item *item; + char *e_name = (char *)(entry + 1); + + data = ext4_xattr_entry_data(xattr_ref, entry, + true); + if (!data) { + ret = EIO; + goto Finish; } - } - if (!entry_found) - return ENOENT; - data = ext4_xattr_entry_data(xattr_ref, entry, - true); - if (!data) - return EIO; + item = ext4_xattr_item_alloc(entry->e_name_index, + e_name, + (size_t)entry->e_name_len); + if (!item) { + ret = ENOMEM; + goto Finish; + } + if (ext4_xattr_item_alloc_data(item, + data, + to_le32(entry->e_value_size) + != EOK)) { + ext4_xattr_item_free(item); + ret = ENOMEM; + goto Finish; + } + ext4_xattr_item_insert(&xattr_ref->root, + item); - if (found_entry) - *found_entry = entry; - if (out_data && out_len) { - *out_data = data; - *out_len = to_le32(entry->e_value_size); } - return EOK; +Finish: + return ret; } -int ext4_xattr_lookup(struct ext4_xattr_ref *xattr_ref, - uint8_t name_index, - char *name, - size_t name_len, - struct ext4_xattr_entry **found_entry, - void **out_data, - size_t *out_len) +static int ext4_xattr_fetch(struct ext4_xattr_ref *xattr_ref) { - int ret = ENOENT; + int ret = EOK; uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size); if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) { - ret = ext4_xattr_inode_lookup(xattr_ref, - name_index, - name, - name_len, - found_entry, - out_data, - out_len); - if (ret != ENOENT) + ret = ext4_xattr_inode_fetch(xattr_ref); + if (ret != EOK) return ret; } if (xattr_ref->block_loaded) - ret = ext4_xattr_block_lookup(xattr_ref, - name_index, - name, - name_len, - found_entry, - out_data, - out_len); + ret = ext4_xattr_block_fetch(xattr_ref); + + xattr_ref->dirty = false; return ret; } +static void +ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref) +{ + struct ext4_rb_node *node; + while ((node = ext4_rb_first(&xattr_ref->root))) { + struct ext4_xattr_item *item = ext4_rb_entry( + node, struct ext4_xattr_item, node); + ext4_xattr_item_remove(&xattr_ref->root, item); + ext4_xattr_item_free(item); + } +} + int ext4_fs_get_xattr_ref(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref, struct ext4_xattr_ref *ref) @@ -198,6 +329,7 @@ int ext4_fs_get_xattr_ref(struct ext4_fs *fs, uint64_t xattr_block; xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb); + memset(&ref->root, 0, sizeof(ref->root)); if (xattr_block) { rc = ext4_block_get(fs->bdev, &inode_ref->block, xattr_block); @@ -210,6 +342,16 @@ int ext4_fs_get_xattr_ref(struct ext4_fs *fs, ref->inode_ref = inode_ref; ref->fs = fs; + + rc = ext4_xattr_fetch(ref); + if (rc != EOK) { + ext4_xattr_purge_items(ref); + if (xattr_block) + ext4_block_set(fs->bdev, &inode_ref->block); + + ref->block_loaded = false; + return rc; + } return EOK; } @@ -219,6 +361,7 @@ void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref) ext4_block_set(ref->fs->bdev, &ref->block); ref->block_loaded = false; } + ext4_xattr_purge_items(ref); ref->inode_ref = NULL; ref->fs = NULL; } diff --git a/lwext4/ext4_xattr.h b/lwext4/ext4_xattr.h index 17615d8..e14cc6a 100644 --- a/lwext4/ext4_xattr.h +++ b/lwext4/ext4_xattr.h @@ -10,12 +10,4 @@ int ext4_fs_get_xattr_ref(struct ext4_fs *fs, void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref); -int ext4_xattr_lookup(struct ext4_xattr_ref *xattr_ref, - uint8_t name_index, - char *name, - size_t name_len, - struct ext4_xattr_entry **found_entry, - void **out_data, - size_t *out_len); - #endif