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