FIX: no entries found during iteration.
[lwext4.git] / lwext4 / ext4_xattr.c
index ebb22f6d4ec17bcbb0d114c8e24710551a17b4c2..a38cc321409c8b3b87309e3e5ff939093810e1e3 100644 (file)
@@ -450,6 +450,11 @@ ext4_xattr_remove_item(struct ext4_xattr_ref *xattr_ref,
                                       name,
                                       name_len);
        if (item) {
+               if (item == xattr_ref->iter_from)
+                       xattr_ref->iter_from = RB_NEXT(ext4_xattr_tree,
+                                                      &xattr_ref->root,
+                                                      item);
+
                RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
                ext4_xattr_item_free(item);
                xattr_ref->ea_size -= EXT4_XATTR_SIZE(item->data_size) +
@@ -488,6 +493,8 @@ static void
 ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
 {
        struct ext4_xattr_item *item, *save_item;
+       uint64_t xattr_block = ext4_inode_get_file_acl(xattr_ref->inode_ref->inode,
+                                             &xattr_ref->fs->sb);
        RB_FOREACH_SAFE(item,
                        ext4_xattr_tree,
                        &xattr_ref->root,
@@ -495,8 +502,13 @@ ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
                RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
                ext4_xattr_item_free(item);
        }
-       xattr_ref->ea_size = sizeof(struct ext4_xattr_header) +
-                               sizeof(struct ext4_xattr_ibody_header);
+       xattr_ref->ea_size = 0;
+       if (xattr_block)
+               xattr_ref->ea_size += sizeof(struct ext4_xattr_header);
+
+       if (ext4_xattr_inode_space(xattr_ref) >
+           sizeof(struct ext4_xattr_ibody_header))
+               xattr_ref->ea_size += sizeof(struct ext4_xattr_ibody_header);
 }
 
 
@@ -552,7 +564,47 @@ ext4_xattr_try_free_block(struct ext4_xattr_ref *xattr_ref)
        xattr_ref->inode_ref->dirty = true;
        xattr_ref->block_loaded = false;
        xattr_ref->ea_size -= sizeof(struct ext4_xattr_header);
+}
 
+static void
+ext4_xattr_set_block_header(struct ext4_xattr_ref *xattr_ref)
+{
+       struct ext4_xattr_header *block_header = NULL;
+       block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
+
+       memset(block_header, 0, sizeof(struct ext4_xattr_header));
+       block_header->h_magic = EXT4_XATTR_MAGIC;
+       block_header->h_refcount = to_le32(1);
+       block_header->h_blocks = to_le32(1);
+}
+
+static void
+ext4_xattr_set_inode_entry(struct ext4_xattr_item *item,
+                          struct ext4_xattr_ibody_header *ibody_header,
+                          struct ext4_xattr_entry *entry,
+                          void *ibody_data_ptr)
+{
+       entry->e_name_len   = to_le32(item->name_len);
+       entry->e_name_index = item->name_index;
+       entry->e_value_offs =
+               (char *)ibody_data_ptr -
+               (char *)EXT4_XATTR_IFIRST(ibody_header);
+       entry->e_value_block = 0;
+       entry->e_value_size = item->data_size;
+}
+
+static void
+ext4_xattr_set_block_entry(struct ext4_xattr_item *item,
+                          struct ext4_xattr_header *block_header,
+                          struct ext4_xattr_entry *block_entry,
+                          void *block_data_ptr)
+{
+       block_entry->e_name_len   = to_le32(item->name_len);
+       block_entry->e_name_index = item->name_index;
+       block_entry->e_value_offs =
+               (char *)block_data_ptr - (char *)block_header;
+       block_entry->e_value_block = 0;
+       block_entry->e_value_size = item->data_size;
 }
 
 static int
@@ -563,8 +615,8 @@ ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
        void *ibody_data, *block_data;
        struct ext4_xattr_item *item, *save_item;
        size_t inode_size_rem, block_size_rem;
-       struct ext4_xattr_header *header = NULL;
        struct ext4_xattr_ibody_header *ibody_header = NULL;
+       struct ext4_xattr_header *block_header = NULL;
        struct ext4_xattr_entry *entry = NULL;
        struct ext4_xattr_entry *block_entry = NULL;
 
@@ -580,9 +632,10 @@ ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
                if (inode_size_rem > sizeof(struct ext4_xattr_ibody_header)) {
                        memset(ibody_header, 0, inode_size_rem);
                        ibody_header->h_magic = EXT4_XATTR_MAGIC;
-                       xattr_ref->inode_ref->dirty = true;
                        ibody_data = (char *)ibody_header + inode_size_rem;
                        inode_size_rem -= sizeof(struct ext4_xattr_ibody_header);
+
+                       xattr_ref->inode_ref->dirty = true;
                }
                /* If we need an extra block to hold the EA entries*/
                if (xattr_ref->ea_size > inode_size_rem) {
@@ -592,33 +645,32 @@ ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
                                        goto Finish;
 
                        }
-                       header = EXT4_XATTR_BHDR(&xattr_ref->block);
+                       block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
                        block_entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
-                       memset(header, 0, sizeof(struct ext4_xattr_header));
-                       header->h_magic = EXT4_XATTR_MAGIC;
-                       header->h_refcount = to_le32(1);
-                       header->h_blocks = to_le32(1);
-                       block_data = (char *)header + block_size_rem;
+                       ext4_xattr_set_block_header(xattr_ref);
+                       block_data = (char *)block_header + block_size_rem;
                        block_size_rem -= sizeof(struct ext4_xattr_header);
+
                        xattr_ref->block.dirty = true;
                } else {
                        /* We don't need an extra block.*/
                        if (xattr_ref->block_loaded) {
-                               header = EXT4_XATTR_BHDR(&xattr_ref->block);
-                               header->h_refcount =
-                                       to_le32(to_le32(header->h_refcount) - 1);
-                               if (!header->h_refcount) {
+                               block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
+                               block_header->h_refcount =
+                                       to_le32(to_le32(block_header->h_refcount) - 1);
+                               if (!block_header->h_refcount) {
                                        ext4_xattr_try_free_block(xattr_ref);
-                                       header = NULL;
+                                       block_header = NULL;
                                } else {
-                                       xattr_ref->block.dirty = true;
                                        block_entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
-                                       block_data = (char *)header + block_size_rem;
+                                       block_data = (char *)block_header + block_size_rem;
                                        block_size_rem -= sizeof(struct ext4_xattr_header);
                                        ext4_inode_set_file_acl(xattr_ref->inode_ref->inode,
                                                        &xattr_ref->fs->sb,
                                                        0);
+
                                        xattr_ref->inode_ref->dirty = true;
+                                       xattr_ref->block.dirty = true;
                                }
                        }
                }
@@ -629,20 +681,21 @@ ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
                        if (EXT4_XATTR_SIZE(item->data_size) +
                                EXT4_XATTR_LEN(item->name_len) <=
                            inode_size_rem) {
-                               ibody_data = (char *)ibody_data - EXT4_XATTR_SIZE(item->data_size);
-                               entry->e_name_len   = to_le32(item->name_len);
-                               entry->e_name_index = item->name_index;
-                               entry->e_value_offs =
-                                       (char *)ibody_data -
-                                       (char *)EXT4_XATTR_IFIRST(ibody_header);
-                               entry->e_value_block = 0;
-                               entry->e_value_size = item->data_size;
-                               memcpy(EXT4_XATTR_NAME(entry), item->name, item->name_len);
+                               ibody_data = (char *)ibody_data -
+                                       EXT4_XATTR_SIZE(item->data_size);
+                               ext4_xattr_set_inode_entry(item,
+                                                          ibody_header,
+                                                          entry,
+                                                          ibody_data);
+                               memcpy(EXT4_XATTR_NAME(entry),
+                                       item->name, item->name_len);
                                memcpy(ibody_data, item->data, item->data_size);
                                entry = EXT4_XATTR_NEXT(entry);
                                inode_size_rem -=
                                        EXT4_XATTR_SIZE(item->data_size) +
                                        EXT4_XATTR_LEN(item->name_len);
+
+                               xattr_ref->inode_ref->dirty = true;
                                continue;
                        }
                        if (EXT4_XATTR_SIZE(item->data_size) +
@@ -650,25 +703,28 @@ ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
                                ret = ENOSPC;
                                goto Finish;
                        }
-                       block_data = (char *)block_data - EXT4_XATTR_SIZE(item->data_size);
-                       block_entry->e_name_len   = to_le32(item->name_len);
-                       block_entry->e_name_index = item->name_index;
-                       block_entry->e_value_offs =
-                               (char *)block_data - (char *)header;
-                       block_entry->e_value_block = 0;
-                       block_entry->e_value_size = item->data_size;
-                       memcpy(EXT4_XATTR_NAME(block_entry), item->name, item->name_len);
-                       memcpy(block_data, item->data, item->data_size);
+                       block_data = (char *)block_data -
+                               EXT4_XATTR_SIZE(item->data_size);
+                       ext4_xattr_set_block_entry(item,
+                                                  block_header,
+                                                  block_entry,
+                                                  block_data);
+                       memcpy(EXT4_XATTR_NAME(block_entry),
+                               item->name, item->name_len);
+                       memcpy(block_data,
+                               item->data, item->data_size);
                        block_entry = EXT4_XATTR_NEXT(block_entry);
                        block_size_rem -=
                                EXT4_XATTR_SIZE(item->data_size) +
                                EXT4_XATTR_LEN(item->name_len);
+
                        block_modified = true;
                }
                xattr_ref->dirty = false;
                if (block_modified) {
-                       ext4_xattr_rehash(header,
+                       ext4_xattr_rehash(block_header,
                                          EXT4_XATTR_BFIRST(&xattr_ref->block));
+                       xattr_ref->block.dirty = true;
                }
        }
 
@@ -676,6 +732,36 @@ Finish:
        return ret;
 }
 
+void
+ext4_fs_xattr_iterate(struct ext4_xattr_ref *ref,
+                     int (iter)(struct ext4_xattr_ref *ref,
+                                struct ext4_xattr_item *item))
+{
+       struct ext4_xattr_item *item;
+       if (!ref->iter_from)
+               ref->iter_from = RB_MIN(ext4_xattr_tree, &ref->root);
+
+       RB_FOREACH_FROM(item,
+                       ext4_xattr_tree,
+                       ref->iter_from) {
+               int ret = EXT4_XATTR_ITERATE_CONT;
+               if (iter)
+                       iter(ref, item);
+
+               if (ret != EXT4_XATTR_ITERATE_CONT) {
+                       if (ret == EXT4_XATTR_ITERATE_STOP)
+                               ref->iter_from = NULL;
+
+                       break;
+               }
+       }
+}
+
+static void
+ext4_fs_xattr_iterate_reset(struct ext4_xattr_ref *ref)
+{
+       ref->iter_from = NULL;
+}
 
 int ext4_fs_set_xattr(struct ext4_xattr_ref *ref,
                      uint8_t name_index,
@@ -779,6 +865,7 @@ int ext4_fs_get_xattr_ref(struct ext4_fs *fs,
                                              &fs->sb);
        RB_INIT(&ref->root);
        ref->ea_size = 0;
+       ref->iter_from = NULL;
        if (xattr_block) {
                rc = ext4_block_get(fs->bdev,
                                    &ref->block, xattr_block);