FIX: ext4_extent.c failed to be compiled.
[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  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  * - Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * - The name of the author may not be used to endorse or promote products
15  *   derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /** @addtogroup lwext4
30  * @{
31  */
32 /**
33  * @file  ext4_xattr.c
34  * @brief Extended Attribute manipulation.
35  */
36
37 #include "ext4_config.h"
38 #include "ext4_types.h"
39 #include "ext4_fs.h"
40 #include "ext4_errno.h"
41 #include "ext4_blockdev.h"
42 #include "ext4_super.h"
43 #include "ext4_crc32c.h"
44 #include "ext4_debug.h"
45 #include "ext4_block_group.h"
46 #include "ext4_balloc.h"
47 #include "ext4_inode.h"
48
49 #include <string.h>
50 #include <stdlib.h>
51
52 /**
53  * @file  ext4_xattr.c
54  * @brief Extended Attribute Manipulation
55  */
56
57 #define NAME_HASH_SHIFT 5
58 #define VALUE_HASH_SHIFT 16
59
60 static inline void ext4_xattr_compute_hash(struct ext4_xattr_header *header,
61                                            struct ext4_xattr_entry *entry)
62 {
63         uint32_t hash = 0;
64         char *name = EXT4_XATTR_NAME(entry);
65         int n;
66
67         for (n = 0; n < entry->e_name_len; n++) {
68                 hash = (hash << NAME_HASH_SHIFT) ^
69                        (hash >> (8 * sizeof(hash) - NAME_HASH_SHIFT)) ^ *name++;
70         }
71
72         if (entry->e_value_block == 0 && entry->e_value_size != 0) {
73                 uint32_t *value =
74                     (uint32_t *)((char *)header + to_le16(entry->e_value_offs));
75                 for (n = (to_le32(entry->e_value_size) + EXT4_XATTR_ROUND) >>
76                          EXT4_XATTR_PAD_BITS;
77                      n; n--) {
78                         hash = (hash << VALUE_HASH_SHIFT) ^
79                                (hash >> (8 * sizeof(hash) - VALUE_HASH_SHIFT)) ^
80                                to_le32(*value++);
81                 }
82         }
83         entry->e_hash = to_le32(hash);
84 }
85
86 #define BLOCK_HASH_SHIFT 16
87
88 /*
89  * ext4_xattr_rehash()
90  *
91  * Re-compute the extended attribute hash value after an entry has changed.
92  */
93 static void ext4_xattr_rehash(struct ext4_xattr_header *header,
94                               struct ext4_xattr_entry *entry)
95 {
96         struct ext4_xattr_entry *here;
97         uint32_t hash = 0;
98
99         ext4_xattr_compute_hash(header, entry);
100         here = EXT4_XATTR_ENTRY(header + 1);
101         while (!EXT4_XATTR_IS_LAST_ENTRY(here)) {
102                 if (!here->e_hash) {
103                         /* Block is not shared if an entry's hash value == 0 */
104                         hash = 0;
105                         break;
106                 }
107                 hash = (hash << BLOCK_HASH_SHIFT) ^
108                        (hash >> (8 * sizeof(hash) - BLOCK_HASH_SHIFT)) ^
109                        to_le32(here->e_hash);
110                 here = EXT4_XATTR_NEXT(here);
111         }
112         header->h_hash = to_le32(hash);
113 }
114
115 static uint32_t
116 ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref,
117                           ext4_fsblk_t blocknr,
118                           struct ext4_xattr_header *header)
119 {
120         uint32_t checksum = 0;
121         uint64_t le64_blocknr = blocknr;
122         struct ext4_sblock *sb = &inode_ref->fs->sb;
123
124         if (ext4_sb_has_feature_read_only(sb,
125                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
126                 uint32_t orig_checksum;
127
128                 /* Preparation: temporarily set bg checksum to 0 */
129                 orig_checksum = header->h_checksum;
130                 header->h_checksum = 0;
131                 /* First calculate crc32 checksum against fs uuid */
132                 checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid));
133                 /* Then calculate crc32 checksum block number */
134                 checksum = ext4_crc32c(checksum, &le64_blocknr,
135                                      sizeof(le64_blocknr));
136                 /* Finally calculate crc32 checksum against 
137                  * the entire xattr block */
138                 checksum = ext4_crc32c(checksum, header,
139                                    ext4_sb_get_block_size(sb));
140                 header->h_checksum = orig_checksum;
141         }
142         return checksum;
143 }
144
145 static void
146 ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref,
147                               ext4_fsblk_t blocknr,
148                               struct ext4_xattr_header *header)
149 {
150         struct ext4_sblock *sb = &inode_ref->fs->sb;
151         if (!ext4_sb_has_feature_read_only(sb,
152                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
153                 return;
154
155         header->h_checksum =
156                 ext4_xattr_block_checksum(inode_ref, blocknr, header);
157 }
158
159 static int ext4_xattr_item_cmp(struct ext4_xattr_item *a,
160                                struct ext4_xattr_item *b)
161 {
162         int result;
163         result = a->name_index - b->name_index;
164         if (result)
165                 return result;
166
167         result = a->name_len - b->name_len;
168         if (result)
169                 return result;
170
171         return memcmp(a->name, b->name, a->name_len);
172 }
173
174 RB_GENERATE_INTERNAL(ext4_xattr_tree, ext4_xattr_item, node,
175                      ext4_xattr_item_cmp, static inline)
176
177 static struct ext4_xattr_item *
178 ext4_xattr_item_alloc(uint8_t name_index, const char *name, size_t name_len)
179 {
180         struct ext4_xattr_item *item;
181         item = malloc(sizeof(struct ext4_xattr_item) + name_len);
182         if (!item)
183                 return NULL;
184
185         item->name_index = name_index;
186         item->name = (char *)(item + 1);
187         item->name_len = name_len;
188         item->data = NULL;
189         item->data_size = 0;
190
191         memset(&item->node, 0, sizeof(item->node));
192         memcpy(item->name, name, name_len);
193
194         return item;
195 }
196
197 static int ext4_xattr_item_alloc_data(struct ext4_xattr_item *item,
198                                       const void *orig_data, size_t data_size)
199 {
200         void *data = NULL;
201         ext4_assert(!item->data);
202         data = malloc(data_size);
203         if (!data)
204                 return ENOMEM;
205
206         if (orig_data)
207                 memcpy(data, orig_data, data_size);
208
209         item->data = data;
210         item->data_size = data_size;
211         return EOK;
212 }
213
214 static void ext4_xattr_item_free_data(struct ext4_xattr_item *item)
215 {
216         ext4_assert(item->data);
217         free(item->data);
218         item->data = NULL;
219         item->data_size = 0;
220 }
221
222 static int ext4_xattr_item_resize_data(struct ext4_xattr_item *item,
223                                        size_t new_data_size)
224 {
225         if (new_data_size != item->data_size) {
226                 void *new_data;
227                 new_data = realloc(item->data, new_data_size);
228                 if (!new_data)
229                         return ENOMEM;
230
231                 item->data = new_data;
232                 item->data_size = new_data_size;
233         }
234         return EOK;
235 }
236
237 static void ext4_xattr_item_free(struct ext4_xattr_item *item)
238 {
239         if (item->data)
240                 ext4_xattr_item_free_data(item);
241
242         free(item);
243 }
244
245 static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref,
246                                    struct ext4_xattr_entry *entry,
247                                    bool in_inode)
248 {
249         char *ret;
250         if (in_inode) {
251                 struct ext4_xattr_ibody_header *header;
252                 struct ext4_xattr_entry *first_entry;
253                 int16_t inode_size =
254                     ext4_get16(&xattr_ref->fs->sb, inode_size);
255                 header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
256                 first_entry = EXT4_XATTR_IFIRST(header);
257
258                 ret = ((char *)first_entry + to_le16(entry->e_value_offs));
259                 if (ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size)) -
260                         (char *)xattr_ref->inode_ref->inode > inode_size)
261                         ret = NULL;
262
263                 return ret;
264
265         }
266         int32_t block_size = ext4_sb_get_block_size(&xattr_ref->fs->sb);
267         ret = ((char *)xattr_ref->block.data + to_le16(entry->e_value_offs));
268         if (ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size)) -
269                         (char *)xattr_ref->block.data > block_size)
270                 ret = NULL;
271         return ret;
272 }
273
274 static int ext4_xattr_block_fetch(struct ext4_xattr_ref *xattr_ref)
275 {
276         int ret = EOK;
277         size_t size_rem;
278         void *data;
279         struct ext4_xattr_entry *entry = NULL;
280
281         ext4_assert(xattr_ref->block.data);
282         entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
283
284         size_rem = ext4_sb_get_block_size(&xattr_ref->fs->sb);
285         for (; size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
286              entry = EXT4_XATTR_NEXT(entry),
287              size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
288                 struct ext4_xattr_item *item;
289                 char *e_name = EXT4_XATTR_NAME(entry);
290
291                 data = ext4_xattr_entry_data(xattr_ref, entry, false);
292                 if (!data) {
293                         ret = EIO;
294                         goto Finish;
295                 }
296
297                 item = ext4_xattr_item_alloc(entry->e_name_index, e_name,
298                                              (size_t)entry->e_name_len);
299                 if (!item) {
300                         ret = ENOMEM;
301                         goto Finish;
302                 }
303                 if (ext4_xattr_item_alloc_data(
304                         item, data, to_le32(entry->e_value_size)) != EOK) {
305                         ext4_xattr_item_free(item);
306                         ret = ENOMEM;
307                         goto Finish;
308                 }
309                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
310                 xattr_ref->ea_size += EXT4_XATTR_SIZE(item->data_size) +
311                                       EXT4_XATTR_LEN(item->name_len);
312         }
313
314 Finish:
315         return ret;
316 }
317
318 static int ext4_xattr_inode_fetch(struct ext4_xattr_ref *xattr_ref)
319 {
320         void *data;
321         size_t size_rem;
322         int ret = EOK;
323         struct ext4_xattr_ibody_header *header = NULL;
324         struct ext4_xattr_entry *entry = NULL;
325         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
326
327         header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
328         entry = EXT4_XATTR_IFIRST(header);
329
330         size_rem = inode_size - EXT4_GOOD_OLD_INODE_SIZE -
331                    xattr_ref->inode_ref->inode->extra_isize;
332         for (; size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
333              entry = EXT4_XATTR_NEXT(entry),
334              size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
335                 struct ext4_xattr_item *item;
336                 char *e_name = EXT4_XATTR_NAME(entry);
337
338                 data = ext4_xattr_entry_data(xattr_ref, entry, true);
339                 if (!data) {
340                         ret = EIO;
341                         goto Finish;
342                 }
343
344                 item = ext4_xattr_item_alloc(entry->e_name_index, e_name,
345                                              (size_t)entry->e_name_len);
346                 if (!item) {
347                         ret = ENOMEM;
348                         goto Finish;
349                 }
350                 if (ext4_xattr_item_alloc_data(
351                         item, data, to_le32(entry->e_value_size)) != EOK) {
352                         ext4_xattr_item_free(item);
353                         ret = ENOMEM;
354                         goto Finish;
355                 }
356                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
357                 xattr_ref->ea_size += EXT4_XATTR_SIZE(item->data_size) +
358                                       EXT4_XATTR_LEN(item->name_len);
359         }
360
361 Finish:
362         return ret;
363 }
364
365 static size_t ext4_xattr_inode_space(struct ext4_xattr_ref *xattr_ref)
366 {
367         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
368         uint16_t size_rem = inode_size - EXT4_GOOD_OLD_INODE_SIZE -
369                             xattr_ref->inode_ref->inode->extra_isize;
370         return size_rem;
371 }
372
373 static size_t ext4_xattr_block_space(struct ext4_xattr_ref *xattr_ref)
374 {
375         return ext4_sb_get_block_size(&xattr_ref->fs->sb);
376 }
377
378 static int ext4_xattr_fetch(struct ext4_xattr_ref *xattr_ref)
379 {
380         int ret = EOK;
381         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb, inode_size);
382         if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
383                 ret = ext4_xattr_inode_fetch(xattr_ref);
384                 if (ret != EOK)
385                         return ret;
386         }
387
388         if (xattr_ref->block_loaded)
389                 ret = ext4_xattr_block_fetch(xattr_ref);
390
391         xattr_ref->dirty = false;
392         return ret;
393 }
394
395 static struct ext4_xattr_item *
396 ext4_xattr_lookup_item(struct ext4_xattr_ref *xattr_ref, uint8_t name_index,
397                        const char *name, size_t name_len)
398 {
399         struct ext4_xattr_item tmp = {
400                 .name_index = name_index,
401                 .name = (char *)name, /*RB_FIND - won't touch this string*/
402                 .name_len = name_len,
403         };
404
405         return RB_FIND(ext4_xattr_tree, &xattr_ref->root, &tmp);
406 }
407
408 static struct ext4_xattr_item *
409 ext4_xattr_insert_item(struct ext4_xattr_ref *xattr_ref, uint8_t name_index,
410                        const char *name, size_t name_len, const void *data,
411                        size_t data_size)
412 {
413         struct ext4_xattr_item *item;
414         item = ext4_xattr_item_alloc(name_index, name, name_len);
415         if (!item)
416                 return NULL;
417
418         if ((xattr_ref->ea_size + EXT4_XATTR_SIZE(data_size) +
419                 EXT4_XATTR_LEN(item->name_len)
420                         >
421             ext4_xattr_inode_space(xattr_ref) -
422                 sizeof(struct ext4_xattr_ibody_header))
423                 &&
424             (xattr_ref->ea_size + EXT4_XATTR_SIZE(data_size) +
425                 EXT4_XATTR_LEN(item->name_len) >
426             ext4_xattr_block_space(xattr_ref) -
427                 sizeof(struct ext4_xattr_header))) {
428                 ext4_xattr_item_free(item);
429
430                 return NULL;
431         }
432         if (ext4_xattr_item_alloc_data(item, data, data_size) != EOK) {
433                 ext4_xattr_item_free(item);
434                 return NULL;
435         }
436         RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
437         xattr_ref->ea_size +=
438             EXT4_XATTR_SIZE(item->data_size) + EXT4_XATTR_LEN(item->name_len);
439         xattr_ref->dirty = true;
440         return item;
441 }
442
443 static int ext4_xattr_remove_item(struct ext4_xattr_ref *xattr_ref,
444                                   uint8_t name_index, const char *name,
445                                   size_t name_len)
446 {
447         int ret = ENOENT;
448         struct ext4_xattr_item *item =
449             ext4_xattr_lookup_item(xattr_ref, name_index, name, name_len);
450         if (item) {
451                 if (item == xattr_ref->iter_from)
452                         xattr_ref->iter_from =
453                             RB_NEXT(ext4_xattr_tree, &xattr_ref->root, item);
454
455                 xattr_ref->ea_size -= EXT4_XATTR_SIZE(item->data_size) +
456                                       EXT4_XATTR_LEN(item->name_len);
457
458                 RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
459                 ext4_xattr_item_free(item);
460                 xattr_ref->dirty = true;
461                 ret = EOK;
462         }
463         return ret;
464 }
465
466 static int ext4_xattr_resize_item(struct ext4_xattr_ref *xattr_ref,
467                                   struct ext4_xattr_item *item,
468                                   size_t new_data_size)
469 {
470         int ret = EOK;
471         size_t old_data_size = item->data_size;
472         if ((xattr_ref->ea_size - EXT4_XATTR_SIZE(old_data_size) +
473                 EXT4_XATTR_SIZE(new_data_size)
474                         >
475             ext4_xattr_inode_space(xattr_ref) -
476                 sizeof(struct ext4_xattr_ibody_header))
477                 &&
478             (xattr_ref->ea_size - EXT4_XATTR_SIZE(old_data_size) +
479                 EXT4_XATTR_SIZE(new_data_size)
480                         >
481             ext4_xattr_block_space(xattr_ref) -
482                 sizeof(struct ext4_xattr_header))) {
483
484                 return ENOSPC;
485         }
486         ret = ext4_xattr_item_resize_data(item, new_data_size);
487         if (ret != EOK) {
488                 return ret;
489         }
490         xattr_ref->ea_size =
491             xattr_ref->ea_size -
492             EXT4_XATTR_SIZE(old_data_size) +
493             EXT4_XATTR_SIZE(new_data_size);
494         xattr_ref->dirty = true;
495         return ret;
496 }
497
498 static void ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
499 {
500         struct ext4_xattr_item *item, *save_item;
501         RB_FOREACH_SAFE(item, ext4_xattr_tree, &xattr_ref->root, save_item)
502         {
503                 RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
504                 ext4_xattr_item_free(item);
505         }
506         xattr_ref->ea_size = 0;
507 }
508
509 static int ext4_xattr_try_alloc_block(struct ext4_xattr_ref *xattr_ref)
510 {
511         int ret = EOK;
512
513         ext4_fsblk_t xattr_block = 0;
514         xattr_block = ext4_inode_get_file_acl(xattr_ref->inode_ref->inode,
515                                               &xattr_ref->fs->sb);
516         if (!xattr_block) {
517                 ext4_fsblk_t goal =
518                         ext4_fs_inode_to_goal_block(xattr_ref->inode_ref);
519
520                 ret = ext4_balloc_alloc_block(xattr_ref->inode_ref,
521                                               goal,
522                                               &xattr_block);
523                 if (ret != EOK)
524                         goto Finish;
525
526                 ret = ext4_block_get(xattr_ref->fs->bdev, &xattr_ref->block,
527                                      xattr_block);
528                 if (ret != EOK) {
529                         ext4_balloc_free_block(xattr_ref->inode_ref,
530                                                xattr_block);
531                         goto Finish;
532                 }
533
534                 ext4_inode_set_file_acl(xattr_ref->inode_ref->inode,
535                                         &xattr_ref->fs->sb, xattr_block);
536                 xattr_ref->inode_ref->dirty = true;
537                 xattr_ref->block_loaded = true;
538         }
539
540 Finish:
541         return ret;
542 }
543
544 static void ext4_xattr_try_free_block(struct ext4_xattr_ref *xattr_ref)
545 {
546         ext4_fsblk_t xattr_block;
547         xattr_block = ext4_inode_get_file_acl(xattr_ref->inode_ref->inode,
548                                               &xattr_ref->fs->sb);
549         ext4_inode_set_file_acl(xattr_ref->inode_ref->inode, &xattr_ref->fs->sb,
550                                 0);
551         ext4_block_set(xattr_ref->fs->bdev, &xattr_ref->block);
552         ext4_balloc_free_block(xattr_ref->inode_ref, xattr_block);
553         xattr_ref->inode_ref->dirty = true;
554         xattr_ref->block_loaded = false;
555 }
556
557 static void ext4_xattr_set_block_header(struct ext4_xattr_ref *xattr_ref)
558 {
559         struct ext4_xattr_header *block_header = NULL;
560         block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
561
562         memset(block_header, 0, sizeof(struct ext4_xattr_header));
563         block_header->h_magic = EXT4_XATTR_MAGIC;
564         block_header->h_refcount = to_le32(1);
565         block_header->h_blocks = to_le32(1);
566 }
567
568 static void
569 ext4_xattr_set_inode_entry(struct ext4_xattr_item *item,
570                            struct ext4_xattr_ibody_header *ibody_header,
571                            struct ext4_xattr_entry *entry, void *ibody_data_ptr)
572 {
573         entry->e_name_len = to_le32(item->name_len);
574         entry->e_name_index = item->name_index;
575         entry->e_value_offs =
576             (char *)ibody_data_ptr - (char *)EXT4_XATTR_IFIRST(ibody_header);
577         entry->e_value_block = 0;
578         entry->e_value_size = item->data_size;
579 }
580
581 static void ext4_xattr_set_block_entry(struct ext4_xattr_item *item,
582                                        struct ext4_xattr_header *block_header,
583                                        struct ext4_xattr_entry *block_entry,
584                                        void *block_data_ptr)
585 {
586         block_entry->e_name_len = to_le32(item->name_len);
587         block_entry->e_name_index = item->name_index;
588         block_entry->e_value_offs =
589             (char *)block_data_ptr - (char *)block_header;
590         block_entry->e_value_block = 0;
591         block_entry->e_value_size = item->data_size;
592 }
593
594 static int ext4_xattr_write_to_disk(struct ext4_xattr_ref *xattr_ref)
595 {
596         int ret = EOK;
597         bool block_modified = false;
598         void *ibody_data = NULL;
599         void *block_data = NULL;
600         struct ext4_xattr_item *item, *save_item;
601         size_t inode_size_rem, block_size_rem;
602         struct ext4_xattr_ibody_header *ibody_header = NULL;
603         struct ext4_xattr_header *block_header = NULL;
604         struct ext4_xattr_entry *entry = NULL;
605         struct ext4_xattr_entry *block_entry = NULL;
606
607         inode_size_rem = ext4_xattr_inode_space(xattr_ref);
608         block_size_rem = ext4_xattr_block_space(xattr_ref);
609         if (inode_size_rem > sizeof(struct ext4_xattr_ibody_header)) {
610                 ibody_header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
611                 entry = EXT4_XATTR_IFIRST(ibody_header);
612         }
613
614         if (!xattr_ref->dirty)
615                 goto Finish;
616         /* If there are enough spaces in the ibody EA table.*/
617         if (inode_size_rem > sizeof(struct ext4_xattr_ibody_header)) {
618                 memset(ibody_header, 0, inode_size_rem);
619                 ibody_header->h_magic = EXT4_XATTR_MAGIC;
620                 ibody_data = (char *)ibody_header + inode_size_rem;
621                 inode_size_rem -= sizeof(struct ext4_xattr_ibody_header);
622
623                 xattr_ref->inode_ref->dirty = true;
624         }
625         /* If we need an extra block to hold the EA entries*/
626         if (xattr_ref->ea_size > inode_size_rem) {
627                 if (!xattr_ref->block_loaded) {
628                         ret = ext4_xattr_try_alloc_block(xattr_ref);
629                         if (ret != EOK)
630                                 goto Finish;
631                 }
632                 block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
633                 block_entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
634                 ext4_xattr_set_block_header(xattr_ref);
635                 block_data = (char *)block_header + block_size_rem;
636                 block_size_rem -= sizeof(struct ext4_xattr_header);
637
638                 xattr_ref->block.dirty = true;
639         } else {
640                 /* We don't need an extra block.*/
641                 if (xattr_ref->block_loaded) {
642                         block_header = EXT4_XATTR_BHDR(&xattr_ref->block);
643                         block_header->h_refcount =
644                             to_le32(to_le32(block_header->h_refcount) - 1);
645                         if (!block_header->h_refcount) {
646                                 ext4_xattr_try_free_block(xattr_ref);
647                                 block_header = NULL;
648                         } else {
649                                 block_entry =
650                                     EXT4_XATTR_BFIRST(&xattr_ref->block);
651                                 block_data =
652                                     (char *)block_header + block_size_rem;
653                                 block_size_rem -=
654                                     sizeof(struct ext4_xattr_header);
655                                 ext4_inode_set_file_acl(
656                                     xattr_ref->inode_ref->inode,
657                                     &xattr_ref->fs->sb, 0);
658
659                                 xattr_ref->inode_ref->dirty = true;
660                                 xattr_ref->block.dirty = true;
661                         }
662                 }
663         }
664         RB_FOREACH_SAFE(item, ext4_xattr_tree, &xattr_ref->root, save_item)
665         {
666                 if (EXT4_XATTR_SIZE(item->data_size) +
667                         EXT4_XATTR_LEN(item->name_len) <=
668                     inode_size_rem) {
669                         ibody_data = (char *)ibody_data -
670                                      EXT4_XATTR_SIZE(item->data_size);
671                         ext4_xattr_set_inode_entry(item, ibody_header, entry,
672                                                    ibody_data);
673                         memcpy(EXT4_XATTR_NAME(entry), item->name,
674                                item->name_len);
675                         memcpy(ibody_data, item->data, item->data_size);
676                         entry = EXT4_XATTR_NEXT(entry);
677                         inode_size_rem -= EXT4_XATTR_SIZE(item->data_size) +
678                                           EXT4_XATTR_LEN(item->name_len);
679
680                         xattr_ref->inode_ref->dirty = true;
681                         continue;
682                 }
683                 if (EXT4_XATTR_SIZE(item->data_size) +
684                         EXT4_XATTR_LEN(item->name_len) >
685                     block_size_rem) {
686                         ret = ENOSPC;
687                         goto Finish;
688                 }
689                 block_data =
690                     (char *)block_data - EXT4_XATTR_SIZE(item->data_size);
691                 ext4_xattr_set_block_entry(item, block_header, block_entry,
692                                            block_data);
693                 memcpy(EXT4_XATTR_NAME(block_entry), item->name,
694                        item->name_len);
695                 memcpy(block_data, item->data, item->data_size);
696                 block_entry = EXT4_XATTR_NEXT(block_entry);
697                 block_size_rem -= EXT4_XATTR_SIZE(item->data_size) +
698                                   EXT4_XATTR_LEN(item->name_len);
699
700                 block_modified = true;
701         }
702         xattr_ref->dirty = false;
703         if (block_modified) {
704                 ext4_xattr_rehash(block_header,
705                                   EXT4_XATTR_BFIRST(&xattr_ref->block));
706                 ext4_xattr_set_block_checksum(xattr_ref->inode_ref,
707                                               xattr_ref->block.lb_id,
708                                               block_header);
709                 xattr_ref->block.dirty = true;
710         }
711
712 Finish:
713         return ret;
714 }
715
716 void ext4_fs_xattr_iterate(struct ext4_xattr_ref *ref,
717                            int (*iter)(struct ext4_xattr_ref *ref,
718                                      struct ext4_xattr_item *item))
719 {
720         struct ext4_xattr_item *item;
721         if (!ref->iter_from)
722                 ref->iter_from = RB_MIN(ext4_xattr_tree, &ref->root);
723
724         RB_FOREACH_FROM(item, ext4_xattr_tree, ref->iter_from)
725         {
726                 int ret = EXT4_XATTR_ITERATE_CONT;
727                 if (iter)
728                         iter(ref, item);
729
730                 if (ret != EXT4_XATTR_ITERATE_CONT) {
731                         if (ret == EXT4_XATTR_ITERATE_STOP)
732                                 ref->iter_from = NULL;
733
734                         break;
735                 }
736         }
737 }
738
739 void ext4_fs_xattr_iterate_reset(struct ext4_xattr_ref *ref)
740 {
741         ref->iter_from = NULL;
742 }
743
744 int ext4_fs_set_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
745                       const char *name, size_t name_len, const void *data,
746                       size_t data_size, bool replace)
747 {
748         int ret = EOK;
749         struct ext4_xattr_item *item =
750             ext4_xattr_lookup_item(ref, name_index, name, name_len);
751         if (replace) {
752                 if (!item) {
753                         ret = ENODATA;
754                         goto Finish;
755                 }
756                 if (item->data_size != data_size)
757                         ret = ext4_xattr_resize_item(ref, item, data_size);
758
759                 if (ret != EOK) {
760                         goto Finish;
761                 }
762                 memcpy(item->data, data, data_size);
763         } else {
764                 if (item) {
765                         ret = EEXIST;
766                         goto Finish;
767                 }
768                 item = ext4_xattr_insert_item(ref, name_index, name, name_len,
769                                               data, data_size);
770                 if (!item)
771                         ret = ENOMEM;
772         }
773 Finish:
774         return ret;
775 }
776
777 int ext4_fs_remove_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
778                          const char *name, size_t name_len)
779 {
780         return ext4_xattr_remove_item(ref, name_index, name, name_len);
781 }
782
783 int ext4_fs_get_xattr(struct ext4_xattr_ref *ref, uint8_t name_index,
784                       const char *name, size_t name_len, void *buf, size_t buf_size,
785                       size_t *data_size)
786 {
787         int ret = EOK;
788         size_t item_size = 0;
789         struct ext4_xattr_item *item =
790             ext4_xattr_lookup_item(ref, name_index, name, name_len);
791
792         if (!item) {
793                 ret = ENODATA;
794                 goto Finish;
795         }
796         item_size = item->data_size;
797         if (buf_size > item_size)
798                 buf_size = item_size;
799
800         if (buf)
801                 memcpy(buf, item->data, buf_size);
802
803 Finish:
804         if (data_size)
805                 *data_size = item_size;
806
807         return ret;
808 }
809
810 int ext4_fs_get_xattr_ref(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
811                           struct ext4_xattr_ref *ref)
812 {
813         int rc;
814         ext4_fsblk_t xattr_block;
815         xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
816         RB_INIT(&ref->root);
817         ref->ea_size = 0;
818         ref->iter_from = NULL;
819         if (xattr_block) {
820                 rc = ext4_block_get(fs->bdev, &ref->block, xattr_block);
821                 if (rc != EOK)
822                         return EIO;
823
824                 ref->block_loaded = true;
825         } else
826                 ref->block_loaded = false;
827
828         ref->inode_ref = inode_ref;
829         ref->fs = fs;
830
831         rc = ext4_xattr_fetch(ref);
832         if (rc != EOK) {
833                 ext4_xattr_purge_items(ref);
834                 if (xattr_block)
835                         ext4_block_set(fs->bdev, &inode_ref->block);
836
837                 ref->block_loaded = false;
838                 return rc;
839         }
840         return EOK;
841 }
842
843 void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref)
844 {
845         ext4_xattr_write_to_disk(ref);
846         if (ref->block_loaded) {
847                 ext4_block_set(ref->fs->bdev, &ref->block);
848                 ref->block_loaded = false;
849         }
850         ext4_xattr_purge_items(ref);
851         ref->inode_ref = NULL;
852         ref->fs = NULL;
853 }
854
855 struct xattr_prefix {
856         const char *prefix;
857         uint8_t name_index;
858 };
859
860 static const struct xattr_prefix prefix_tbl[] = {
861     {"user.", EXT4_XATTR_INDEX_USER},
862     {"system.", EXT4_XATTR_INDEX_SYSTEM},
863     {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS},
864     {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT},
865     {NULL, 0},
866 };
867
868 const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len,
869                               uint8_t *name_index, size_t *name_len)
870 {
871         int i;
872         ext4_assert(name_index);
873         if (!full_name_len) {
874                 if (name_len)
875                         *name_len = 0;
876
877                 return NULL;
878         }
879
880         for (i = 0; prefix_tbl[i].prefix; i++) {
881                 size_t prefix_len = strlen(prefix_tbl[i].prefix);
882                 if (full_name_len >= prefix_len &&
883                     !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) {
884                         *name_index = prefix_tbl[i].name_index;
885                         if (name_len)
886                                 *name_len = full_name_len - prefix_len;
887
888                         return full_name + prefix_len;
889                 }
890         }
891         if (name_len)
892                 *name_len = 0;
893
894         return NULL;
895 }
896
897 const char *ext4_get_xattr_name_prefix(uint8_t name_index, size_t *ret_prefix_len)
898 {
899         int i;
900
901         for (i = 0; prefix_tbl[i].prefix; i++) {
902                 size_t prefix_len = strlen(prefix_tbl[i].prefix);
903                 if (prefix_tbl[i].name_index == name_index) {
904                         if (ret_prefix_len)
905                                 *ret_prefix_len = prefix_len;
906
907                         return prefix_tbl[i].prefix;
908                 }
909         }
910         if (ret_prefix_len)
911                 *ret_prefix_len = 0;
912
913         return NULL;
914 }
915
916 /**
917  * @}
918  */