Don't make those rb-tree symbols global.
[lwext4.git] / lwext4 / ext4_xattr.c
1 #include "ext4_config.h"
2 #include "ext4_types.h"
3 #include "ext4_fs.h"
4 #include "ext4_errno.h"
5 #include "ext4_blockdev.h"
6 #include "ext4_super.h"
7 #include "ext4_debug.h"
8 #include "ext4_block_group.h"
9 #include "ext4_balloc.h"
10 #include "ext4_inode.h"
11 #include "ext4_extent.h"
12
13 #include <string.h>
14 #include <stdlib.h>
15
16 static int ext4_xattr_item_cmp(struct ext4_xattr_item *a,
17                                 struct ext4_xattr_item *b)
18 {
19         int result;
20         result = a->name_index - b->name_index;
21         if (result)
22                 return result;
23
24         result = a->name_len - b->name_len;
25         if (result)
26                 return result;
27
28         return memcmp(a->name, b->name, a->name_len);
29 }
30
31 RB_GENERATE_INTERNAL(ext4_xattr_tree,
32                      ext4_xattr_item,
33                      node,
34                      ext4_xattr_item_cmp,
35                      static inline)
36
37 static struct ext4_xattr_item *
38 ext4_xattr_item_alloc(uint8_t name_index,
39                       char   *name,
40                       size_t  name_len)
41 {
42         struct ext4_xattr_item *item;
43         item = malloc(sizeof(struct ext4_xattr_item) +
44                         name_len);
45         if (!item)
46                 return NULL;
47
48         item->name_index = name_index;
49         item->name       = (char *)(item + 1);
50         item->name_len   = name_len;
51         item->data       = NULL;
52         item->data_size  = 0;
53         
54         memset(&item->node, 0, sizeof(item->node));
55         memcpy(item->name, name, name_len);
56
57         return item;
58 }
59
60 static int
61 ext4_xattr_item_alloc_data(struct ext4_xattr_item *item,
62                            void *orig_data,
63                            size_t data_size)
64 {
65         void *data = NULL;
66         ext4_assert(!item->data);
67         data = malloc(data_size);
68         if (!data)
69                 return ENOMEM;
70
71         if (orig_data)
72                 memcpy(data, orig_data, data_size);
73
74         item->data = data;
75         item->data_size = data_size;
76         return EOK;
77 }
78
79 static void
80 ext4_xattr_item_free_data(struct ext4_xattr_item *item)
81 {
82         ext4_assert(item->data);
83         free(item->data);
84         item->data = NULL;
85         item->data_size = 0;
86 }
87
88 static int
89 ext4_xattr_item_resize_data(struct ext4_xattr_item *item,
90                             size_t new_data_size)
91 {
92         if (new_data_size != item->data_size) {
93                 void *new_data;
94                 new_data = realloc(item->data, new_data_size);
95                 if (!new_data)
96                         return ENOMEM;
97
98                 item->data = new_data;
99                 item->data_size = new_data_size;
100         }
101         return EOK;
102 }
103
104 static void
105 ext4_xattr_item_free(struct ext4_xattr_item *item)
106 {
107         if (item->data)
108                 ext4_xattr_item_free_data(item);
109
110         free(item);
111 }
112
113
114 static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref,
115                                    struct ext4_xattr_entry *entry,
116                                    bool in_inode)
117 {
118         void *ret;
119         if (in_inode) {
120                 struct ext4_xattr_ibody_header *header;
121                 struct ext4_xattr_entry *first_entry;
122                 uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
123                                                  inode_size);
124                 header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
125                 first_entry = EXT4_XATTR_IFIRST(header);
126  
127                 ret = (void *)((char *)first_entry + to_le16(entry->e_value_offs));
128                 if ((char *)ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size))
129                         - (char *)xattr_ref->inode_ref->inode >
130                     inode_size)
131                         ret = NULL;
132
133         } else {
134                 uint32_t block_size =
135                                 ext4_sb_get_block_size(&xattr_ref->fs->sb);
136                 ret = (void *)((char *)xattr_ref->block.data + 
137                                 to_le16(entry->e_value_offs));
138                 if ((char *)ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size))
139                         - (char *)xattr_ref->block.data >
140                     block_size)
141                         ret = NULL;
142         }
143         return ret;
144 }
145
146 static int ext4_xattr_block_fetch(struct ext4_xattr_ref *xattr_ref)
147 {
148         int ret = EOK;
149         size_t size_rem;
150         void *data;
151         struct ext4_xattr_entry *entry = NULL;
152
153         ext4_assert(xattr_ref->block.data);
154         entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
155
156         size_rem = ext4_sb_get_block_size(&xattr_ref->fs->sb);
157         for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
158             entry = EXT4_XATTR_NEXT(entry),
159             size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
160                 struct ext4_xattr_item *item;
161                 char *e_name = (char *)(entry + 1);
162
163                 data = ext4_xattr_entry_data(xattr_ref, entry,
164                                              false);
165                 if (!data) {
166                         ret = EIO;
167                         goto Finish;
168                 }
169
170                 item = ext4_xattr_item_alloc(entry->e_name_index,
171                                              e_name,
172                                              (size_t)entry->e_name_len);
173                 if (!item) {
174                         ret = ENOMEM;
175                         goto Finish;
176                 }
177                 if (ext4_xattr_item_alloc_data(item,
178                                                data,
179                                                to_le32(entry->e_value_size))
180                         != EOK) {
181                         ext4_xattr_item_free(item);
182                         ret = ENOMEM;
183                         goto Finish;
184                 }
185                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
186
187         }
188
189 Finish:
190         return ret;
191 }
192
193 static int ext4_xattr_inode_fetch(struct ext4_xattr_ref *xattr_ref)
194 {
195         void *data;
196         size_t size_rem;
197         int ret = EOK;
198         struct ext4_xattr_ibody_header *header = NULL;
199         struct ext4_xattr_entry *entry = NULL;
200         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
201                                          inode_size);
202
203         header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
204         entry = EXT4_XATTR_IFIRST(header);
205
206         size_rem = inode_size -
207                 EXT4_GOOD_OLD_INODE_SIZE -
208                 xattr_ref->inode_ref->inode->extra_isize;
209         for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
210             entry = EXT4_XATTR_NEXT(entry),
211             size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
212                 struct ext4_xattr_item *item;
213                 char *e_name = (char *)(entry + 1);
214
215                 data = ext4_xattr_entry_data(xattr_ref, entry,
216                                              true);
217                 if (!data) {
218                         ret = EIO;
219                         goto Finish;
220                 }
221
222                 item = ext4_xattr_item_alloc(entry->e_name_index,
223                                              e_name,
224                                              (size_t)entry->e_name_len);
225                 if (!item) {
226                         ret = ENOMEM;
227                         goto Finish;
228                 }
229                 if (ext4_xattr_item_alloc_data(item,
230                                                data,
231                                                to_le32(entry->e_value_size))
232                         != EOK) {
233                         ext4_xattr_item_free(item);
234                         ret = ENOMEM;
235                         goto Finish;
236                 }
237                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
238         }
239
240 Finish:
241         return ret;
242 }
243
244 static int ext4_xattr_fetch(struct ext4_xattr_ref *xattr_ref)
245 {
246         int ret = EOK;
247         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
248                                          inode_size);
249         if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
250                 ret = ext4_xattr_inode_fetch(xattr_ref);
251                 if (ret != EOK)
252                         return ret;
253
254         }
255
256         if (xattr_ref->block_loaded)
257                 ret = ext4_xattr_block_fetch(xattr_ref);
258
259         xattr_ref->dirty = false;
260         return ret;
261 }
262
263 static void
264 ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
265 {
266         struct ext4_xattr_item *item, *save_item;
267         RB_FOREACH_SAFE(item,
268                         ext4_xattr_tree,
269                         &xattr_ref->root,
270                         save_item) {
271                 RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
272                 ext4_xattr_item_free(item);
273         }
274         xattr_ref->ea_size = 0;
275 }
276
277 int ext4_fs_get_xattr_ref(struct ext4_fs *fs,
278                           struct ext4_inode_ref *inode_ref,
279                           struct ext4_xattr_ref *ref)
280 {
281         int rc;
282         uint64_t xattr_block;
283         xattr_block = ext4_inode_get_file_acl(inode_ref->inode,
284                                               &fs->sb);
285         RB_INIT(&ref->root);
286         if (xattr_block) {
287                 rc = ext4_block_get(fs->bdev,
288                                     &inode_ref->block, xattr_block);
289                 if (rc != EOK)
290                         return EIO;
291         
292                 ref->block_loaded = true;
293         } else
294                 ref->block_loaded = false;
295
296         ref->inode_ref = inode_ref;
297         ref->fs = fs;
298
299         rc = ext4_xattr_fetch(ref);
300         if (rc != EOK) {
301                 ext4_xattr_purge_items(ref);
302                 if (xattr_block)
303                         ext4_block_set(fs->bdev, &inode_ref->block);
304
305                 ref->block_loaded = false;
306                 return rc;
307         }
308         return EOK;
309 }
310
311 void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref)
312 {
313         if (ref->block_loaded) {
314                 ext4_block_set(ref->fs->bdev, &ref->block);
315                 ref->block_loaded = false;
316         }
317         ext4_xattr_purge_items(ref);
318         ref->inode_ref = NULL;
319         ref->fs = NULL;
320 }
321