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