2 * Copyright (c) 2017 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3 * Copyright (c) 2017 Kaho Ng (ngkaho1234@gmail.com)
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.
11 /** @addtogroup lwext4
16 * @brief Extended Attribute manipulation.
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>
25 #include <ext4_balloc.h>
26 #include <ext4_block_group.h>
27 #include <ext4_blockdev.h>
28 #include <ext4_crc32.h>
30 #include <ext4_inode.h>
31 #include <ext4_super.h>
32 #include <ext4_trans.h>
33 #include <ext4_xattr.h>
38 #if CONFIG_XATTR_ENABLE
42 * @brief Extended Attribute Manipulation
45 /* Extended Attribute(EA) */
47 /* Magic value in attribute blocks */
48 #define EXT4_XATTR_MAGIC 0xEA020000
50 /* Maximum number of references to one attribute block */
51 #define EXT4_XATTR_REFCOUNT_MAX 1024
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
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)) & \
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))
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( \
81 #define EXT4_XATTR_IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr) + 1))
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)
88 #define EXT4_ZERO_XATTR_VALUE ((void *)-1)
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 */
102 struct ext4_xattr_ibody_header {
103 uint32_t h_magic; /* magic number for identification */
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 */
118 #define NAME_HASH_SHIFT 5
119 #define VALUE_HASH_SHIFT 16
121 static inline void ext4_xattr_compute_hash(struct ext4_xattr_header *header,
122 struct ext4_xattr_entry *entry)
125 char *name = EXT4_XATTR_NAME(entry);
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++;
133 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
135 (uint32_t *)((char *)header + to_le16(entry->e_value_offs));
136 for (n = (to_le32(entry->e_value_size) + EXT4_XATTR_ROUND) >>
139 hash = (hash << VALUE_HASH_SHIFT) ^
140 (hash >> (8 * sizeof(hash) - VALUE_HASH_SHIFT)) ^
144 entry->e_hash = to_le32(hash);
147 #define BLOCK_HASH_SHIFT 16
150 * ext4_xattr_rehash()
152 * Re-compute the extended attribute hash value after an entry has changed.
154 static void ext4_xattr_rehash(struct ext4_xattr_header *header,
155 struct ext4_xattr_entry *entry)
157 struct ext4_xattr_entry *here;
160 ext4_xattr_compute_hash(header, entry);
161 here = EXT4_XATTR_ENTRY(header + 1);
162 while (!EXT4_XATTR_IS_LAST_ENTRY(here)) {
164 /* Block is not shared if an entry's hash value == 0 */
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);
173 header->h_hash = to_le32(hash);
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)
181 uint32_t checksum = 0;
182 uint64_t le64_blocknr = blocknr;
183 struct ext4_sblock *sb = &inode_ref->fs->sb;
185 if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
186 uint32_t orig_checksum;
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 */
193 ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
194 /* Then calculate crc32 checksum block number */
196 ext4_crc32c(checksum, &le64_blocknr, sizeof(le64_blocknr));
197 /* Finally calculate crc32 checksum against
198 * the entire xattr block */
200 ext4_crc32c(checksum, header, ext4_sb_get_block_size(sb));
201 header->h_checksum = orig_checksum;
206 #define ext4_xattr_block_checksum(...) 0
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)
213 struct ext4_sblock *sb = &inode_ref->fs->sb;
214 if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
218 ext4_xattr_block_checksum(inode_ref, blocknr, header);
221 struct xattr_prefix {
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},
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,
242 ext4_assert(name_index);
247 if (!full_name_len) {
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)) {
259 prefix_tbl[i].prefix[prefix_len - 1] == '.';
260 *name_index = prefix_tbl[i].name_index;
262 *name_len = full_name_len - prefix_len;
264 if (!(full_name_len - prefix_len) && require_name)
269 return full_name + prefix_len;
280 const char *ext4_get_xattr_name_prefix(uint8_t name_index,
281 size_t *ret_prefix_len)
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) {
289 *ret_prefix_len = prefix_len;
291 return prefix_tbl[i].prefix;
300 static const char ext4_xattr_empty_value;
303 * @brief Insert/Remove/Modify the given entry
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
309 * @return Return EOK when finished, ENOSPC when there is no enough space
311 static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
312 struct ext4_xattr_search *s, bool dry_run)
314 struct ext4_xattr_entry *last;
315 size_t free, min_offs = (char *)s->end - (char *)s->base,
316 name_len = i->name_len;
319 * If the entry is going to be removed but not found, return 0 to
322 if (!i->value && s->not_found)
325 /* Compute min_offs and last. */
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);
335 /* Calculate free space in the block. */
336 free = min_offs - ((char *)last - (char *)s->base) - sizeof(uint32_t);
338 free += EXT4_XATTR_SIZE(s->here->e_value_size) +
339 EXT4_XATTR_LEN(s->here->e_name_len);
342 /* See whether there is enough space to hold new entry */
344 EXT4_XATTR_SIZE(i->value_len) + EXT4_XATTR_LEN(name_len))
348 /* Return EOK now if we do not intend to modify the content. */
352 /* First remove the old entry's data part */
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;
358 EXT4_XATTR_SIZE(to_le32(s->here->e_value_size));
361 /* Remove the data part. */
362 memmove((char *)first_value + value_size, first_value,
363 (char *)value - (char *)first_value);
365 /* Zero the gap created */
366 memset(first_value, 0, value_size);
369 * Calculate the new min_offs after removal of the old
372 min_offs += value_size;
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.
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);
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);
390 /* If caller wants us to insert... */
394 value_offs = min_offs - EXT4_XATTR_SIZE(i->value_len);
399 struct ext4_xattr_entry *here = s->here;
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);
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);
413 /* Set valid last entry indicator */
414 *(uint32_t *)EXT4_XATTR_NEXT(last) = 0;
419 /* Insert the value's part */
421 memcpy((char *)s->base + value_offs, i->value,
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 +
428 0, EXT4_XATTR_SIZE(i->value_len) -
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));
440 /* Zero the gap created */
441 memset((char *)last - shift_offs + sizeof(uint32_t), 0,
450 static inline bool ext4_xattr_is_empty(struct ext4_xattr_search *s)
452 if (!EXT4_XATTR_IS_LAST_ENTRY(s->first))
459 * @brief Find the entry according to given information
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
465 static void ext4_xattr_find_entry(struct ext4_xattr_info *i,
466 struct ext4_xattr_search *s)
468 struct ext4_xattr_entry *entry = NULL;
474 * Find the wanted EA entry by simply comparing the namespace,
475 * name and the length of name.
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)) {
485 s->not_found = false;
486 i->value_len = to_le32(entry->e_value_size);
488 i->value = (char *)s->base +
489 to_le16(entry->e_value_offs);
499 * @brief Check whether the xattr block's content is valid
501 * @param inode_ref Inode reference
502 * @param block The block buffer to be validated
504 * @return true if @block is valid, false otherwise.
506 static bool ext4_xattr_is_block_valid(struct ext4_inode_ref *inode_ref,
507 struct ext4_block *block)
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);
517 * Check whether the magic number in the header is correct.
519 if (header->h_magic != to_le32(EXT4_XATTR_MAGIC))
523 * The in-kernel filesystem driver only supports 1 block currently.
525 if (header->h_blocks != to_le32(1))
529 * Check if those entries are maliciously corrupted to inflict harm
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))
538 if ((char *)base + to_le16(entry->e_value_offs) +
539 to_le32(entry->e_value_size) >
544 * The name length field should also be correct,
545 * also there should be an 4-byte zero entry at the
548 if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) >
552 if (to_le32(entry->e_value_size)) {
553 size_t offs = to_le16(entry->e_value_offs);
559 * Entry field and data field do not override each other.
561 if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t))
568 * @brief Check whether the inode buffer's content is valid
570 * @param inode_ref Inode reference
572 * @return true if the inode buffer is valid, false otherwise.
574 static bool ext4_xattr_is_ibody_valid(struct ext4_inode_ref *inode_ref)
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);
583 iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
584 entry = EXT4_XATTR_IFIRST(iheader);
586 end = (char *)inode_ref->inode + inode_size;
587 min_offs = (char *)end - (char *)base;
590 * Check whether the magic number in the header is correct.
592 if (iheader->h_magic != to_le32(EXT4_XATTR_MAGIC))
596 * Check if those entries are maliciously corrupted to inflict harm
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))
605 if ((char *)base + to_le16(entry->e_value_offs) +
606 to_le32(entry->e_value_size) >
611 * The name length field should also be correct,
612 * also there should be an 4-byte zero entry at the
615 if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) >
619 if (to_le32(entry->e_value_size)) {
620 size_t offs = to_le16(entry->e_value_offs);
626 * Entry field and data field do not override each other.
628 if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t))
635 * @brief An EA entry finder for inode buffer
637 struct ext4_xattr_finder {
639 * @brief The information of the EA entry to be find
641 struct ext4_xattr_info i;
644 * @brief Search context block of the current search
646 struct ext4_xattr_search s;
649 * @brief Inode reference to the corresponding inode
651 struct ext4_inode_ref *inode_ref;
654 static void ext4_xattr_ibody_initialize(struct ext4_inode_ref *inode_ref)
656 struct ext4_xattr_ibody_header *header;
657 struct ext4_fs *fs = inode_ref->fs;
659 ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
660 size_t inode_size = ext4_get16(&fs->sb, inode_size);
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;
671 * @brief Initialize a given xattr block
673 * @param inode_ref Inode reference
674 * @param block xattr block buffer
676 static void ext4_xattr_block_initialize(struct ext4_inode_ref *inode_ref,
677 struct ext4_block *block)
679 struct ext4_xattr_header *header;
680 struct ext4_fs *fs = inode_ref->fs;
682 memset(block->data, 0, ext4_sb_get_block_size(&fs->sb));
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);
689 ext4_trans_set_block_dirty(block->buf);
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)
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);
704 * @brief Find an EA entry inside a xattr block
706 * @param inode_ref Inode reference
707 * @param finder The caller-provided finder block with
709 * @param block The block buffer to be looked into
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.
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)
721 /* Initialize the caller-given finder */
722 finder->inode_ref = inode_ref;
723 memset(&finder->s, 0, sizeof(finder->s));
728 /* Check the validity of the buffer */
729 if (!ext4_xattr_is_block_valid(inode_ref, block))
732 ext4_xattr_block_init_search(inode_ref, &finder->s, block);
733 ext4_xattr_find_entry(&finder->i, &finder->s);
738 * @brief Find an EA entry inside an inode's extra space
740 * @param inode_ref Inode reference
741 * @param finder The caller-provided finder block with
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.
748 static int ext4_xattr_ibody_find_entry(struct ext4_inode_ref *inode_ref,
749 struct ext4_xattr_finder *finder)
751 struct ext4_fs *fs = inode_ref->fs;
752 struct ext4_xattr_ibody_header *iheader;
754 ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
755 size_t inode_size = ext4_get16(&fs->sb, inode_size);
757 /* Initialize the caller-given finder */
758 finder->inode_ref = inode_ref;
759 memset(&finder->s, 0, sizeof(finder->s));
762 * If there is no extra inode space
763 * set ext4_xattr_ibody_finder::s::not_found to true and return EOK
766 finder->s.not_found = true;
770 /* Check the validity of the buffer */
771 if (!ext4_xattr_is_ibody_valid(inode_ref))
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);
783 * @brief Try to allocate a block holding EA entries.
785 * @param inode_ref Inode reference
789 static int ext4_xattr_try_alloc_block(struct ext4_inode_ref *inode_ref)
793 ext4_fsblk_t xattr_block = 0;
795 ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb);
798 * Only allocate a xattr block when there is no xattr block
802 ext4_fsblk_t goal = ext4_fs_inode_to_goal_block(inode_ref);
804 ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block);
808 ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb,
817 * @brief Try to free a block holding EA entries.
819 * @param inode_ref Inode reference
823 static void ext4_xattr_try_free_block(struct ext4_inode_ref *inode_ref)
825 ext4_fsblk_t xattr_block;
827 ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb);
829 * Free the xattr block used by the inode when there is one.
832 ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb,
834 ext4_balloc_free_block(inode_ref, xattr_block);
835 inode_ref->dirty = true;
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.
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
851 int ext4_xattr_list(struct ext4_inode_ref *inode_ref,
852 struct ext4_xattr_list_entry *list, size_t *list_len)
856 struct ext4_fs *fs = inode_ref->fs;
857 struct ext4_xattr_ibody_header *iheader;
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);
868 * If there is extra inode space and the xattr buffer in the
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);
876 * The format of the list should be like this:
878 * name_len indicates the length in bytes of the name
879 * of the EA entry. The string is null-terminated.
881 * list->name => (char *)(list + 1);
882 * list->next => (void *)((char *)(list + 1) + name_len + 1);
884 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
885 entry = EXT4_XATTR_NEXT(entry)) {
886 size_t name_len = entry->e_name_len;
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),
895 list_prev->next = list;
898 list = (struct ext4_xattr_list_entry
899 *)(list->name + name_len + 1);
903 * Size calculation by pointer arithmetics.
906 (char *)((struct ext4_xattr_list_entry *)0 + 1) +
908 (char *)(struct ext4_xattr_list_entry *)0;
913 * If there is a xattr block used by the inode
916 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
923 * As we don't allow the content in the block being invalid,
926 if (!ext4_xattr_is_block_valid(inode_ref, &block)) {
931 entry = EXT4_XATTR_BFIRST(&block);
934 * The format of the list should be like this:
936 * name_len indicates the length in bytes of the name
937 * of the EA entry. The string is null-terminated.
939 * list->name => (char *)(list + 1);
940 * list->next => (void *)((char *)(list + 1) + name_len + 1);
942 * Same as above actually.
944 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
945 entry = EXT4_XATTR_NEXT(entry)) {
946 size_t name_len = entry->e_name_len;
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),
955 list_prev->next = list;
958 list = (struct ext4_xattr_list_entry
959 *)(list->name + name_len + 1);
963 * Size calculation by pointer arithmetics.
966 (char *)((struct ext4_xattr_list_entry *)0 + 1) +
968 (char *)(struct ext4_xattr_list_entry *)0;
972 list_prev->next = NULL;
974 if (ret == EOK && list_len)
978 ext4_block_set(fs->bdev, &block);
984 * @brief Query EA entry's value with given name-index and name
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
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,
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);
1010 i.name_index = name_index;
1012 i.name_len = name_len;
1019 ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
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) {
1028 (char *)ibody_finder.s.base + value_offs;
1029 memcpy(buf, data_loc,
1030 (buf_len < value_len) ? buf_len : value_len);
1033 struct ext4_block block;
1035 /* Return ENODATA if there is no EA block */
1042 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
1046 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1049 ext4_block_set(fs->bdev, &block);
1053 /* Return ENODATA if entry is not found */
1054 if (block_finder.s.not_found) {
1055 ext4_block_set(fs->bdev, &block);
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) {
1064 (char *)block_finder.s.base + value_offs;
1065 memcpy(buf, data_loc,
1066 (buf_len < value_len) ? buf_len : value_len);
1070 * Free the xattr block buffer returned by
1071 * ext4_xattr_block_find_entry.
1073 ext4_block_set(fs->bdev, &block);
1077 if (ret == EOK && data_len)
1078 *data_len = value_len;
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
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
1094 * @return Error code
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)
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);
1108 *orig_block = block->lb_id;
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);
1117 /* Allocate a new block to be used by this inode */
1118 ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block);
1122 ret = ext4_trans_block_get(fs->bdev, new_block, xattr_block);
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));
1131 * Decrement the reference count of the original xattr block
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);
1138 header = EXT4_XATTR_BHDR(new_block);
1139 header->h_refcount = to_le32(1);
1147 ext4_balloc_free_block(inode_ref, xattr_block);
1150 * Modify the in-inode pointer to point to the new xattr block
1152 ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, xattr_block);
1153 inode_ref->dirty = true;
1161 * @brief Given an EA entry's name, remove the EA entry
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
1168 * @return Error code
1170 int ext4_xattr_remove(struct ext4_inode_ref *inode_ref, uint8_t name_index,
1171 const char *name, size_t name_len)
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;
1183 xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1185 i.name_index = name_index;
1187 i.name_len = name_len;
1194 ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1198 if (ibody_finder.s.not_found && xattr_block) {
1199 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
1203 block_loaded = true;
1205 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1210 /* Return ENODATA if entry is not found */
1211 if (block_finder.s.not_found) {
1219 bool allocated = false;
1220 struct ext4_block new_block;
1223 * There will be no effect when the xattr block is only referenced
1226 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1227 &xattr_block, &allocated);
1232 /* Prevent double-freeing */
1233 block_loaded = false;
1237 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1242 /* Now remove the entry */
1243 ext4_xattr_set_entry(&i, &block_finder.s, false);
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);
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,
1258 ext4_trans_set_block_dirty(new_block.buf);
1259 ext4_block_set(fs->bdev, &new_block);
1263 /* Now remove the entry */
1264 ext4_xattr_set_entry(&i, &block_finder.s, false);
1265 inode_ref->dirty = true;
1269 ext4_block_set(fs->bdev, &block);
1275 * @brief Insert/overwrite an EA entry into/in a xattr block
1277 * @param inode_ref Inode reference
1278 * @param i The information of the given EA entry
1280 * @return Error code
1282 static int ext4_xattr_block_set(struct ext4_inode_ref *inode_ref,
1283 struct ext4_xattr_info *i,
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;
1292 orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1294 ext4_assert(i->value);
1295 if (!orig_xattr_block) {
1296 struct ext4_xattr_search s;
1297 struct ext4_xattr_header *header;
1299 /* If insertion of new entry is not allowed... */
1305 ret = ext4_xattr_try_alloc_block(inode_ref);
1310 ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1311 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1313 ext4_xattr_try_free_block(inode_ref);
1317 ext4_xattr_block_initialize(inode_ref, &block);
1318 ext4_xattr_block_init_search(inode_ref, &s, &block);
1320 ret = ext4_xattr_set_entry(i, &s, false);
1322 header = EXT4_XATTR_BHDR(&block);
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,
1331 ext4_trans_set_block_dirty(block.buf);
1333 ext4_block_set(fs->bdev, &block);
1335 ext4_xattr_try_free_block(inode_ref);
1338 struct ext4_xattr_finder finder;
1339 struct ext4_xattr_header *header;
1341 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1345 header = EXT4_XATTR_BHDR(&block);
1348 * Consider the following case when insertion of new
1349 * entry is not allowed
1351 if (to_le32(header->h_refcount) > 1 && no_insert) {
1353 * There are other people referencing the
1356 ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1358 ext4_block_set(fs->bdev, &block);
1361 if (finder.s.not_found) {
1362 ext4_block_set(fs->bdev, &block);
1369 * There will be no effect when the xattr block is only referenced
1372 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1373 &orig_xattr_block, &allocated);
1375 ext4_block_set(fs->bdev, &block);
1380 ext4_block_set(fs->bdev, &block);
1384 ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1386 ext4_block_set(fs->bdev, &block);
1390 ret = ext4_xattr_set_entry(i, &finder.s, false);
1392 header = EXT4_XATTR_BHDR(&block);
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,
1401 ext4_trans_set_block_dirty(block.buf);
1403 ext4_block_set(fs->bdev, &block);
1410 * @brief Remove an EA entry from a xattr block
1412 * @param inode_ref Inode reference
1413 * @param i The information of the given EA entry
1415 * @return Error code
1417 static int ext4_xattr_block_remove(struct ext4_inode_ref *inode_ref,
1418 struct ext4_xattr_info *i)
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);
1430 ext4_assert(orig_xattr_block);
1431 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1436 * There will be no effect when the xattr block is only referenced
1439 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1440 &orig_xattr_block, &allocated);
1442 ext4_block_set(fs->bdev, &block);
1447 ext4_block_set(fs->bdev, &block);
1451 ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1453 if (!finder.s.not_found) {
1455 ret = ext4_xattr_set_entry(i, &finder.s, false);
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,
1464 ext4_trans_set_block_dirty(block.buf);
1467 ext4_block_set(fs->bdev, &block);
1473 * @brief Insert an EA entry into a given inode reference
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
1482 * @return Error code
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,
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);
1497 i.name_index = name_index;
1499 i.name_len = name_len;
1500 i.value = (value_len) ? value : &ext4_xattr_empty_value;
1501 i.value_len = value_len;
1505 orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1508 * Even if entry is not found, search context block inside the
1509 * finder is still valid and can be used to insert entry.
1511 ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1513 ext4_xattr_ibody_initialize(inode_ref);
1514 ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1517 if (ibody_finder.s.not_found) {
1518 if (orig_xattr_block) {
1520 ret = ext4_xattr_block_set(inode_ref, &i, true);
1523 else if (ret == ENODATA)
1525 else if (ret != EOK)
1533 /* Only try to set entry in ibody if inode is sufficiently large */
1535 ret = ext4_xattr_set_entry(&i, &ibody_finder.s, false);
1539 if (ret == ENOSPC) {
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;
1548 } else if (ret == EOK) {
1550 ret = ext4_xattr_block_remove(inode_ref, &i);
1552 inode_ref->dirty = true;