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>
40 * @brief Extended Attribute Manipulation
43 /* Extended Attribute(EA) */
45 /* Magic value in attribute blocks */
46 #define EXT4_XATTR_MAGIC 0xEA020000
48 /* Maximum number of references to one attribute block */
49 #define EXT4_XATTR_REFCOUNT_MAX 1024
52 #define EXT4_XATTR_INDEX_USER 1
53 #define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS 2
54 #define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT 3
55 #define EXT4_XATTR_INDEX_TRUSTED 4
56 #define EXT4_XATTR_INDEX_LUSTRE 5
57 #define EXT4_XATTR_INDEX_SECURITY 6
58 #define EXT4_XATTR_INDEX_SYSTEM 7
59 #define EXT4_XATTR_INDEX_RICHACL 8
60 #define EXT4_XATTR_INDEX_ENCRYPTION 9
62 #define EXT4_XATTR_PAD_BITS 2
63 #define EXT4_XATTR_PAD (1 << EXT4_XATTR_PAD_BITS)
64 #define EXT4_XATTR_ROUND (EXT4_XATTR_PAD - 1)
65 #define EXT4_XATTR_LEN(name_len) \
66 (((name_len) + EXT4_XATTR_ROUND + sizeof(struct ext4_xattr_entry)) & \
68 #define EXT4_XATTR_NEXT(entry) \
69 ((struct ext4_xattr_entry *)((char *)(entry) + \
70 EXT4_XATTR_LEN((entry)->e_name_len)))
71 #define EXT4_XATTR_SIZE(size) (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
72 #define EXT4_XATTR_NAME(entry) ((char *)((entry) + 1))
74 #define EXT4_XATTR_IHDR(sb, raw_inode) \
75 ((struct ext4_xattr_ibody_header *)((char *)raw_inode + \
76 EXT4_GOOD_OLD_INODE_SIZE + \
77 ext4_inode_get_extra_isize( \
79 #define EXT4_XATTR_IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr) + 1))
81 #define EXT4_XATTR_BHDR(block) ((struct ext4_xattr_header *)((block)->data))
82 #define EXT4_XATTR_ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
83 #define EXT4_XATTR_BFIRST(block) EXT4_XATTR_ENTRY(EXT4_XATTR_BHDR(block) + 1)
84 #define EXT4_XATTR_IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0)
86 #define EXT4_ZERO_XATTR_VALUE ((void *)-1)
90 struct ext4_xattr_header {
91 uint32_t h_magic; /* magic number for identification */
92 uint32_t h_refcount; /* reference count */
93 uint32_t h_blocks; /* number of disk blocks used */
94 uint32_t h_hash; /* hash value of all attributes */
95 uint32_t h_checksum; /* crc32c(uuid+id+xattrblock) */
96 /* id = inum if refcount=1, blknum otherwise */
97 uint32_t h_reserved[3]; /* zero right now */
100 struct ext4_xattr_ibody_header {
101 uint32_t h_magic; /* magic number for identification */
104 struct ext4_xattr_entry {
105 uint8_t e_name_len; /* length of name */
106 uint8_t e_name_index; /* attribute name index */
107 uint16_t e_value_offs; /* offset in disk block of value */
108 uint32_t e_value_block; /* disk block attribute is stored on (n/i) */
109 uint32_t e_value_size; /* size of attribute value */
110 uint32_t e_hash; /* hash value of name and value */
116 #define NAME_HASH_SHIFT 5
117 #define VALUE_HASH_SHIFT 16
119 static inline void ext4_xattr_compute_hash(struct ext4_xattr_header *header,
120 struct ext4_xattr_entry *entry)
123 char *name = EXT4_XATTR_NAME(entry);
126 for (n = 0; n < entry->e_name_len; n++) {
127 hash = (hash << NAME_HASH_SHIFT) ^
128 (hash >> (8 * sizeof(hash) - NAME_HASH_SHIFT)) ^ *name++;
131 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
133 (uint32_t *)((char *)header + to_le16(entry->e_value_offs));
134 for (n = (to_le32(entry->e_value_size) + EXT4_XATTR_ROUND) >>
137 hash = (hash << VALUE_HASH_SHIFT) ^
138 (hash >> (8 * sizeof(hash) - VALUE_HASH_SHIFT)) ^
142 entry->e_hash = to_le32(hash);
145 #define BLOCK_HASH_SHIFT 16
148 * ext4_xattr_rehash()
150 * Re-compute the extended attribute hash value after an entry has changed.
152 static void ext4_xattr_rehash(struct ext4_xattr_header *header,
153 struct ext4_xattr_entry *entry)
155 struct ext4_xattr_entry *here;
158 ext4_xattr_compute_hash(header, entry);
159 here = EXT4_XATTR_ENTRY(header + 1);
160 while (!EXT4_XATTR_IS_LAST_ENTRY(here)) {
162 /* Block is not shared if an entry's hash value == 0 */
166 hash = (hash << BLOCK_HASH_SHIFT) ^
167 (hash >> (8 * sizeof(hash) - BLOCK_HASH_SHIFT)) ^
168 to_le32(here->e_hash);
169 here = EXT4_XATTR_NEXT(here);
171 header->h_hash = to_le32(hash);
174 #if CONFIG_META_CSUM_ENABLE
175 static uint32_t ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref,
176 ext4_fsblk_t blocknr,
177 struct ext4_xattr_header *header)
179 uint32_t checksum = 0;
180 uint64_t le64_blocknr = blocknr;
181 struct ext4_sblock *sb = &inode_ref->fs->sb;
183 if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
184 uint32_t orig_checksum;
186 /* Preparation: temporarily set bg checksum to 0 */
187 orig_checksum = header->h_checksum;
188 header->h_checksum = 0;
189 /* First calculate crc32 checksum against fs uuid */
191 ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid));
192 /* Then calculate crc32 checksum block number */
194 ext4_crc32c(checksum, &le64_blocknr, sizeof(le64_blocknr));
195 /* Finally calculate crc32 checksum against
196 * the entire xattr block */
198 ext4_crc32c(checksum, header, ext4_sb_get_block_size(sb));
199 header->h_checksum = orig_checksum;
204 #define ext4_xattr_block_checksum(...) 0
207 static void ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref,
208 ext4_fsblk_t blocknr __unused,
209 struct ext4_xattr_header *header)
211 struct ext4_sblock *sb = &inode_ref->fs->sb;
212 if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
216 ext4_xattr_block_checksum(inode_ref, blocknr, header);
219 struct xattr_prefix {
224 static const struct xattr_prefix prefix_tbl[] = {
225 {"user.", EXT4_XATTR_INDEX_USER},
226 {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS},
227 {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT},
228 {"trusted.", EXT4_XATTR_INDEX_TRUSTED},
229 {"security.", EXT4_XATTR_INDEX_SECURITY},
230 {"system.", EXT4_XATTR_INDEX_SYSTEM},
231 {"system.richacl", EXT4_XATTR_INDEX_RICHACL},
235 const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len,
236 uint8_t *name_index, size_t *name_len,
240 ext4_assert(name_index);
245 if (!full_name_len) {
252 for (i = 0; prefix_tbl[i].prefix; i++) {
253 size_t prefix_len = strlen(prefix_tbl[i].prefix);
254 if (full_name_len >= prefix_len &&
255 !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) {
257 prefix_tbl[i].prefix[prefix_len - 1] == '.';
258 *name_index = prefix_tbl[i].name_index;
260 *name_len = full_name_len - prefix_len;
262 if (!(full_name_len - prefix_len) && require_name)
267 return full_name + prefix_len;
278 const char *ext4_get_xattr_name_prefix(uint8_t name_index,
279 size_t *ret_prefix_len)
283 for (i = 0; prefix_tbl[i].prefix; i++) {
284 size_t prefix_len = strlen(prefix_tbl[i].prefix);
285 if (prefix_tbl[i].name_index == name_index) {
287 *ret_prefix_len = prefix_len;
289 return prefix_tbl[i].prefix;
298 static const char ext4_xattr_empty_value;
301 * @brief Insert/Remove/Modify the given entry
303 * @param i The information of the given EA entry
304 * @param s Search context block
305 * @param dry_run Do not modify the content of the buffer
307 * @return Return EOK when finished, ENOSPC when there is no enough space
309 static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
310 struct ext4_xattr_search *s, bool dry_run)
312 struct ext4_xattr_entry *last;
313 size_t free, min_offs = (char *)s->end - (char *)s->base,
314 name_len = i->name_len;
317 * If the entry is going to be removed but not found, return 0 to
320 if (!i->value && s->not_found)
323 /* Compute min_offs and last. */
325 for (; !EXT4_XATTR_IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
326 if (last->e_value_size) {
327 size_t offs = to_le16(last->e_value_offs);
333 /* Calculate free space in the block. */
334 free = min_offs - ((char *)last - (char *)s->base) - sizeof(uint32_t);
336 free += EXT4_XATTR_SIZE(s->here->e_value_size) +
337 EXT4_XATTR_LEN(s->here->e_name_len);
340 /* See whether there is enough space to hold new entry */
342 EXT4_XATTR_SIZE(i->value_len) + EXT4_XATTR_LEN(name_len))
346 /* Return EOK now if we do not intend to modify the content. */
350 /* First remove the old entry's data part */
352 size_t value_offs = to_le16(s->here->e_value_offs);
353 void *value = (char *)s->base + value_offs;
354 void *first_value = (char *)s->base + min_offs;
356 EXT4_XATTR_SIZE(to_le32(s->here->e_value_size));
359 /* Remove the data part. */
360 memmove((char *)first_value + value_size, first_value,
361 (char *)value - (char *)first_value);
363 /* Zero the gap created */
364 memset(first_value, 0, value_size);
367 * Calculate the new min_offs after removal of the old
370 min_offs += value_size;
374 * Adjust the value offset of entries which has value offset
375 * prior to the s->here. The offset of these entries won't be
376 * shifted if the size of the entry we removed is zero.
378 for (last = s->first; !EXT4_XATTR_IS_LAST_ENTRY(last);
379 last = EXT4_XATTR_NEXT(last)) {
380 size_t offs = to_le16(last->e_value_offs);
382 /* For zero-value-length entry, offs will be zero. */
383 if (offs < value_offs)
384 last->e_value_offs = to_le16(offs + value_size);
388 /* If caller wants us to insert... */
392 value_offs = min_offs - EXT4_XATTR_SIZE(i->value_len);
397 struct ext4_xattr_entry *here = s->here;
399 /* Reuse the current entry we have got */
400 here->e_value_offs = to_le16(value_offs);
401 here->e_value_size = to_le32(i->value_len);
403 /* Insert a new entry */
404 last->e_name_len = (uint8_t)name_len;
405 last->e_name_index = i->name_index;
406 last->e_value_offs = to_le16(value_offs);
407 last->e_value_block = 0;
408 last->e_value_size = to_le32(i->value_len);
409 memcpy(EXT4_XATTR_NAME(last), i->name, name_len);
411 /* Set valid last entry indicator */
412 *(uint32_t *)EXT4_XATTR_NEXT(last) = 0;
417 /* Insert the value's part */
419 memcpy((char *)s->base + value_offs, i->value,
422 /* Clear the padding bytes if there is */
423 if (EXT4_XATTR_SIZE(i->value_len) != i->value_len)
424 memset((char *)s->base + value_offs +
426 0, EXT4_XATTR_SIZE(i->value_len) -
432 /* Remove the whole entry */
433 shift_offs = (char *)EXT4_XATTR_NEXT(s->here) - (char *)s->here;
434 memmove(s->here, EXT4_XATTR_NEXT(s->here),
435 (char *)last + sizeof(uint32_t) -
436 (char *)EXT4_XATTR_NEXT(s->here));
438 /* Zero the gap created */
439 memset((char *)last - shift_offs + sizeof(uint32_t), 0,
448 static inline bool ext4_xattr_is_empty(struct ext4_xattr_search *s)
450 if (!EXT4_XATTR_IS_LAST_ENTRY(s->first))
457 * @brief Find the entry according to given information
459 * @param i The information of the EA entry to be found,
460 * including name_index, name and the length of name
461 * @param s Search context block
463 static void ext4_xattr_find_entry(struct ext4_xattr_info *i,
464 struct ext4_xattr_search *s)
466 struct ext4_xattr_entry *entry = NULL;
472 * Find the wanted EA entry by simply comparing the namespace,
473 * name and the length of name.
475 for (entry = s->first; !EXT4_XATTR_IS_LAST_ENTRY(entry);
476 entry = EXT4_XATTR_NEXT(entry)) {
477 size_t name_len = entry->e_name_len;
478 const char *name = EXT4_XATTR_NAME(entry);
479 if (name_len == i->name_len &&
480 entry->e_name_index == i->name_index &&
481 !memcmp(name, i->name, name_len)) {
483 s->not_found = false;
484 i->value_len = to_le32(entry->e_value_size);
486 i->value = (char *)s->base +
487 to_le16(entry->e_value_offs);
497 * @brief Check whether the xattr block's content is valid
499 * @param inode_ref Inode reference
500 * @param block The block buffer to be validated
502 * @return true if @block is valid, false otherwise.
504 static bool ext4_xattr_is_block_valid(struct ext4_inode_ref *inode_ref,
505 struct ext4_block *block)
508 void *base = block->data,
509 *end = block->data + ext4_sb_get_block_size(&inode_ref->fs->sb);
510 size_t min_offs = (char *)end - (char *)base;
511 struct ext4_xattr_header *header = EXT4_XATTR_BHDR(block);
512 struct ext4_xattr_entry *entry = EXT4_XATTR_BFIRST(block);
515 * Check whether the magic number in the header is correct.
517 if (header->h_magic != to_le32(EXT4_XATTR_MAGIC))
521 * The in-kernel filesystem driver only supports 1 block currently.
523 if (header->h_blocks != to_le32(1))
527 * Check if those entries are maliciously corrupted to inflict harm
530 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
531 entry = EXT4_XATTR_NEXT(entry)) {
532 if (!to_le32(entry->e_value_size) &&
533 to_le16(entry->e_value_offs))
536 if ((char *)base + to_le16(entry->e_value_offs) +
537 to_le32(entry->e_value_size) >
542 * The name length field should also be correct,
543 * also there should be an 4-byte zero entry at the
546 if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) >
550 if (to_le32(entry->e_value_size)) {
551 size_t offs = to_le16(entry->e_value_offs);
557 * Entry field and data field do not override each other.
559 if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t))
566 * @brief Check whether the inode buffer's content is valid
568 * @param inode_ref Inode reference
570 * @return true if the inode buffer is valid, false otherwise.
572 static bool ext4_xattr_is_ibody_valid(struct ext4_inode_ref *inode_ref)
576 struct ext4_fs *fs = inode_ref->fs;
577 struct ext4_xattr_ibody_header *iheader;
578 struct ext4_xattr_entry *entry;
579 size_t inode_size = ext4_get16(&fs->sb, inode_size);
581 iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
582 entry = EXT4_XATTR_IFIRST(iheader);
584 end = (char *)inode_ref->inode + inode_size;
585 min_offs = (char *)end - (char *)base;
588 * Check whether the magic number in the header is correct.
590 if (iheader->h_magic != to_le32(EXT4_XATTR_MAGIC))
594 * Check if those entries are maliciously corrupted to inflict harm
597 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
598 entry = EXT4_XATTR_NEXT(entry)) {
599 if (!to_le32(entry->e_value_size) &&
600 to_le16(entry->e_value_offs))
603 if ((char *)base + to_le16(entry->e_value_offs) +
604 to_le32(entry->e_value_size) >
609 * The name length field should also be correct,
610 * also there should be an 4-byte zero entry at the
613 if ((char *)EXT4_XATTR_NEXT(entry) + sizeof(uint32_t) >
617 if (to_le32(entry->e_value_size)) {
618 size_t offs = to_le16(entry->e_value_offs);
624 * Entry field and data field do not override each other.
626 if ((char *)base + min_offs < (char *)entry + sizeof(uint32_t))
633 * @brief An EA entry finder for inode buffer
635 struct ext4_xattr_finder {
637 * @brief The information of the EA entry to be find
639 struct ext4_xattr_info i;
642 * @brief Search context block of the current search
644 struct ext4_xattr_search s;
647 * @brief Inode reference to the corresponding inode
649 struct ext4_inode_ref *inode_ref;
652 static void ext4_xattr_ibody_initialize(struct ext4_inode_ref *inode_ref)
654 struct ext4_xattr_ibody_header *header;
655 struct ext4_fs *fs = inode_ref->fs;
657 ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
658 size_t inode_size = ext4_get16(&fs->sb, inode_size);
662 header = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
663 memset(header, 0, inode_size - EXT4_GOOD_OLD_INODE_SIZE - extra_isize);
664 header->h_magic = to_le32(EXT4_XATTR_MAGIC);
665 inode_ref->dirty = true;
669 * @brief Initialize a given xattr block
671 * @param inode_ref Inode reference
672 * @param block xattr block buffer
674 static void ext4_xattr_block_initialize(struct ext4_inode_ref *inode_ref,
675 struct ext4_block *block)
677 struct ext4_xattr_header *header;
678 struct ext4_fs *fs = inode_ref->fs;
680 memset(block->data, 0, ext4_sb_get_block_size(&fs->sb));
682 header = EXT4_XATTR_BHDR(block);
683 header->h_magic = to_le32(EXT4_XATTR_MAGIC);
684 header->h_refcount = to_le32(1);
685 header->h_blocks = to_le32(1);
687 ext4_trans_set_block_dirty(block->buf);
690 static void ext4_xattr_block_init_search(struct ext4_inode_ref *inode_ref,
691 struct ext4_xattr_search *s,
692 struct ext4_block *block)
694 s->base = block->data;
695 s->end = block->data + ext4_sb_get_block_size(&inode_ref->fs->sb);
696 s->first = EXT4_XATTR_BFIRST(block);
702 * @brief Find an EA entry inside a xattr block
704 * @param inode_ref Inode reference
705 * @param finder The caller-provided finder block with
707 * @param block The block buffer to be looked into
709 * @return Return EOK no matter the entry is found or not.
710 * If the IO operation or the buffer validation failed,
711 * return other value.
713 static int ext4_xattr_block_find_entry(struct ext4_inode_ref *inode_ref,
714 struct ext4_xattr_finder *finder,
715 struct ext4_block *block)
719 /* Initialize the caller-given finder */
720 finder->inode_ref = inode_ref;
721 memset(&finder->s, 0, sizeof(finder->s));
726 /* Check the validity of the buffer */
727 if (!ext4_xattr_is_block_valid(inode_ref, block))
730 ext4_xattr_block_init_search(inode_ref, &finder->s, block);
731 ext4_xattr_find_entry(&finder->i, &finder->s);
736 * @brief Find an EA entry inside an inode's extra space
738 * @param inode_ref Inode reference
739 * @param finder The caller-provided finder block with
742 * @return Return EOK no matter the entry is found or not.
743 * If the IO operation or the buffer validation failed,
744 * return other value.
746 static int ext4_xattr_ibody_find_entry(struct ext4_inode_ref *inode_ref,
747 struct ext4_xattr_finder *finder)
749 struct ext4_fs *fs = inode_ref->fs;
750 struct ext4_xattr_ibody_header *iheader;
752 ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
753 size_t inode_size = ext4_get16(&fs->sb, inode_size);
755 /* Initialize the caller-given finder */
756 finder->inode_ref = inode_ref;
757 memset(&finder->s, 0, sizeof(finder->s));
760 * If there is no extra inode space
761 * set ext4_xattr_ibody_finder::s::not_found to true and return EOK
764 finder->s.not_found = true;
768 /* Check the validity of the buffer */
769 if (!ext4_xattr_is_ibody_valid(inode_ref))
772 iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
773 finder->s.base = EXT4_XATTR_IFIRST(iheader);
774 finder->s.end = (char *)inode_ref->inode + inode_size;
775 finder->s.first = EXT4_XATTR_IFIRST(iheader);
776 ext4_xattr_find_entry(&finder->i, &finder->s);
781 * @brief Try to allocate a block holding EA entries.
783 * @param inode_ref Inode reference
787 static int ext4_xattr_try_alloc_block(struct ext4_inode_ref *inode_ref)
791 ext4_fsblk_t xattr_block = 0;
793 ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb);
796 * Only allocate a xattr block when there is no xattr block
800 ext4_fsblk_t goal = ext4_fs_inode_to_goal_block(inode_ref);
802 ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block);
806 ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb,
815 * @brief Try to free a block holding EA entries.
817 * @param inode_ref Inode reference
821 static void ext4_xattr_try_free_block(struct ext4_inode_ref *inode_ref)
823 ext4_fsblk_t xattr_block;
825 ext4_inode_get_file_acl(inode_ref->inode, &inode_ref->fs->sb);
827 * Free the xattr block used by the inode when there is one.
830 ext4_inode_set_file_acl(inode_ref->inode, &inode_ref->fs->sb,
832 ext4_balloc_free_block(inode_ref, xattr_block);
833 inode_ref->dirty = true;
838 * @brief Put a list of EA entries into a caller-provided buffer
839 * In order to make sure that @list buffer can fit in the data,
840 * the routine should be called twice.
842 * @param inode_ref Inode reference
843 * @param list A caller-provided buffer to hold a list of EA entries.
844 * If list == NULL, list_len will contain the size of
845 * the buffer required to hold these entries
846 * @param list_len The length of the data written to @list
849 int ext4_xattr_list(struct ext4_inode_ref *inode_ref,
850 struct ext4_xattr_list_entry *list, size_t *list_len)
854 struct ext4_fs *fs = inode_ref->fs;
855 struct ext4_xattr_ibody_header *iheader;
857 ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
858 struct ext4_block block;
859 bool block_loaded = false;
860 ext4_fsblk_t xattr_block = 0;
861 struct ext4_xattr_entry *entry;
862 struct ext4_xattr_list_entry *list_prev = NULL;
863 xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
866 * If there is extra inode space and the xattr buffer in the
869 if (extra_isize && ext4_xattr_is_ibody_valid(inode_ref)) {
870 iheader = EXT4_XATTR_IHDR(&fs->sb, inode_ref->inode);
871 entry = EXT4_XATTR_IFIRST(iheader);
874 * The format of the list should be like this:
876 * name_len indicates the length in bytes of the name
877 * of the EA entry. The string is null-terminated.
879 * list->name => (char *)(list + 1);
880 * list->next => (void *)((char *)(list + 1) + name_len + 1);
882 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
883 entry = EXT4_XATTR_NEXT(entry)) {
884 size_t name_len = entry->e_name_len;
886 list->name_index = entry->e_name_index;
887 list->name_len = name_len;
888 list->name = (char *)(list + 1);
889 memcpy(list->name, EXT4_XATTR_NAME(entry),
893 list_prev->next = list;
896 list = (struct ext4_xattr_list_entry
897 *)(list->name + name_len + 1);
901 * Size calculation by pointer arithmetics.
904 (char *)((struct ext4_xattr_list_entry *)0 + 1) +
906 (char *)(struct ext4_xattr_list_entry *)0;
911 * If there is a xattr block used by the inode
914 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
921 * As we don't allow the content in the block being invalid,
924 if (!ext4_xattr_is_block_valid(inode_ref, &block)) {
929 entry = EXT4_XATTR_BFIRST(&block);
932 * The format of the list should be like this:
934 * name_len indicates the length in bytes of the name
935 * of the EA entry. The string is null-terminated.
937 * list->name => (char *)(list + 1);
938 * list->next => (void *)((char *)(list + 1) + name_len + 1);
940 * Same as above actually.
942 for (; !EXT4_XATTR_IS_LAST_ENTRY(entry);
943 entry = EXT4_XATTR_NEXT(entry)) {
944 size_t name_len = entry->e_name_len;
946 list->name_index = entry->e_name_index;
947 list->name_len = name_len;
948 list->name = (char *)(list + 1);
949 memcpy(list->name, EXT4_XATTR_NAME(entry),
953 list_prev->next = list;
956 list = (struct ext4_xattr_list_entry
957 *)(list->name + name_len + 1);
961 * Size calculation by pointer arithmetics.
964 (char *)((struct ext4_xattr_list_entry *)0 + 1) +
966 (char *)(struct ext4_xattr_list_entry *)0;
970 list_prev->next = NULL;
972 if (ret == EOK && list_len)
976 ext4_block_set(fs->bdev, &block);
982 * @brief Query EA entry's value with given name-index and name
984 * @param inode_ref Inode reference
985 * @param name_index Name-index
986 * @param name Name of the EA entry to be queried
987 * @param name_len Length of name in bytes
988 * @param buf Output buffer to hold content
989 * @param buf_len Output buffer's length
990 * @param data_len The length of data of the EA entry found
994 int ext4_xattr_get(struct ext4_inode_ref *inode_ref, uint8_t name_index,
995 const char *name, size_t name_len, void *buf, size_t buf_len,
999 struct ext4_xattr_finder ibody_finder;
1000 struct ext4_xattr_finder block_finder;
1001 struct ext4_xattr_info i;
1002 size_t value_len = 0;
1003 size_t value_offs = 0;
1004 struct ext4_fs *fs = inode_ref->fs;
1005 ext4_fsblk_t xattr_block;
1006 xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1008 i.name_index = name_index;
1010 i.name_len = name_len;
1017 ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1021 if (!ibody_finder.s.not_found) {
1022 value_len = to_le32(ibody_finder.s.here->e_value_size);
1023 value_offs = to_le32(ibody_finder.s.here->e_value_offs);
1024 if (buf_len && buf) {
1026 (char *)ibody_finder.s.base + value_offs;
1027 memcpy(buf, data_loc,
1028 (buf_len < value_len) ? buf_len : value_len);
1031 struct ext4_block block;
1033 /* Return ENODATA if there is no EA block */
1040 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
1044 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1047 ext4_block_set(fs->bdev, &block);
1051 /* Return ENODATA if entry is not found */
1052 if (block_finder.s.not_found) {
1053 ext4_block_set(fs->bdev, &block);
1058 value_len = to_le32(block_finder.s.here->e_value_size);
1059 value_offs = to_le32(block_finder.s.here->e_value_offs);
1060 if (buf_len && buf) {
1062 (char *)block_finder.s.base + value_offs;
1063 memcpy(buf, data_loc,
1064 (buf_len < value_len) ? buf_len : value_len);
1068 * Free the xattr block buffer returned by
1069 * ext4_xattr_block_find_entry.
1071 ext4_block_set(fs->bdev, &block);
1075 if (ret == EOK && data_len)
1076 *data_len = value_len;
1082 * @brief Try to copy the content of an xattr block to a newly-allocated
1083 * block. If the operation fails, the block buffer provided by
1084 * caller will be freed
1086 * @param inode_ref Inode reference
1087 * @param block The block buffer reference
1088 * @param new_block The newly-allocated block buffer reference
1089 * @param orig_block The block number of @block
1090 * @param allocated a new block is allocated
1092 * @return Error code
1094 static int ext4_xattr_copy_new_block(struct ext4_inode_ref *inode_ref,
1095 struct ext4_block *block,
1096 struct ext4_block *new_block,
1097 ext4_fsblk_t *orig_block, bool *allocated)
1100 ext4_fsblk_t xattr_block = 0;
1101 struct ext4_xattr_header *header;
1102 struct ext4_fs *fs = inode_ref->fs;
1103 header = EXT4_XATTR_BHDR(block);
1106 *orig_block = block->lb_id;
1111 /* Only do copy when a block is referenced by more than one inode. */
1112 if (to_le32(header->h_refcount) > 1) {
1113 ext4_fsblk_t goal = ext4_fs_inode_to_goal_block(inode_ref);
1115 /* Allocate a new block to be used by this inode */
1116 ret = ext4_balloc_alloc_block(inode_ref, goal, &xattr_block);
1120 ret = ext4_trans_block_get(fs->bdev, new_block, xattr_block);
1124 /* Copy the content of the whole block */
1125 memcpy(new_block->data, block->data,
1126 ext4_sb_get_block_size(&inode_ref->fs->sb));
1129 * Decrement the reference count of the original xattr block
1132 header->h_refcount = to_le32(to_le32(header->h_refcount) - 1);
1133 ext4_trans_set_block_dirty(block->buf);
1134 ext4_trans_set_block_dirty(new_block->buf);
1136 header = EXT4_XATTR_BHDR(new_block);
1137 header->h_refcount = to_le32(1);
1145 ext4_balloc_free_block(inode_ref, xattr_block);
1148 * Modify the in-inode pointer to point to the new xattr block
1150 ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, xattr_block);
1151 inode_ref->dirty = true;
1159 * @brief Given an EA entry's name, remove the EA entry
1161 * @param inode_ref Inode reference
1162 * @param name_index Name-index
1163 * @param name Name of the EA entry to be removed
1164 * @param name_len Length of name in bytes
1166 * @return Error code
1168 int ext4_xattr_remove(struct ext4_inode_ref *inode_ref, uint8_t name_index,
1169 const char *name, size_t name_len)
1172 struct ext4_block block;
1173 struct ext4_xattr_finder ibody_finder;
1174 struct ext4_xattr_finder block_finder;
1175 bool use_block = false;
1176 bool block_loaded = false;
1177 struct ext4_xattr_info i;
1178 struct ext4_fs *fs = inode_ref->fs;
1179 ext4_fsblk_t xattr_block;
1181 xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1183 i.name_index = name_index;
1185 i.name_len = name_len;
1192 ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1196 if (ibody_finder.s.not_found && xattr_block) {
1197 ret = ext4_trans_block_get(fs->bdev, &block, xattr_block);
1201 block_loaded = true;
1203 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1208 /* Return ENODATA if entry is not found */
1209 if (block_finder.s.not_found) {
1217 bool allocated = false;
1218 struct ext4_block new_block;
1221 * There will be no effect when the xattr block is only referenced
1224 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1225 &xattr_block, &allocated);
1230 /* Prevent double-freeing */
1231 block_loaded = false;
1235 ret = ext4_xattr_block_find_entry(inode_ref, &block_finder,
1240 /* Now remove the entry */
1241 ext4_xattr_set_entry(&i, &block_finder.s, false);
1243 if (ext4_xattr_is_empty(&block_finder.s)) {
1244 ext4_block_set(fs->bdev, &new_block);
1245 ext4_xattr_try_free_block(inode_ref);
1247 struct ext4_xattr_header *header =
1248 EXT4_XATTR_BHDR(&new_block);
1249 header = EXT4_XATTR_BHDR(&new_block);
1250 ext4_assert(block_finder.s.first);
1251 ext4_xattr_rehash(header, block_finder.s.first);
1252 ext4_xattr_set_block_checksum(inode_ref,
1256 ext4_trans_set_block_dirty(new_block.buf);
1257 ext4_block_set(fs->bdev, &new_block);
1261 /* Now remove the entry */
1262 ext4_xattr_set_entry(&i, &block_finder.s, false);
1263 inode_ref->dirty = true;
1267 ext4_block_set(fs->bdev, &block);
1273 * @brief Insert/overwrite an EA entry into/in a xattr block
1275 * @param inode_ref Inode reference
1276 * @param i The information of the given EA entry
1278 * @return Error code
1280 static int ext4_xattr_block_set(struct ext4_inode_ref *inode_ref,
1281 struct ext4_xattr_info *i,
1285 bool allocated = false;
1286 struct ext4_fs *fs = inode_ref->fs;
1287 struct ext4_block block, new_block;
1288 ext4_fsblk_t orig_xattr_block;
1290 orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1292 ext4_assert(i->value);
1293 if (!orig_xattr_block) {
1294 struct ext4_xattr_search s;
1295 struct ext4_xattr_header *header;
1297 /* If insertion of new entry is not allowed... */
1303 ret = ext4_xattr_try_alloc_block(inode_ref);
1308 ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1309 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1311 ext4_xattr_try_free_block(inode_ref);
1315 ext4_xattr_block_initialize(inode_ref, &block);
1316 ext4_xattr_block_init_search(inode_ref, &s, &block);
1318 ret = ext4_xattr_set_entry(i, &s, false);
1320 header = EXT4_XATTR_BHDR(&block);
1322 ext4_assert(s.here);
1323 ext4_assert(s.first);
1324 ext4_xattr_compute_hash(header, s.here);
1325 ext4_xattr_rehash(header, s.first);
1326 ext4_xattr_set_block_checksum(inode_ref,
1329 ext4_trans_set_block_dirty(block.buf);
1331 ext4_block_set(fs->bdev, &block);
1333 ext4_xattr_try_free_block(inode_ref);
1336 struct ext4_xattr_finder finder;
1337 struct ext4_xattr_header *header;
1339 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1343 header = EXT4_XATTR_BHDR(&block);
1346 * Consider the following case when insertion of new
1347 * entry is not allowed
1349 if (to_le32(header->h_refcount) > 1 && no_insert) {
1351 * There are other people referencing the
1354 ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1356 ext4_block_set(fs->bdev, &block);
1359 if (finder.s.not_found) {
1360 ext4_block_set(fs->bdev, &block);
1367 * There will be no effect when the xattr block is only referenced
1370 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1371 &orig_xattr_block, &allocated);
1373 ext4_block_set(fs->bdev, &block);
1378 ext4_block_set(fs->bdev, &block);
1382 ret = ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1384 ext4_block_set(fs->bdev, &block);
1388 ret = ext4_xattr_set_entry(i, &finder.s, false);
1390 header = EXT4_XATTR_BHDR(&block);
1392 ext4_assert(finder.s.here);
1393 ext4_assert(finder.s.first);
1394 ext4_xattr_compute_hash(header, finder.s.here);
1395 ext4_xattr_rehash(header, finder.s.first);
1396 ext4_xattr_set_block_checksum(inode_ref,
1399 ext4_trans_set_block_dirty(block.buf);
1401 ext4_block_set(fs->bdev, &block);
1408 * @brief Remove an EA entry from a xattr block
1410 * @param inode_ref Inode reference
1411 * @param i The information of the given EA entry
1413 * @return Error code
1415 static int ext4_xattr_block_remove(struct ext4_inode_ref *inode_ref,
1416 struct ext4_xattr_info *i)
1419 bool allocated = false;
1420 const void *value = i->value;
1421 struct ext4_fs *fs = inode_ref->fs;
1422 struct ext4_xattr_finder finder;
1423 struct ext4_block block, new_block;
1424 struct ext4_xattr_header *header;
1425 ext4_fsblk_t orig_xattr_block;
1426 orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1428 ext4_assert(orig_xattr_block);
1429 ret = ext4_trans_block_get(fs->bdev, &block, orig_xattr_block);
1434 * There will be no effect when the xattr block is only referenced
1437 ret = ext4_xattr_copy_new_block(inode_ref, &block, &new_block,
1438 &orig_xattr_block, &allocated);
1440 ext4_block_set(fs->bdev, &block);
1445 ext4_block_set(fs->bdev, &block);
1449 ext4_xattr_block_find_entry(inode_ref, &finder, &block);
1451 if (!finder.s.not_found) {
1453 ret = ext4_xattr_set_entry(i, &finder.s, false);
1456 header = EXT4_XATTR_BHDR(&block);
1457 ext4_assert(finder.s.first);
1458 ext4_xattr_rehash(header, finder.s.first);
1459 ext4_xattr_set_block_checksum(inode_ref,
1462 ext4_trans_set_block_dirty(block.buf);
1465 ext4_block_set(fs->bdev, &block);
1471 * @brief Insert an EA entry into a given inode reference
1473 * @param inode_ref Inode reference
1474 * @param name_index Name-index
1475 * @param name Name of the EA entry to be inserted
1476 * @param name_len Length of name in bytes
1477 * @param value Input buffer to hold content
1478 * @param value_len Length of input content
1480 * @return Error code
1482 int ext4_xattr_set(struct ext4_inode_ref *inode_ref, uint8_t name_index,
1483 const char *name, size_t name_len, const void *value,
1487 struct ext4_fs *fs = inode_ref->fs;
1488 struct ext4_xattr_finder ibody_finder;
1489 struct ext4_xattr_info i;
1490 bool block_found = false;
1491 ext4_fsblk_t orig_xattr_block;
1492 size_t extra_isize =
1493 ext4_inode_get_extra_isize(&fs->sb, inode_ref->inode);
1495 i.name_index = name_index;
1497 i.name_len = name_len;
1498 i.value = (value_len) ? value : &ext4_xattr_empty_value;
1499 i.value_len = value_len;
1503 orig_xattr_block = ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
1506 * Even if entry is not found, search context block inside the
1507 * finder is still valid and can be used to insert entry.
1509 ret = ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1511 ext4_xattr_ibody_initialize(inode_ref);
1512 ext4_xattr_ibody_find_entry(inode_ref, &ibody_finder);
1515 if (ibody_finder.s.not_found) {
1516 if (orig_xattr_block) {
1518 ret = ext4_xattr_block_set(inode_ref, &i, true);
1521 else if (ret == ENODATA)
1523 else if (ret != EOK)
1531 /* Only try to set entry in ibody if inode is sufficiently large */
1533 ret = ext4_xattr_set_entry(&i, &ibody_finder.s, false);
1537 if (ret == ENOSPC) {
1539 ret = ext4_xattr_block_set(inode_ref, &i, false);
1540 ibody_finder.i.value = NULL;
1541 ext4_xattr_set_entry(&ibody_finder.i,
1542 &ibody_finder.s, false);
1543 inode_ref->dirty = true;
1546 } else if (ret == EOK) {
1548 ret = ext4_xattr_block_remove(inode_ref, &i);
1550 inode_ref->dirty = true;