EA modification protocol implementations.(1)
[lwext4.git] / lwext4 / ext4_xattr.c
1 /*
2  * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
4  *
5  *
6  * HelenOS:
7  * Copyright (c) 2012 Martin Sucha
8  * Copyright (c) 2012 Frantisek Princ
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * - Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * - Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in the
19  *   documentation and/or other materials provided with the distribution.
20  * - The name of the author may not be used to endorse or promote products
21  *   derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 /** @addtogroup lwext4
36  * @{
37  */
38 /**
39  * @file  ext4_xattr.c
40  * @brief Extended Attribute manipulation.
41  */
42
43 #include "ext4_config.h"
44 #include "ext4_types.h"
45 #include "ext4_fs.h"
46 #include "ext4_errno.h"
47 #include "ext4_blockdev.h"
48 #include "ext4_super.h"
49 #include "ext4_debug.h"
50 #include "ext4_block_group.h"
51 #include "ext4_balloc.h"
52 #include "ext4_inode.h"
53 #include "ext4_extent.h"
54
55 #include <string.h>
56 #include <stdlib.h>
57
58 /**
59  * @file  ext4_xattr.c
60  * @brief Extended Attribute Manipulation
61  */
62
63 #define NAME_HASH_SHIFT 5
64 #define VALUE_HASH_SHIFT 16
65
66 static inline void
67 ext4_xattr_compute_hash(struct ext4_xattr_header *header,
68                         struct ext4_xattr_entry *entry)
69 {
70         uint32_t hash = 0;
71         char *name = EXT4_XATTR_NAME(entry);
72         int n;
73
74         for (n = 0; n < entry->e_name_len; n++) {
75                 hash = (hash << NAME_HASH_SHIFT) ^
76                         (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
77                         *name++;
78         }
79
80         if (entry->e_value_block == 0 && entry->e_value_size != 0) {
81                 uint32_t *value = (uint32_t *)((char *)header +
82                                 to_le16(entry->e_value_offs));
83                 for (n = (to_le32(entry->e_value_size) +
84                                         EXT4_XATTR_ROUND) >> EXT4_XATTR_PAD_BITS; n; n--) {
85                         hash = (hash << VALUE_HASH_SHIFT) ^
86                                 (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
87                                 to_le32(*value++);
88                 }
89         }
90         entry->e_hash = to_le32(hash);
91 }
92
93 static int ext4_xattr_item_cmp(struct ext4_xattr_item *a,
94                                 struct ext4_xattr_item *b)
95 {
96         int result;
97         result = a->name_index - b->name_index;
98         if (result)
99                 return result;
100
101         result = a->name_len - b->name_len;
102         if (result)
103                 return result;
104
105         return memcmp(a->name, b->name, a->name_len);
106 }
107
108 RB_GENERATE_INTERNAL(ext4_xattr_tree,
109                      ext4_xattr_item,
110                      node,
111                      ext4_xattr_item_cmp,
112                      static inline)
113
114 static struct ext4_xattr_item *
115 ext4_xattr_item_alloc(uint8_t name_index,
116                       char   *name,
117                       size_t  name_len)
118 {
119         struct ext4_xattr_item *item;
120         item = malloc(sizeof(struct ext4_xattr_item) +
121                         name_len);
122         if (!item)
123                 return NULL;
124
125         item->name_index = name_index;
126         item->name       = (char *)(item + 1);
127         item->name_len   = name_len;
128         item->data       = NULL;
129         item->data_size  = 0;
130         
131         memset(&item->node, 0, sizeof(item->node));
132         memcpy(item->name, name, name_len);
133
134         return item;
135 }
136
137 static int
138 ext4_xattr_item_alloc_data(struct ext4_xattr_item *item,
139                            void *orig_data,
140                            size_t data_size)
141 {
142         void *data = NULL;
143         ext4_assert(!item->data);
144         data = malloc(data_size);
145         if (!data)
146                 return ENOMEM;
147
148         if (orig_data)
149                 memcpy(data, orig_data, data_size);
150
151         item->data = data;
152         item->data_size = data_size;
153         return EOK;
154 }
155
156 static void
157 ext4_xattr_item_free_data(struct ext4_xattr_item *item)
158 {
159         ext4_assert(item->data);
160         free(item->data);
161         item->data = NULL;
162         item->data_size = 0;
163 }
164
165 static int
166 ext4_xattr_item_resize_data(struct ext4_xattr_item *item,
167                             size_t new_data_size)
168 {
169         if (new_data_size != item->data_size) {
170                 void *new_data;
171                 new_data = realloc(item->data, new_data_size);
172                 if (!new_data)
173                         return ENOMEM;
174
175                 item->data = new_data;
176                 item->data_size = new_data_size;
177         }
178         return EOK;
179 }
180
181 static void
182 ext4_xattr_item_free(struct ext4_xattr_item *item)
183 {
184         if (item->data)
185                 ext4_xattr_item_free_data(item);
186
187         free(item);
188 }
189
190
191 static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref,
192                                    struct ext4_xattr_entry *entry,
193                                    bool in_inode)
194 {
195         void *ret;
196         if (in_inode) {
197                 struct ext4_xattr_ibody_header *header;
198                 struct ext4_xattr_entry *first_entry;
199                 uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
200                                                  inode_size);
201                 header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
202                 first_entry = EXT4_XATTR_IFIRST(header);
203  
204                 ret = (void *)((char *)first_entry + to_le16(entry->e_value_offs));
205                 if ((char *)ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size))
206                         - (char *)xattr_ref->inode_ref->inode >
207                     inode_size)
208                         ret = NULL;
209
210         } else {
211                 uint32_t block_size =
212                                 ext4_sb_get_block_size(&xattr_ref->fs->sb);
213                 ret = (void *)((char *)xattr_ref->block.data + 
214                                 to_le16(entry->e_value_offs));
215                 if ((char *)ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size))
216                         - (char *)xattr_ref->block.data >
217                     block_size)
218                         ret = NULL;
219         }
220         return ret;
221 }
222
223 static int ext4_xattr_block_fetch(struct ext4_xattr_ref *xattr_ref)
224 {
225         int ret = EOK;
226         size_t size_rem;
227         void *data;
228         struct ext4_xattr_entry *entry = NULL;
229
230         ext4_assert(xattr_ref->block.data);
231         entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
232
233         size_rem = ext4_sb_get_block_size(&xattr_ref->fs->sb);
234         for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
235             entry = EXT4_XATTR_NEXT(entry),
236             size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
237                 struct ext4_xattr_item *item;
238                 char *e_name = EXT4_XATTR_NAME(entry);
239
240                 data = ext4_xattr_entry_data(xattr_ref, entry,
241                                              false);
242                 if (!data) {
243                         ret = EIO;
244                         goto Finish;
245                 }
246
247                 item = ext4_xattr_item_alloc(entry->e_name_index,
248                                              e_name,
249                                              (size_t)entry->e_name_len);
250                 if (!item) {
251                         ret = ENOMEM;
252                         goto Finish;
253                 }
254                 if (ext4_xattr_item_alloc_data(item,
255                                                data,
256                                                to_le32(entry->e_value_size))
257                         != EOK) {
258                         ext4_xattr_item_free(item);
259                         ret = ENOMEM;
260                         goto Finish;
261                 }
262                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
263                 xattr_ref->ea_size += item->data_size;
264         }
265
266 Finish:
267         return ret;
268 }
269
270 static int ext4_xattr_inode_fetch(struct ext4_xattr_ref *xattr_ref)
271 {
272         void *data;
273         size_t size_rem;
274         int ret = EOK;
275         struct ext4_xattr_ibody_header *header = NULL;
276         struct ext4_xattr_entry *entry = NULL;
277         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
278                                          inode_size);
279
280         header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
281         entry = EXT4_XATTR_IFIRST(header);
282
283         size_rem = inode_size -
284                 EXT4_GOOD_OLD_INODE_SIZE -
285                 xattr_ref->inode_ref->inode->extra_isize;
286         for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
287             entry = EXT4_XATTR_NEXT(entry),
288             size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
289                 struct ext4_xattr_item *item;
290                 char *e_name = EXT4_XATTR_NAME(entry);
291
292                 data = ext4_xattr_entry_data(xattr_ref, entry,
293                                              true);
294                 if (!data) {
295                         ret = EIO;
296                         goto Finish;
297                 }
298
299                 item = ext4_xattr_item_alloc(entry->e_name_index,
300                                              e_name,
301                                              (size_t)entry->e_name_len);
302                 if (!item) {
303                         ret = ENOMEM;
304                         goto Finish;
305                 }
306                 if (ext4_xattr_item_alloc_data(item,
307                                                data,
308                                                to_le32(entry->e_value_size))
309                         != EOK) {
310                         ext4_xattr_item_free(item);
311                         ret = ENOMEM;
312                         goto Finish;
313                 }
314                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
315                 xattr_ref->ea_size += item->data_size;
316         }
317
318 Finish:
319         return ret;
320 }
321
322 static int ext4_xattr_fetch(struct ext4_xattr_ref *xattr_ref)
323 {
324         int ret = EOK;
325         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
326                                          inode_size);
327         if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
328                 ret = ext4_xattr_inode_fetch(xattr_ref);
329                 if (ret != EOK)
330                         return ret;
331
332         }
333
334         if (xattr_ref->block_loaded)
335                 ret = ext4_xattr_block_fetch(xattr_ref);
336
337         xattr_ref->dirty = false;
338         return ret;
339 }
340
341 static struct ext4_xattr_item *
342 ext4_xattr_lookup_item(struct ext4_xattr_ref *xattr_ref,
343                        uint8_t name_index,
344                        char   *name,
345                        size_t  name_len)
346 {
347         struct ext4_xattr_item tmp, *ret;
348         tmp.name_index = name_index;
349         tmp.name       = name;
350         tmp.name_len   = name_len;
351         ret = RB_FIND(ext4_xattr_tree, &xattr_ref->root,
352                         &tmp);
353         return ret;
354 }
355
356 static void
357 ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
358 {
359         struct ext4_xattr_item *item, *save_item;
360         RB_FOREACH_SAFE(item,
361                         ext4_xattr_tree,
362                         &xattr_ref->root,
363                         save_item) {
364                 RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
365                 ext4_xattr_item_free(item);
366         }
367         xattr_ref->ea_size = 0;
368 }
369
370 static size_t
371 ext4_xattr_inode_space(struct ext4_xattr_ref *xattr_ref)
372 {
373         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
374                                          inode_size);
375         uint16_t size_rem = inode_size -
376                         EXT4_GOOD_OLD_INODE_SIZE -
377                         xattr_ref->inode_ref->inode->extra_isize;
378         return size_rem;
379 }
380
381 static size_t
382 ext4_xattr_block_space(struct ext4_xattr_ref *xattr_ref)
383 {
384         return ext4_sb_get_block_size(&xattr_ref->fs->sb);
385 }
386
387 static int
388 ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
389 {
390         int ret = EOK;
391         struct ext4_xattr_item *item, *save_item;
392         size_t inode_size_rem, block_size_rem;
393         inode_size_rem = ext4_xattr_inode_space(xattr_ref);
394         block_size_rem = ext4_xattr_block_space(xattr_ref);
395
396         if (xattr_ref->dirty) {
397                 RB_FOREACH_SAFE(item,
398                                 ext4_xattr_tree,
399                                 &xattr_ref->root,
400                                 save_item) {
401                         /*This should not fail!*/
402                         ext4_assert(inode_size_rem + block_size_rem);
403                         if (inode_size_rem) {
404                                 
405                         }
406                 }
407                 xattr_ref->dirty = false;
408         }
409         return ret;
410 }
411
412 int ext4_fs_get_xattr_ref(struct ext4_fs *fs,
413                           struct ext4_inode_ref *inode_ref,
414                           struct ext4_xattr_ref *ref)
415 {
416         int rc;
417         uint64_t xattr_block;
418         xattr_block = ext4_inode_get_file_acl(inode_ref->inode,
419                                               &fs->sb);
420         RB_INIT(&ref->root);
421         if (xattr_block) {
422                 rc = ext4_block_get(fs->bdev,
423                                     &inode_ref->block, xattr_block);
424                 if (rc != EOK)
425                         return EIO;
426         
427                 ref->block_loaded = true;
428         } else
429                 ref->block_loaded = false;
430
431         ref->inode_ref = inode_ref;
432         ref->fs = fs;
433
434         rc = ext4_xattr_fetch(ref);
435         if (rc != EOK) {
436                 ext4_xattr_purge_items(ref);
437                 if (xattr_block)
438                         ext4_block_set(fs->bdev, &inode_ref->block);
439
440                 ref->block_loaded = false;
441                 return rc;
442         }
443         return EOK;
444 }
445
446 void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref)
447 {
448         if (ref->block_loaded) {
449                 ext4_block_set(ref->fs->bdev, &ref->block);
450                 ref->block_loaded = false;
451         }
452         ext4_xattr_purge_items(ref);
453         ref->inode_ref = NULL;
454         ref->fs = NULL;
455 }
456
457 /**
458  * @}
459  */