Make ext4_xattr & ext4_extents GPL licensed
[lwext4.git] / src / ext4_xattr.c
1 /*
2  * Copyright (c) 2017 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * Copyright (c) 2017 Kaho Ng (ngkaho1234@gmail.com)
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  */
10
11 /** @addtogroup lwext4
12  * @{
13  */
14 /**
15  * @file  ext4_xattr.c
16  * @brief Extended Attribute manipulation.
17  */
18
19 #include <ext4_config.h>
20 #include <ext4_debug.h>
21 #include <ext4_errno.h>
22 #include <ext4_misc.h>
23 #include <ext4_types.h>
24
25 #include <ext4_balloc.h>
26 #include <ext4_block_group.h>
27 #include <ext4_blockdev.h>
28 #include <ext4_crc32.h>
29 #include <ext4_fs.h>
30 #include <ext4_inode.h>
31 #include <ext4_super.h>
32 #include <ext4_trans.h>
33 #include <ext4_xattr.h>
34
35 #include <stdlib.h>
36 #include <string.h>
37
38 /**
39  * @file  ext4_xattr.c
40  * @brief Extended Attribute Manipulation
41  */
42
43 /* Extended Attribute(EA) */
44
45 /* Magic value in attribute blocks */
46 #define EXT4_XATTR_MAGIC        0xEA020000
47
48 /* Maximum number of references to one attribute block */
49 #define EXT4_XATTR_REFCOUNT_MAX     1024
50
51 /* Name indexes */
52 #define EXT4_XATTR_INDEX_USER           1
53 #define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS   2
54 #define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT  3
55 #define EXT4_XATTR_INDEX_TRUSTED        4
56 #define EXT4_XATTR_INDEX_LUSTRE         5
57 #define EXT4_XATTR_INDEX_SECURITY           6
58 #define EXT4_XATTR_INDEX_SYSTEM         7
59 #define EXT4_XATTR_INDEX_RICHACL        8
60 #define EXT4_XATTR_INDEX_ENCRYPTION     9
61
62 #define EXT4_XATTR_PAD_BITS 2
63 #define EXT4_XATTR_PAD (1 << EXT4_XATTR_PAD_BITS)
64 #define EXT4_XATTR_ROUND (EXT4_XATTR_PAD - 1)
65 #define EXT4_XATTR_LEN(name_len)                                               \
66     (((name_len) + EXT4_XATTR_ROUND + sizeof(struct ext4_xattr_entry)) &   \
67      ~EXT4_XATTR_ROUND)
68 #define EXT4_XATTR_NEXT(entry)                                                 \
69     ((struct ext4_xattr_entry *)((char *)(entry) +                         \
70                      EXT4_XATTR_LEN((entry)->e_name_len)))
71 #define EXT4_XATTR_SIZE(size) (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
72 #define EXT4_XATTR_NAME(entry) ((char *)((entry) + 1))
73
74 #define EXT4_XATTR_IHDR(sb, raw_inode)                                         \
75     ((struct ext4_xattr_ibody_header *)((char *)raw_inode +                \
76                         EXT4_GOOD_OLD_INODE_SIZE +         \
77                         ext4_inode_get_extra_isize(        \
78                         sb, raw_inode)))
79 #define EXT4_XATTR_IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr) + 1))
80
81 #define EXT4_XATTR_BHDR(block) ((struct ext4_xattr_header *)((block)->data))
82 #define EXT4_XATTR_ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
83 #define EXT4_XATTR_BFIRST(block) EXT4_XATTR_ENTRY(EXT4_XATTR_BHDR(block) + 1)
84 #define EXT4_XATTR_IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0)
85
86 #define EXT4_ZERO_XATTR_VALUE ((void *)-1)
87
88 #pragma pack(push, 1)
89
90 struct ext4_xattr_header {
91     uint32_t h_magic;   /* magic number for identification */
92     uint32_t h_refcount;    /* reference count */
93     uint32_t h_blocks;  /* number of disk blocks used */
94     uint32_t h_hash;        /* hash value of all attributes */
95     uint32_t h_checksum;    /* crc32c(uuid+id+xattrblock) */
96                 /* id = inum if refcount=1, blknum otherwise */
97     uint32_t h_reserved[3]; /* zero right now */
98 };
99
100 struct ext4_xattr_ibody_header {
101     uint32_t h_magic;   /* magic number for identification */
102 };
103
104 struct ext4_xattr_entry {
105     uint8_t e_name_len; /* length of name */
106     uint8_t e_name_index;   /* attribute name index */
107     uint16_t e_value_offs;  /* offset in disk block of value */
108     uint32_t e_value_block; /* disk block attribute is stored on (n/i) */
109     uint32_t e_value_size;  /* size of attribute value */
110     uint32_t e_hash;        /* hash value of name and value */
111 };
112
113 #pragma pack(pop)
114
115
116 #define NAME_HASH_SHIFT 5
117 #define VALUE_HASH_SHIFT 16
118
119 static inline void ext4_xattr_compute_hash(struct ext4_xattr_header *header,
120                                            struct ext4_xattr_entry *entry)
121 {
122         uint32_t hash = 0;
123         char *name = EXT4_XATTR_NAME(entry);
124         int n;
125
126         for (n = 0; n < entry->e_name_len; n++) {
127                 hash = (hash << NAME_HASH_SHIFT) ^
128                        (hash >> (8 * sizeof(hash) - NAME_HASH_SHIFT)) ^ *name++;
129         }
130
131         if (entry->e_value_block == 0 && entry->e_value_size != 0) {
132                 uint32_t *value =
133                     (uint32_t *)((char *)header + to_le16(entry->e_value_offs));
134                 for (n = (to_le32(entry->e_value_size) + EXT4_XATTR_ROUND) >>
135                          EXT4_XATTR_PAD_BITS;
136                      n; n--) {
137                         hash = (hash << VALUE_HASH_SHIFT) ^
138                                (hash >> (8 * sizeof(hash) - VALUE_HASH_SHIFT)) ^
139                                to_le32(*value++);
140                 }
141         }
142         entry->e_hash = to_le32(hash);
143 }
144
145 #define BLOCK_HASH_SHIFT 16
146
147 /*
148  * ext4_xattr_rehash()
149  *
150  * Re-compute the extended attribute hash value after an entry has changed.
151  */
152 static void ext4_xattr_rehash(struct ext4_xattr_header *header,
153                               struct ext4_xattr_entry *entry)
154 {
155         struct ext4_xattr_entry *here;
156         uint32_t hash = 0;
157
158         ext4_xattr_compute_hash(header, entry);
159         here = EXT4_XATTR_ENTRY(header + 1);
160         while (!EXT4_XATTR_IS_LAST_ENTRY(here)) {
161                 if (!here->e_hash) {
162                         /* Block is not shared if an entry's hash value == 0 */
163                         hash = 0;
164                         break;
165                 }
166                 hash = (hash << BLOCK_HASH_SHIFT) ^
167                        (hash >> (8 * sizeof(hash) - BLOCK_HASH_SHIFT)) ^
168                        to_le32(here->e_hash);
169                 here = EXT4_XATTR_NEXT(here);
170         }
171         header->h_hash = to_le32(hash);
172 }
173
174 #if CONFIG_META_CSUM_ENABLE
175 static uint32_t ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref,
176                                           ext4_fsblk_t blocknr,
177                                           struct ext4_xattr_header *header)
178 {
179         uint32_t checksum = 0;
180         uint64_t le64_blocknr = blocknr;
181         struct ext4_sblock *sb = &inode_ref->fs->sb;
182
183         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
184                 uint32_t orig_checksum;
185
186                 /* Preparation: temporarily set bg checksum to 0 */
187                 orig_checksum = header->h_checksum;
188                 header->h_checksum = 0;
189                 /* First calculate crc32 checksum against fs uuid */
190                 checksum =
191                     ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
192                 /* Then calculate crc32 checksum block number */
193                 checksum =
194                     ext4_crc32c(checksum, &le64_blocknr, sizeof(le64_blocknr));
195                 /* Finally calculate crc32 checksum against
196                  * the entire xattr block */
197                 checksum =
198                     ext4_crc32c(checksum, header, ext4_sb_get_block_size(sb));
199                 header->h_checksum = orig_checksum;
200         }
201         return checksum;
202 }
203 #else
204 #define ext4_xattr_block_checksum(...) 0
205 #endif
206
207 static void ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref,
208                                           ext4_fsblk_t blocknr __unused,
209                                           struct ext4_xattr_header *header)
210 {
211         struct ext4_sblock *sb = &inode_ref->fs->sb;
212         if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
213                 return;
214
215         header->h_checksum =
216             ext4_xattr_block_checksum(inode_ref, blocknr, header);
217 }
218
219 struct xattr_prefix {
220         const char *prefix;
221         uint8_t name_index;
222 };
223
224 static const struct xattr_prefix prefix_tbl[] = {
225     {"user.", EXT4_XATTR_INDEX_USER},
226     {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS},
227     {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT},
228     {"trusted.", EXT4_XATTR_INDEX_TRUSTED},
229     {"security.", EXT4_XATTR_INDEX_SECURITY},
230     {"system.", EXT4_XATTR_INDEX_SYSTEM},
231     {"system.richacl", EXT4_XATTR_INDEX_RICHACL},
232     {NULL, 0},
233 };
234
235 const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len,
236                                     uint8_t *name_index, size_t *name_len,
237                                     bool *found)
238 {
239         int i;
240         ext4_assert(name_index);
241         ext4_assert(found);
242
243         *found = false;
244
245         if (!full_name_len) {
246                 if (name_len)
247                         *name_len = 0;
248
249                 return NULL;
250         }
251
252         for (i = 0; prefix_tbl[i].prefix; i++) {
253                 size_t prefix_len = strlen(prefix_tbl[i].prefix);
254                 if (full_name_len >= prefix_len &&
255                     !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) {
256                         bool require_name =
257                             prefix_tbl[i].prefix[prefix_len - 1] == '.';
258                         *name_index = prefix_tbl[i].name_index;
259                         if (name_len)
260                                 *name_len = full_name_len - prefix_len;
261
262                         if (!(full_name_len - prefix_len) && require_name)
263                                 return NULL;
264
265                         *found = true;
266                         if (require_name)
267                                 return full_name + prefix_len;
268
269                         return NULL;
270                 }
271         }
272         if (name_len)
273                 *name_len = 0;
274
275         return NULL;
276 }
277
278 const char *ext4_get_xattr_name_prefix(uint8_t name_index,
279                                        size_t *ret_prefix_len)
280 {
281         int i;
282
283         for (i = 0; prefix_tbl[i].prefix; i++) {
284                 size_t prefix_len = strlen(prefix_tbl[i].prefix);
285                 if (prefix_tbl[i].name_index == name_index) {
286                         if (ret_prefix_len)
287                                 *ret_prefix_len = prefix_len;
288
289                         return prefix_tbl[i].prefix;
290                 }
291         }
292         if (ret_prefix_len)
293                 *ret_prefix_len = 0;
294
295         return NULL;
296 }
297
298 static const char ext4_xattr_empty_value;
299
300 /**
301  * @brief Insert/Remove/Modify the given entry
302  *
303  * @param i The information of the given EA entry
304  * @param s Search context block
305  * @param dry_run Do not modify the content of the buffer
306  *
307  * @return Return EOK when finished, ENOSPC when there is no enough space
308  */
309 static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
310                                 struct ext4_xattr_search *s, bool dry_run)
311 {
312         struct ext4_xattr_entry *last;
313         size_t free, min_offs = (char *)s->end - (char *)s->base,
314                      name_len = i->name_len;
315
316         /*
317          * If the entry is going to be removed but not found, return 0 to
318          * indicate success.
319          */
320         if (!i->value && s->not_found)
321                 return EOK;
322
323         /* Compute min_offs and last. */
324         last = s->first;
325         for (; !EXT4_XATTR_IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
326                 if (last->e_value_size) {
327                         size_t offs = to_le16(last->e_value_offs);
328                         if (offs < min_offs)
329                                 min_offs = offs;
330                 }
331         }
332
333         /* Calculate free space in the block. */
334         free = min_offs - ((char *)last - (char *)s->base) - sizeof(uint32_t);
335         if (!s->not_found)
336                 free += EXT4_XATTR_SIZE(s->here->e_value_size) +
337                         EXT4_XATTR_LEN(s->here->e_name_len);
338
339         if (i->value) {
340                 /* See whether there is enough space to hold new entry */
341                 if (free <
342                     EXT4_XATTR_SIZE(i->value_len) + EXT4_XATTR_LEN(name_len))
343                         return ENOSPC;
344         }
345
346         /* Return EOK now if we do not intend to modify the content. */
347         if (dry_run)
348                 return EOK;
349
350         /* First remove the old entry's data part */
351         if (!s->not_found) {
352                 size_t value_offs = to_le16(s->here->e_value_offs);
353                 void *value = (char *)s->base + value_offs;
354                 void *first_value = (char *)s->base + min_offs;
355                 size_t value_size =
356                     EXT4_XATTR_SIZE(to_le32(s->here->e_value_size));
357
358                 if (value_offs) {
359                         /* Remove the data part. */
360                         memmove((char *)first_value + value_size, first_value,
361                                 (char *)value - (char *)first_value);
362
363                         /* Zero the gap created */
364                         memset(first_value, 0, value_size);
365
366                         /*
367                          * Calculate the new min_offs after removal of the old
368                          * entry's data part
369                          */
370                         min_offs += value_size;
371                 }
372
373                 /*
374                  * Adjust the value offset of entries which has value offset
375                  * prior to the s->here. The offset of these entries won't be
376                  * shifted if the size of the entry we removed is zero.
377                  */
378                 for (last = s->first; !EXT4_XATTR_IS_LAST_ENTRY(last);
379                      last = EXT4_XATTR_NEXT(last)) {
380                         size_t offs = to_le16(last->e_value_offs);
381
382                         /* For zero-value-length entry, offs will be zero. */
383                         if (offs < value_offs)
384                                 last->e_value_offs = to_le16(offs + value_size);
385                 }
386         }
387
388         /* If caller wants us to insert... */
389         if (i->value) {
390                 size_t value_offs;
391                 if (i->value_len)
392                         value_offs = min_offs - EXT4_XATTR_SIZE(i->value_len);
393                 else
394                         value_offs = 0;
395
396                 if (!s->not_found) {
397                         struct ext4_xattr_entry *here = s->here;
398
399                         /* Reuse the current entry we have got */
400                         here->e_value_offs = to_le16(value_offs);
401                         here->e_value_size = to_le32(i->value_len);
402                 } else {
403                         /* Insert a new entry */
404                         last->e_name_len = (uint8_t)name_len;
405                         last->e_name_index = i->name_index;
406                         last->e_value_offs = to_le16(value_offs);
407                         last->e_value_block = 0;
408                         last->e_value_size = to_le32(i->value_len);
409                         memcpy(EXT4_XATTR_NAME(last), i->name, name_len);
410
411                         /* Set valid last entry indicator */
412                         *(uint32_t *)EXT4_XATTR_NEXT(last) = 0;
413
414                         s->here = last;
415                 }
416
417                 /* Insert the value's part */
418                 if (value_offs) {
419                         memcpy((char *)s->base + value_offs, i->value,
420                                i->value_len);
421
422                         /* Clear the padding bytes if there is */
423                         if (EXT4_XATTR_SIZE(i->value_len) != i->value_len)
424                                 memset((char *)s->base + value_offs +
425                                            i->value_len,
426                                        0, EXT4_XATTR_SIZE(i->value_len) -
427                                               i->value_len);
428                 }
429         } else {
430                 size_t shift_offs;
431
432                 /* Remove the whole entry */
433                 shift_offs = (char *)EXT4_XATTR_NEXT(s->here) - (char *)s->here;
434                 memmove(s->here, EXT4_XATTR_NEXT(s->here),
435                         (char *)last + sizeof(uint32_t) -
436                             (char *)EXT4_XATTR_NEXT(s->here));
437
438                 /* Zero the gap created */
439                 memset((char *)last - shift_offs + sizeof(uint32_t), 0,
440                        shift_offs);
441
442                 s->here = NULL;
443         }
444
445         return EOK;
446 }
447
448 static inline bool ext4_xattr_is_empty(struct ext4_xattr_search *s)
449 {
450         if (!EXT4_XATTR_IS_LAST_ENTRY(s->first))
451                 return false;
452
453         return true;
454 }
455
456 /**
457  * @brief Find the entry according to given information
458  *
459  * @param i The information of the EA entry to be found,
460  *          including name_index, name and the length of name
461  * @param s Search context block
462  */
463 static void ext4_xattr_find_entry(struct ext4_xattr_info *i,
464                                   struct ext4_xattr_search *s)
465 {
466         struct ext4_xattr_entry *entry = NULL;
467
468         s->not_found = true;
469         s->here = NULL;
470
471         /*
472          * Find the wanted EA entry by simply comparing the namespace,
473          * name and the length of name.
474          */
475         for (entry = s->first; !EXT4_XATTR_IS_LAST_ENTRY(entry);
476              entry = EXT4_XATTR_NEXT(entry)) {
477                 size_t name_len = entry->e_name_len;
478                 const char *name = EXT4_XATTR_NAME(entry);
479                 if (name_len == i->name_len &&
480                     entry->e_name_index == i->name_index &&
481                     !memcmp(name, i->name, name_len)) {
482                         s->here = entry;
483                         s->not_found = false;
484                         i->value_len = to_le32(entry->e_value_size);
485                         if (i->value_len)
486                                 i->value = (char *)s->base +
487                                            to_le16(entry->e_value_offs);
488                         else
489                                 i->value = NULL;
490
491                         return;
492                 }
493         }
494 }
495
496 /**
497  * @brief Check whether the xattr block's content is valid
498  *
499  * @param inode_ref Inode reference
500  * @param block     The block buffer to be validated
501  *
502  * @return true if @block is valid, false otherwise.
503  */
504 static bool ext4_xattr_is_block_valid(struct ext4_inode_ref *inode_ref,
505                                       struct ext4_block *block)
506 {
507
508         void *base = block->data,
509              *end = block->data + ext4_sb_get_block_size(&inode_ref->fs->sb);
510         size_t min_offs = (char *)end - (char *)base;
511         struct ext4_xattr_header *header = EXT4_XATTR_BHDR(block);
512         struct ext4_xattr_entry *entry = EXT4_XATTR_BFIRST(block);
513
514         /*
515          * Check whether the magic number in the header is correct.
516          */
517         if (header->h_magic != to_le32(EXT4_XATTR_MAGIC))
518                 return false;
519
520         /*
521          * The in-kernel filesystem driver only supports 1 block currently.
522          */
523         if (header->h_blocks != to_le32(1))
524                 return false;
525
526         /*
527          * Check if those entries are maliciously corrupted to inflict harm
528          * upon us.
529          */
530         for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
531              entry = EXT4_XATTR_NEXT(entry)) {
532                 if (!to_le32(entry->e_value_size) &&
533                     to_le16(entry->e_value_offs))
534                         return false;
535
536                 if ((char *)base + to_le16(entry->e_value_offs) +
537                         to_le32(entry->e_value_size) >
538                     (char *)end)
539                         return false;
540
541                 /*
542                  * The name length field should also be correct,
543                  * also there should be an 4-byte zero entry at the
544                  * end.
545                  */
546                 if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) >
547                     (char *)end)
548                         return false;
549
550                 if (to_le32(entry->e_value_size)) {
551                         size_t offs = to_le16(entry->e_value_offs);
552                         if (offs < min_offs)
553                                 min_offs = offs;
554                 }
555         }
556         /*
557          * Entry field and data field do not override each other.
558          */
559         if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t))
560                 return false;
561
562         return true;
563 }
564
565 /**
566  * @brief Check whether the inode buffer's content is valid
567  *
568  * @param inode_ref Inode reference
569  *
570  * @return true if the inode buffer is valid, false otherwise.
571  */
572 static bool ext4_xattr_is_ibody_valid(struct ext4_inode_ref *inode_ref)
573 {
574         size_t min_offs;
575         void *base, *end;
576         struct ext4_fs *fs = inode_ref->fs;
577         struct ext4_xattr_ibody_header *iheader;
578         struct ext4_xattr_entry *entry;
579         size_t inode_size = ext4_get16(&fs->sb, inode_size);
580
581         iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
582         entry = EXT4_XATTR_IFIRST(iheader);
583         base = iheader;
584         end = (char *)inode_ref->inode + inode_size;
585         min_offs = (char *)end - (char *)base;
586
587         /*
588          * Check whether the magic number in the header is correct.
589          */
590         if (iheader->h_magic != to_le32(EXT4_XATTR_MAGIC))
591                 return false;
592
593         /*
594          * Check if those entries are maliciously corrupted to inflict harm
595          * upon us.
596          */
597         for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
598              entry = EXT4_XATTR_NEXT(entry)) {
599                 if (!to_le32(entry->e_value_size) &&
600                     to_le16(entry->e_value_offs))
601                         return false;
602
603                 if ((char *)base + to_le16(entry->e_value_offs) +
604                         to_le32(entry->e_value_size) >
605                     (char *)end)
606                         return false;
607
608                 /*
609                  * The name length field should also be correct,
610                  * also there should be an 4-byte zero entry at the
611                  * end.
612                  */
613                 if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) >
614                     (char *)end)
615                         return false;
616
617                 if (to_le32(entry->e_value_size)) {
618                         size_t offs = to_le16(entry->e_value_offs);
619                         if (offs < min_offs)
620                                 min_offs = offs;
621                 }
622         }
623         /*
624          * Entry field and data field do not override each other.
625          */
626         if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t))
627                 return false;
628
629         return true;
630 }
631
632 /**
633  * @brief An EA entry finder for inode buffer
634  */
635 struct ext4_xattr_finder {
636         /**
637          * @brief The information of the EA entry to be find
638          */
639         struct ext4_xattr_info i;
640
641         /**
642          * @brief Search context block of the current search
643          */
644         struct ext4_xattr_search s;
645
646         /**
647          * @brief Inode reference to the corresponding inode
648          */
649         struct ext4_inode_ref *inode_ref;
650 };
651
652 static void ext4_xattr_ibody_initialize(struct ext4_inode_ref *inode_ref)
653 {
654         struct ext4_xattr_ibody_header *header;
655         struct ext4_fs *fs = inode_ref->fs;
656         size_t extra_isize =
657             ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
658         size_t inode_size = ext4_get16(&fs->sb, inode_size);
659         if (!extra_isize)
660                 return;
661
662         header = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
663         memset(header, 0, inode_size - EXT4_GOOD_OLD_INODE_SIZE - extra_isize);
664         header->h_magic = to_le32(EXT4_XATTR_MAGIC);
665         inode_ref->dirty = true;
666 }
667
668 /**
669  * @brief Initialize a given xattr block
670  *
671  * @param inode_ref Inode reference
672  * @param block xattr block buffer
673  */
674 static void ext4_xattr_block_initialize(struct ext4_inode_ref *inode_ref,
675                                         struct ext4_block *block)
676 {
677         struct ext4_xattr_header *header;
678         struct ext4_fs *fs = inode_ref->fs;
679
680         memset(block->data, 0, ext4_sb_get_block_size(&fs->sb));
681
682         header = EXT4_XATTR_BHDR(block);
683         header->h_magic = to_le32(EXT4_XATTR_MAGIC);
684         header->h_refcount = to_le32(1);
685         header->h_blocks = to_le32(1);
686
687         ext4_trans_set_block_dirty(block->buf);
688 }
689
690 static void ext4_xattr_block_init_search(struct ext4_inode_ref *inode_ref,
691                                          struct ext4_xattr_search *s,
692                                          struct ext4_block *block)
693 {
694         s->base = block->data;
695         s->end = block->data + ext4_sb_get_block_size(&inode_ref->fs->sb);
696         s->first = EXT4_XATTR_BFIRST(block);
697         s->here = NULL;
698         s->not_found = true;
699 }
700
701 /**
702  * @brief Find an EA entry inside a xattr block
703  *
704  * @param inode_ref Inode reference
705  * @param finder    The caller-provided finder block with
706  *                  information filled
707  * @param block     The block buffer to be looked into
708  *
709  * @return Return EOK no matter the entry is found or not.
710  *         If the IO operation or the buffer validation failed,
711  *         return other value.
712  */
713 static int ext4_xattr_block_find_entry(struct ext4_inode_ref *inode_ref,
714                                        struct ext4_xattr_finder *finder,
715                                        struct ext4_block *block)
716 {
717         int ret = EOK;
718
719         /* Initialize the caller-given finder */
720         finder->inode_ref = inode_ref;
721         memset(&finder->s, 0, sizeof(finder->s));
722
723         if (ret != EOK)
724                 return ret;
725
726         /* Check the validity of the buffer */
727         if (!ext4_xattr_is_block_valid(inode_ref, block))
728                 return EIO;
729
730         ext4_xattr_block_init_search(inode_ref, &finder->s, block);
731         ext4_xattr_find_entry(&finder->i, &finder->s);
732         return EOK;
733 }
734
735 /**
736  * @brief Find an EA entry inside an inode's extra space
737  *
738  * @param inode_ref Inode reference
739  * @param finder    The caller-provided finder block with
740  *                  information filled
741  *
742  * @return Return EOK no matter the entry is found or not.
743  *         If the IO operation or the buffer validation failed,
744  *         return other value.
745  */
746 static int ext4_xattr_ibody_find_entry(struct ext4_inode_ref *inode_ref,
747                                        struct ext4_xattr_finder *finder)
748 {
749         struct ext4_fs *fs = inode_ref->fs;
750         struct ext4_xattr_ibody_header *iheader;
751         size_t extra_isize =
752             ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
753         size_t inode_size = ext4_get16(&fs->sb, inode_size);
754
755         /* Initialize the caller-given finder */
756         finder->inode_ref = inode_ref;
757         memset(&finder->s, 0, sizeof(finder->s));
758
759         /*
760          * If there is no extra inode space
761          * set ext4_xattr_ibody_finder::s::not_found to true and return EOK
762          */
763         if (!extra_isize) {
764                 finder->s.not_found = true;
765                 return EOK;
766         }
767
768         /* Check the validity of the buffer */
769         if (!ext4_xattr_is_ibody_valid(inode_ref))
770                 return EIO;
771
772         iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
773         finder->s.base = EXT4_XATTR_IFIRST(iheader);
774         finder->s.end = (char *)inode_ref->inode + inode_size;
775         finder->s.first = EXT4_XATTR_IFIRST(iheader);
776         ext4_xattr_find_entry(&finder->i, &finder->s);
777         return EOK;
778 }
779
780 /**
781  * @brief Try to allocate a block holding EA entries.
782  *
783  * @param inode_ref Inode reference
784  *
785  * @return Error code
786  */
787 static int ext4_xattr_try_alloc_block(struct ext4_inode_ref *inode_ref)
788 {
789         int ret = EOK;
790
791         ext4_fsblk_t xattr_block = 0;
792         xattr_block =
793             ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb);
794
795         /*
796          * Only allocate a xattr block when there is no xattr block
797          * used by the inode.
798          */
799         if (!xattr_block) {
800                 ext4_fsblk_t goal = ext4_fs_inode_to_goal_block(inode_ref);
801
802                 ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block);
803                 if (ret != EOK)
804                         goto Finish;
805
806                 ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb,
807                                         xattr_block);
808         }
809
810 Finish:
811         return ret;
812 }
813
814 /**
815  * @brief Try to free a block holding EA entries.
816  *
817  * @param inode_ref Inode reference
818  *
819  * @return Error code
820  */
821 static void ext4_xattr_try_free_block(struct ext4_inode_ref *inode_ref)
822 {
823         ext4_fsblk_t xattr_block;
824         xattr_block =
825             ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb);
826         /*
827          * Free the xattr block used by the inode when there is one.
828          */
829         if (xattr_block) {
830                 ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb,
831                                         0);
832                 ext4_balloc_free_block(inode_ref, xattr_block);
833                 inode_ref->dirty = true;
834         }
835 }
836
837 /**
838  * @brief Put a list of EA entries into a caller-provided buffer
839  *        In order to make sure that @list buffer can fit in the data,
840  *        the routine should be called twice.
841  *
842  * @param inode_ref Inode reference
843  * @param list A caller-provided buffer to hold a list of EA entries.
844  *             If list == NULL, list_len will contain the size of
845  *             the buffer required to hold these entries
846  * @param list_len The length of the data written to @list
847  * @return Error code
848  */
849 int ext4_xattr_list(struct ext4_inode_ref *inode_ref,
850                     struct ext4_xattr_list_entry *list, size_t *list_len)
851 {
852         int ret = EOK;
853         size_t buf_len = 0;
854         struct ext4_fs *fs = inode_ref->fs;
855         struct ext4_xattr_ibody_header *iheader;
856         size_t extra_isize =
857             ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
858         struct ext4_block block;
859         bool block_loaded = false;
860         ext4_fsblk_t xattr_block = 0;
861         struct ext4_xattr_entry *entry;
862         struct ext4_xattr_list_entry *list_prev = NULL;
863         xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
864
865         /*
866          * If there is extra inode space and the xattr buffer in the
867          * inode is valid.
868          */
869         if (extra_isize && ext4_xattr_is_ibody_valid(inode_ref)) {
870                 iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
871                 entry = EXT4_XATTR_IFIRST(iheader);
872
873                 /*
874                  * The format of the list should be like this:
875                  *
876                  * name_len indicates the length in bytes of the name
877                  * of the EA entry. The string is null-terminated.
878                  *
879                  * list->name => (char *)(list + 1);
880                  * list->next => (void *)((char *)(list + 1) + name_len + 1);
881                  */
882                 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
883                      entry = EXT4_XATTR_NEXT(entry)) {
884                         size_t name_len = entry->e_name_len;
885                         if (list) {
886                                 list->name_index = entry->e_name_index;
887                                 list->name_len = name_len;
888                                 list->name = (char *)(list + 1);
889                                 memcpy(list->name, EXT4_XATTR_NAME(entry),
890                                        list->name_len);
891
892                                 if (list_prev)
893                                         list_prev->next = list;
894
895                                 list_prev = list;
896                                 list = (struct ext4_xattr_list_entry
897                                             *)(list->name + name_len + 1);
898                         }
899
900                         /*
901                          * Size calculation by pointer arithmetics.
902                          */
903                         buf_len +=
904                             (char *)((struct ext4_xattr_list_entry *)0 + 1) +
905                             name_len + 1 -
906                             (char *)(struct ext4_xattr_list_entry *)0;
907                 }
908         }
909
910         /*
911          * If there is a xattr block used by the inode
912          */
913         if (xattr_block) {
914                 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
915                 if (ret != EOK)
916                         goto out;
917
918                 block_loaded = true;
919
920                 /*
921                  * As we don't allow the content in the block being invalid,
922                  * bail out.
923                  */
924                 if (!ext4_xattr_is_block_valid(inode_ref, &block)) {
925                         ret = EIO;
926                         goto out;
927                 }
928
929                 entry = EXT4_XATTR_BFIRST(&block);
930
931                 /*
932                  * The format of the list should be like this:
933                  *
934                  * name_len indicates the length in bytes of the name
935                  * of the EA entry. The string is null-terminated.
936                  *
937                  * list->name => (char *)(list + 1);
938                  * list->next => (void *)((char *)(list + 1) + name_len + 1);
939                  *
940                  * Same as above actually.
941                  */
942                 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
943                      entry = EXT4_XATTR_NEXT(entry)) {
944                         size_t name_len = entry->e_name_len;
945                         if (list) {
946                                 list->name_index = entry->e_name_index;
947                                 list->name_len = name_len;
948                                 list->name = (char *)(list + 1);
949                                 memcpy(list->name, EXT4_XATTR_NAME(entry),
950                                        list->name_len);
951
952                                 if (list_prev)
953                                         list_prev->next = list;
954
955                                 list_prev = list;
956                                 list = (struct ext4_xattr_list_entry
957                                             *)(list->name + name_len + 1);
958                         }
959
960                         /*
961                          * Size calculation by pointer arithmetics.
962                          */
963                         buf_len +=
964                             (char *)((struct ext4_xattr_list_entry *)0 + 1) +
965                             name_len + 1 -
966                             (char *)(struct ext4_xattr_list_entry *)0;
967                 }
968         }
969         if (list_prev)
970                 list_prev->next = NULL;
971 out:
972         if (ret == EOK && list_len)
973                 *list_len = buf_len;
974
975         if (block_loaded)
976                 ext4_block_set(fs->bdev, &block);
977
978         return ret;
979 }
980
981 /**
982  * @brief Query EA entry's value with given name-index and name
983  *
984  * @param inode_ref Inode reference
985  * @param name_index Name-index
986  * @param name Name of the EA entry to be queried
987  * @param name_len Length of name in bytes
988  * @param buf Output buffer to hold content
989  * @param buf_len Output buffer's length
990  * @param data_len The length of data of the EA entry found
991  *
992  * @return Error code
993  */
994 int ext4_xattr_get(struct ext4_inode_ref *inode_ref, uint8_t name_index,
995                    const char *name, size_t name_len, void *buf, size_t buf_len,
996                    size_t *data_len)
997 {
998         int ret = EOK;
999         struct ext4_xattr_finder ibody_finder;
1000         struct ext4_xattr_finder block_finder;
1001         struct ext4_xattr_info i;
1002         size_t value_len = 0;
1003         size_t value_offs = 0;
1004         struct ext4_fs *fs = inode_ref->fs;
1005         ext4_fsblk_t xattr_block;
1006         xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1007
1008         i.name_index = name_index;
1009         i.name = name;
1010         i.name_len = name_len;
1011         i.value = 0;
1012         i.value_len = 0;
1013         if (data_len)
1014                 *data_len = 0;
1015
1016         ibody_finder.i = i;
1017         ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1018         if (ret != EOK)
1019                 goto out;
1020
1021         if (!ibody_finder.s.not_found) {
1022                 value_len = to_le32(ibody_finder.s.here->e_value_size);
1023                 value_offs = to_le32(ibody_finder.s.here->e_value_offs);
1024                 if (buf_len && buf) {
1025                         void *data_loc =
1026                             (char *)ibody_finder.s.base + value_offs;
1027                         memcpy(buf, data_loc,
1028                                (buf_len < value_len) ? buf_len : value_len);
1029                 }
1030         } else {
1031                 struct ext4_block block;
1032
1033                 /* Return ENODATA if there is no EA block */
1034                 if (!xattr_block) {
1035                         ret = ENODATA;
1036                         goto out;
1037                 }
1038
1039                 block_finder.i = i;
1040                 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
1041                 if (ret != EOK)
1042                         goto out;
1043
1044                 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1045                                                   &block);
1046                 if (ret != EOK) {
1047                         ext4_block_set(fs->bdev, &block);
1048                         goto out;
1049                 }
1050
1051                 /* Return ENODATA if entry is not found */
1052                 if (block_finder.s.not_found) {
1053                         ext4_block_set(fs->bdev, &block);
1054                         ret = ENODATA;
1055                         goto out;
1056                 }
1057
1058                 value_len = to_le32(block_finder.s.here->e_value_size);
1059                 value_offs = to_le32(block_finder.s.here->e_value_offs);
1060                 if (buf_len && buf) {
1061                         void *data_loc =
1062                             (char *)block_finder.s.base + value_offs;
1063                         memcpy(buf, data_loc,
1064                                (buf_len < value_len) ? buf_len : value_len);
1065                 }
1066
1067                 /*
1068                  * Free the xattr block buffer returned by
1069                  * ext4_xattr_block_find_entry.
1070                  */
1071                 ext4_block_set(fs->bdev, &block);
1072         }
1073
1074 out:
1075         if (ret == EOK && data_len)
1076                 *data_len = value_len;
1077
1078         return ret;
1079 }
1080
1081 /**
1082  * @brief Try to copy the content of an xattr block to a newly-allocated
1083  *        block. If the operation fails, the block buffer provided by
1084  *        caller will be freed
1085  *
1086  * @param inode_ref Inode reference
1087  * @param block The block buffer reference
1088  * @param new_block The newly-allocated block buffer reference
1089  * @param orig_block The block number of @block
1090  * @param allocated a new block is allocated
1091  *
1092  * @return Error code
1093  */
1094 static int ext4_xattr_copy_new_block(struct ext4_inode_ref *inode_ref,
1095                                      struct ext4_block *block,
1096                                      struct ext4_block *new_block,
1097                                      ext4_fsblk_t *orig_block, bool *allocated)
1098 {
1099         int ret = EOK;
1100         ext4_fsblk_t xattr_block = 0;
1101         struct ext4_xattr_header *header;
1102         struct ext4_fs *fs = inode_ref->fs;
1103         header = EXT4_XATTR_BHDR(block);
1104
1105         if (orig_block)
1106                 *orig_block = block->lb_id;
1107
1108         if (allocated)
1109                 *allocated = false;
1110
1111         /* Only do copy when a block is referenced by more than one inode. */
1112         if (to_le32(header->h_refcount) > 1) {
1113                 ext4_fsblk_t goal = ext4_fs_inode_to_goal_block(inode_ref);
1114
1115                 /* Allocate a new block to be used by this inode */
1116                 ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block);
1117                 if (ret != EOK)
1118                         goto out;
1119
1120                 ret = ext4_trans_block_get(fs->bdev, new_block, xattr_block);
1121                 if (ret != EOK)
1122                         goto out;
1123
1124                 /* Copy the content of the whole block */
1125                 memcpy(new_block->data, block->data,
1126                        ext4_sb_get_block_size(&inode_ref->fs->sb));
1127
1128                 /*
1129                  * Decrement the reference count of the original xattr block
1130                  * by one
1131                  */
1132                 header->h_refcount = to_le32(to_le32(header->h_refcount) - 1);
1133                 ext4_trans_set_block_dirty(block->buf);
1134                 ext4_trans_set_block_dirty(new_block->buf);
1135
1136                 header = EXT4_XATTR_BHDR(new_block);
1137                 header->h_refcount = to_le32(1);
1138
1139                 if (allocated)
1140                         *allocated = true;
1141         }
1142 out:
1143         if (xattr_block) {
1144                 if (ret != EOK)
1145                         ext4_balloc_free_block(inode_ref, xattr_block);
1146                 else {
1147                         /*
1148                          * Modify the in-inode pointer to point to the new xattr block
1149                          */
1150                         ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, xattr_block);
1151                         inode_ref->dirty = true;
1152                 }
1153         }
1154
1155         return ret;
1156 }
1157
1158 /**
1159  * @brief Given an EA entry's name, remove the EA entry
1160  *
1161  * @param inode_ref Inode reference
1162  * @param name_index Name-index
1163  * @param name Name of the EA entry to be removed
1164  * @param name_len Length of name in bytes
1165  *
1166  * @return Error code
1167  */
1168 int ext4_xattr_remove(struct ext4_inode_ref *inode_ref, uint8_t name_index,
1169                       const char *name, size_t name_len)
1170 {
1171         int ret = EOK;
1172         struct ext4_block block;
1173         struct ext4_xattr_finder ibody_finder;
1174         struct ext4_xattr_finder block_finder;
1175         bool use_block = false;
1176         bool block_loaded = false;
1177         struct ext4_xattr_info i;
1178         struct ext4_fs *fs = inode_ref->fs;
1179         ext4_fsblk_t xattr_block;
1180
1181         xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1182
1183         i.name_index = name_index;
1184         i.name = name;
1185         i.name_len = name_len;
1186         i.value = NULL;
1187         i.value_len = 0;
1188
1189         ibody_finder.i = i;
1190         block_finder.i = i;
1191
1192         ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1193         if (ret != EOK)
1194                 goto out;
1195
1196         if (ibody_finder.s.not_found && xattr_block) {
1197                 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
1198                 if (ret != EOK)
1199                         goto out;
1200
1201                 block_loaded = true;
1202                 block_finder.i = i;
1203                 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1204                                                   &block);
1205                 if (ret != EOK)
1206                         goto out;
1207
1208                 /* Return ENODATA if entry is not found */
1209                 if (block_finder.s.not_found) {
1210                         ret = ENODATA;
1211                         goto out;
1212                 }
1213                 use_block = true;
1214         }
1215
1216         if (use_block) {
1217                 bool allocated = false;
1218                 struct ext4_block new_block;
1219
1220                 /*
1221                  * There will be no effect when the xattr block is only referenced
1222                  * once.
1223                  */
1224                 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1225                                                 &xattr_block, &allocated);
1226                 if (ret != EOK)
1227                         goto out;
1228
1229                 if (!allocated) {
1230                         /* Prevent double-freeing */
1231                         block_loaded = false;
1232                         new_block = block;
1233                 }
1234
1235                 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1236                                                   &new_block);
1237                 if (ret != EOK)
1238                         goto out;
1239
1240                 /* Now remove the entry */
1241                 ext4_xattr_set_entry(&i, &block_finder.s, false);
1242
1243                 if (ext4_xattr_is_empty(&block_finder.s)) {
1244                         ext4_block_set(fs->bdev, &new_block);
1245                         ext4_xattr_try_free_block(inode_ref);
1246                 } else {
1247                         struct ext4_xattr_header *header =
1248                             EXT4_XATTR_BHDR(&new_block);
1249                         header = EXT4_XATTR_BHDR(&new_block);
1250                         ext4_assert(block_finder.s.first);
1251                         ext4_xattr_rehash(header, block_finder.s.first);
1252                         ext4_xattr_set_block_checksum(inode_ref,
1253                                                       block.lb_id,
1254                                                       header);
1255
1256                         ext4_trans_set_block_dirty(new_block.buf);
1257                         ext4_block_set(fs->bdev, &new_block);
1258                 }
1259
1260         } else {
1261                 /* Now remove the entry */
1262                 ext4_xattr_set_entry(&i, &block_finder.s, false);
1263                 inode_ref->dirty = true;
1264         }
1265 out:
1266         if (block_loaded)
1267                 ext4_block_set(fs->bdev, &block);
1268
1269         return ret;
1270 }
1271
1272 /**
1273  * @brief Insert/overwrite an EA entry into/in a xattr block
1274  *
1275  * @param inode_ref Inode reference
1276  * @param i The information of the given EA entry
1277  *
1278  * @return Error code
1279  */
1280 static int ext4_xattr_block_set(struct ext4_inode_ref *inode_ref,
1281                                 struct ext4_xattr_info *i,
1282                                 bool no_insert)
1283 {
1284         int ret = EOK;
1285         bool allocated = false;
1286         struct ext4_fs *fs = inode_ref->fs;
1287         struct ext4_block block, new_block;
1288         ext4_fsblk_t orig_xattr_block;
1289
1290         orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1291
1292         ext4_assert(i->value);
1293         if (!orig_xattr_block) {
1294                 struct ext4_xattr_search s;
1295                 struct ext4_xattr_header *header;
1296
1297                 /* If insertion of new entry is not allowed... */
1298                 if (no_insert) {
1299                         ret = ENODATA;
1300                         goto out;
1301                 }
1302
1303                 ret = ext4_xattr_try_alloc_block(inode_ref);
1304                 if (ret != EOK)
1305                         goto out;
1306
1307                 orig_xattr_block =
1308                     ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1309                 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1310                 if (ret != EOK) {
1311                         ext4_xattr_try_free_block(inode_ref);
1312                         goto out;
1313                 }
1314
1315                 ext4_xattr_block_initialize(inode_ref, &block);
1316                 ext4_xattr_block_init_search(inode_ref, &s, &block);
1317
1318                 ret = ext4_xattr_set_entry(i, &s, false);
1319                 if (ret == EOK) {
1320                         header = EXT4_XATTR_BHDR(&block);
1321
1322                         ext4_assert(s.here);
1323                         ext4_assert(s.first);
1324                         ext4_xattr_compute_hash(header, s.here);
1325                         ext4_xattr_rehash(header, s.first);
1326                         ext4_xattr_set_block_checksum(inode_ref,
1327                                                       block.lb_id,
1328                                                       header);
1329                         ext4_trans_set_block_dirty(block.buf);
1330                 }
1331                 ext4_block_set(fs->bdev, &block);
1332                 if (ret != EOK)
1333                         ext4_xattr_try_free_block(inode_ref);
1334
1335         } else {
1336                 struct ext4_xattr_finder finder;
1337                 struct ext4_xattr_header *header;
1338                 finder.i = *i;
1339                 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1340                 if (ret != EOK)
1341                         goto out;
1342
1343                 header = EXT4_XATTR_BHDR(&block);
1344
1345                 /*
1346                  * Consider the following case when insertion of new
1347                  * entry is not allowed
1348                  */
1349                 if (to_le32(header->h_refcount) > 1 && no_insert) {
1350                         /*
1351                          * There are other people referencing the
1352                          * same xattr block
1353                          */
1354                         ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1355                         if (ret != EOK) {
1356                                 ext4_block_set(fs->bdev, &block);
1357                                 goto out;
1358                         }
1359                         if (finder.s.not_found) {
1360                                 ext4_block_set(fs->bdev, &block);
1361                                 ret = ENODATA;
1362                                 goto out;
1363                         }
1364                 }
1365
1366                 /*
1367                  * There will be no effect when the xattr block is only referenced
1368                  * once.
1369                  */
1370                 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1371                                                 &orig_xattr_block, &allocated);
1372                 if (ret != EOK) {
1373                         ext4_block_set(fs->bdev, &block);
1374                         goto out;
1375                 }
1376
1377                 if (allocated) {
1378                         ext4_block_set(fs->bdev, &block);
1379                         new_block = block;
1380                 }
1381
1382                 ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1383                 if (ret != EOK) {
1384                         ext4_block_set(fs->bdev, &block);
1385                         goto out;
1386                 }
1387
1388                 ret = ext4_xattr_set_entry(i, &finder.s, false);
1389                 if (ret == EOK) {
1390                         header = EXT4_XATTR_BHDR(&block);
1391
1392                         ext4_assert(finder.s.here);
1393                         ext4_assert(finder.s.first);
1394                         ext4_xattr_compute_hash(header, finder.s.here);
1395                         ext4_xattr_rehash(header, finder.s.first);
1396                         ext4_xattr_set_block_checksum(inode_ref,
1397                                                       block.lb_id,
1398                                                       header);
1399                         ext4_trans_set_block_dirty(block.buf);
1400                 }
1401                 ext4_block_set(fs->bdev, &block);
1402         }
1403 out:
1404         return ret;
1405 }
1406
1407 /**
1408  * @brief Remove an EA entry from a xattr block
1409  *
1410  * @param inode_ref Inode reference
1411  * @param i The information of the given EA entry
1412  *
1413  * @return Error code
1414  */
1415 static int ext4_xattr_block_remove(struct ext4_inode_ref *inode_ref,
1416                                    struct ext4_xattr_info *i)
1417 {
1418         int ret = EOK;
1419         bool allocated = false;
1420         const void *value = i->value;
1421         struct ext4_fs *fs = inode_ref->fs;
1422         struct ext4_xattr_finder finder;
1423         struct ext4_block block, new_block;
1424         struct ext4_xattr_header *header;
1425         ext4_fsblk_t orig_xattr_block;
1426         orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1427
1428         ext4_assert(orig_xattr_block);
1429         ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1430         if (ret != EOK)
1431                 goto out;
1432
1433         /*
1434          * There will be no effect when the xattr block is only referenced
1435          * once.
1436          */
1437         ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1438                                         &orig_xattr_block, &allocated);
1439         if (ret != EOK) {
1440                 ext4_block_set(fs->bdev, &block);
1441                 goto out;
1442         }
1443
1444         if (allocated) {
1445                 ext4_block_set(fs->bdev, &block);
1446                 block = new_block;
1447         }
1448
1449         ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1450
1451         if (!finder.s.not_found) {
1452                 i->value = NULL;
1453                 ret = ext4_xattr_set_entry(i, &finder.s, false);
1454                 i->value = value;
1455
1456                 header = EXT4_XATTR_BHDR(&block);
1457                 ext4_assert(finder.s.first);
1458                 ext4_xattr_rehash(header, finder.s.first);
1459                 ext4_xattr_set_block_checksum(inode_ref,
1460                                               block.lb_id,
1461                                               header);
1462                 ext4_trans_set_block_dirty(block.buf);
1463         }
1464
1465         ext4_block_set(fs->bdev, &block);
1466 out:
1467         return ret;
1468 }
1469
1470 /**
1471  * @brief Insert an EA entry into a given inode reference
1472  *
1473  * @param inode_ref Inode reference
1474  * @param name_index Name-index
1475  * @param name Name of the EA entry to be inserted
1476  * @param name_len Length of name in bytes
1477  * @param value Input buffer to hold content
1478  * @param value_len Length of input content
1479  *
1480  * @return Error code
1481  */
1482 int ext4_xattr_set(struct ext4_inode_ref *inode_ref, uint8_t name_index,
1483                    const char *name, size_t name_len, const void *value,
1484                    size_t value_len)
1485 {
1486         int ret = EOK;
1487         struct ext4_fs *fs = inode_ref->fs;
1488         struct ext4_xattr_finder ibody_finder;
1489         struct ext4_xattr_info i;
1490         bool block_found = false;
1491         ext4_fsblk_t orig_xattr_block;
1492         size_t extra_isize =
1493             ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
1494
1495         i.name_index = name_index;
1496         i.name = name;
1497         i.name_len = name_len;
1498         i.value = (value_len) ? value : &ext4_xattr_empty_value;
1499         i.value_len = value_len;
1500
1501         ibody_finder.i = i;
1502
1503         orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1504
1505         /*
1506          * Even if entry is not found, search context block inside the
1507          * finder is still valid and can be used to insert entry.
1508          */
1509         ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1510         if (ret != EOK) {
1511                 ext4_xattr_ibody_initialize(inode_ref);
1512                 ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1513         }
1514
1515         if (ibody_finder.s.not_found) {
1516                 if (orig_xattr_block) {
1517                         block_found = true;
1518                         ret = ext4_xattr_block_set(inode_ref, &i, true);
1519                         if (ret == ENOSPC)
1520                                 goto try_insert;
1521                         else if (ret == ENODATA)
1522                                 goto try_insert;
1523                         else if (ret != EOK)
1524                                 goto out;
1525
1526                 } else
1527                         goto try_insert;
1528
1529         } else {
1530         try_insert:
1531                 /* Only try to set entry in ibody if inode is sufficiently large */
1532                 if (extra_isize)
1533                         ret = ext4_xattr_set_entry(&i, &ibody_finder.s, false);
1534                 else
1535                         ret = ENOSPC;
1536
1537                 if (ret == ENOSPC) {
1538                         if (!block_found) {
1539                                 ret = ext4_xattr_block_set(inode_ref, &i, false);
1540                                 ibody_finder.i.value = NULL;
1541                                 ext4_xattr_set_entry(&ibody_finder.i,
1542                                                      &ibody_finder.s, false);
1543                                 inode_ref->dirty = true;
1544                         }
1545
1546                 } else if (ret == EOK) {
1547                         if (block_found)
1548                                 ret = ext4_xattr_block_remove(inode_ref, &i);
1549
1550                         inode_ref->dirty = true;
1551                 }
1552         }
1553
1554 out:
1555         return ret;
1556 }
1557
1558 /**
1559  * @}
1560  */