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