/* * Copyright (c) 2017 Grzegorz Kostka (kostka.grzegorz@gmail.com) * Copyright (c) 2017 Kaho Ng (ngkaho1234@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. */ /** @addtogroup lwext4 * @{ *//** * @file ext4_xattr.c * @brief Extended Attribute manipulation. */ #include <ext4_config.h>#include <ext4_debug.h>#include <ext4_errno.h>#include <ext4_misc.h>#include <ext4_types.h> #include <ext4_balloc.h>#include <ext4_block_group.h>#include <ext4_blockdev.h>#include <ext4_crc32.h>#include <ext4_fs.h>#include <ext4_inode.h>#include <ext4_super.h>#include <ext4_trans.h>#include <ext4_xattr.h> #include <stdlib.h>#include <string.h> #if CONFIG_XATTR_ENABLE /** * @file ext4_xattr.c * @brief Extended Attribute Manipulation */ /* Extended Attribute(EA) */ /* Magic value in attribute blocks */#define EXT4_XATTR_MAGIC 0xEA020000 /* Maximum number of references to one attribute block */#define EXT4_XATTR_REFCOUNT_MAX 1024 /* Name indexes */#define EXT4_XATTR_INDEX_USER 1#define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS 2#define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT 3#define EXT4_XATTR_INDEX_TRUSTED 4#define EXT4_XATTR_INDEX_LUSTRE 5#define EXT4_XATTR_INDEX_SECURITY 6#define EXT4_XATTR_INDEX_SYSTEM 7#define EXT4_XATTR_INDEX_RICHACL 8#define EXT4_XATTR_INDEX_ENCRYPTION 9 #define EXT4_XATTR_PAD_BITS 2#define EXT4_XATTR_PAD (1 << EXT4_XATTR_PAD_BITS)#define EXT4_XATTR_ROUND (EXT4_XATTR_PAD - 1)#define EXT4_XATTR_LEN(name_len) \ (((name_len) + EXT4_XATTR_ROUND + sizeof(struct ext4_xattr_entry)) & \ ~EXT4_XATTR_ROUND)#define EXT4_XATTR_NEXT(entry) \ ((struct ext4_xattr_entry *)((char *)(entry) + \ EXT4_XATTR_LEN((entry)->name_len)))#define EXT4_XATTR_SIZE(size) (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)#define EXT4_XATTR_NAME(entry) ((char *)((entry) + 1)) #define EXT4_XATTR_IHDR(sb, raw_inode) \ ((struct ext4_xattr_ibody_header *)((char *)raw_inode + \ EXT4_GOOD_OLD_INODE_SIZE + \ ext4_inode_get_extra_isize( \ sb, raw_inode)))#define EXT4_XATTR_IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr) + 1)) #define EXT4_XATTR_BHDR(block) ((struct ext4_xattr_header *)((block)->data))#define EXT4_XATTR_ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))#define EXT4_XATTR_BFIRST(block) EXT4_XATTR_ENTRY(EXT4_XATTR_BHDR(block) + 1)#define EXT4_XATTR_IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0) #define EXT4_ZERO_XATTR_VALUE ((void *)-1) #ifdef __plan9__#pragma pack on#else#pragma pack(push, 1)#endif struct ext4_xattr_header { uint32_t magic; /* magic number for identification */ uint32_t refcount; /* reference count */ uint32_t blocks; /* number of disk blocks used */ uint32_t hash; /* hash value of all attributes */ uint32_t checksum; /* crc32c(uuid+id+xattrblock) */ /* id = inum if refcount=1, blknum otherwise */ uint32_t reserved[3]; /* zero right now */}; struct ext4_xattr_ibody_header { uint32_t magic; /* magic number for identification */}; struct ext4_xattr_entry { uint8_t name_len; /* length of name */ uint8_t name_index; /* attribute name index */ uint16_t value_offs; /* offset in disk block of value */ uint32_t value_block; /* disk block attribute is stored on (n/i) */ uint32_t value_size; /* size of attribute value */ uint32_t hash; /* hash value of name and value */}; #ifdef __plan9__#pragma pack off#else#pragma pack(pop)#endif #define EXT4_XATTR_NAME_HASH_SHIFT 5#define EXT4_XATTR_VALUE_HASH_SHIFT 16#define EXT4_XATTR_BLOCK_HASH_SHIFT 16 /**@brief Generic hash function (1 byte as padding unit) * @param hash an input hash to be used (0 if there is no input hash) * @param buf buffer * @param buflen length of buffer * @param shift number of bits to shift for each round * @return hash of the buffer */static inline uint32_t ext4_xattr_generic_hash(uint32_t hash, const char *buf, size_t buflen, int shift){ size_t i; uint32_t ret; /* Input hash value to be used */ ret = hash; for (i = 0; i < buflen; ++i) { /* For each round, rotate the hash from the lowest bit to the * highest bit. Then XOR the hash against the content of * buffer */ ret = (ret << shift) ^ (ret >> (8 * sizeof(ret) - shift)) ^ buf[i]; } return ret;} /**@brief Generic hash function (4 byte as padding unit) * @param hash an input hash to be used (0 if there is no input hash) * @param buf buffer * @param buflen length of buffer * @param shift number of bits to shift for each round * @return hash of the buffer */static inline uint32_t ext4_xattr_generic_hash32(uint32_t hash, const uint32_t *buf, size_t buflen, int shift){ size_t i; uint32_t ret; /* Input hash value to be used */ ret = hash; /* Calculate the number of uint32_t blocks */ buflen = (buflen + sizeof(uint32_t) - 1) / sizeof(uint32_t); for (i = 0; i < buflen; ++i) { /* For each round, rotate the hash from the lowest bit to the * highest bit. Then XOR the hash against the content of * buffer */ ret = (ret << shift) ^ (ret >> (8 * sizeof(ret) - shift)) ^ to_le32(buf[i]); } return ret;} /**@brief Calculate and set hash value of an EA * @param header header of EA block * @param entry EA entry */static inline void ext4_xattr_entry_hash(struct ext4_xattr_header *header, struct ext4_xattr_entry *entry){ uint32_t hash = 0; char *name; uint8_t namelen; name = EXT4_XATTR_NAME(entry); namelen = entry->name_len; /* Calculate the hash of the name of the EA entry */ hash = ext4_xattr_generic_hash(hash, name, namelen, EXT4_XATTR_NAME_HASH_SHIFT); /* Make sure that the entry is valid, or the content of the entry is * not zero in size */ if (entry->value_block == 0 && entry->value_size != 0) { uint32_t *value; value = (uint32_t *)((char *)header + to_le16(entry->value_offs)); /* Then calculate the hash of the content of the EA entry */ hash = ext4_xattr_generic_hash32(hash, value, to_le32(entry->value_size), EXT4_XATTR_VALUE_HASH_SHIFT); } entry->hash = to_le32(hash);} /**@brief Calculate and set hash value of the whole EA block * @param header header of EA block */static void ext4_xattr_block_hash(struct ext4_xattr_header *header){ struct ext4_xattr_entry *here; uint32_t hash = 0; /* Calculate hash for all the entries and store it in * header. The hash algorithm is very similar to the above * hash routines for buffers */ for (here = EXT4_XATTR_ENTRY(header + 1); !EXT4_XATTR_IS_LAST_ENTRY(here); here = EXT4_XATTR_NEXT(here)) { hash = (hash << EXT4_XATTR_BLOCK_HASH_SHIFT) ^ (hash >> (8 * sizeof(hash) - EXT4_XATTR_BLOCK_HASH_SHIFT)) ^ to_le32(here->hash); } header->hash = to_le32(hash);} #if CONFIG_META_CSUM_ENABLEstatic uint32_t ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref, ext4_fsblk_t blocknr, struct ext4_xattr_header *header){ uint32_t checksum = 0; uint64_t le64_blocknr = blocknr; struct ext4_sblock *sb = &inode_ref->fs->sb; if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { uint32_t orig_checksum; /* Preparation: temporarily set bg checksum to 0 */ orig_checksum = header->checksum; header->checksum = 0; /* First calculate crc32 checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); /* Then calculate crc32 checksum block number */ checksum = ext4_crc32c(checksum, &le64_blocknr, sizeof(le64_blocknr)); /* Finally calculate crc32 checksum against * the entire xattr block */ checksum = ext4_crc32c(checksum, header, ext4_sb_get_block_size(sb)); header->checksum = orig_checksum; } return checksum;}#else#define ext4_xattr_block_checksum(...) 0#endif static void ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref, ext4_fsblk_t blocknr __unused, struct ext4_xattr_header *header){ struct ext4_sblock *sb = &inode_ref->fs->sb; if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) return; header->checksum = ext4_xattr_block_checksum(inode_ref, blocknr, header);} struct xattr_prefix { const char *prefix; uint8_t name_index;}; static const struct xattr_prefix prefix_tbl[] = { {"user.", EXT4_XATTR_INDEX_USER}, {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS}, {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT}, {"trusted.", EXT4_XATTR_INDEX_TRUSTED}, {"security.", EXT4_XATTR_INDEX_SECURITY}, {"system.", EXT4_XATTR_INDEX_SYSTEM}, {"system.richacl", EXT4_XATTR_INDEX_RICHACL}, {NULL, 0},}; const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len, uint8_t *name_index, size_t *name_len, bool *found){ int i; ext4_assert(name_index); ext4_assert(found); *found = false; if (!full_name_len) { if (name_len) *name_len = 0; return NULL; } for (i = 0; prefix_tbl[i].prefix; i++) { size_t prefix_len = strlen(prefix_tbl[i].prefix); if (full_name_len >= prefix_len && !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) { bool require_name = prefix_tbl[i].prefix[prefix_len - 1] == '.'; *name_index = prefix_tbl[i].name_index; if (name_len) *name_len = full_name_len - prefix_len; if (!(full_name_len - prefix_len) && require_name) return NULL; *found = true; if (require_name) return full_name + prefix_len; return NULL; } } if (name_len) *name_len = 0; return NULL;} const char *ext4_get_xattr_name_prefix(uint8_t name_index, size_t *ret_prefix_len){ int i; for (i = 0; prefix_tbl[i].prefix; i++) { size_t prefix_len = strlen(prefix_tbl[i].prefix); if (prefix_tbl[i].name_index == name_index) { if (ret_prefix_len) *ret_prefix_len = prefix_len; return prefix_tbl[i].prefix; } } if (ret_prefix_len) *ret_prefix_len = 0; return NULL;} static const char ext4_xattr_empty_value; /** * @brief Insert/Remove/Modify the given entry * * @param i The information of the given EA entry * @param s Search context block * @param dry_run Do not modify the content of the buffer * * @return Return EOK when finished, ENOSPC when there is no enough space */static int ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s, bool dry_run){ struct ext4_xattr_entry *last; size_t free, min_offs = (char *)s->end - (char *)s->base, name_len = i->name_len; /* * If the entry is going to be removed but not found, return 0 to * indicate success. */ if (!i->value && s->not_found) return EOK; /* Compute min_offs and last. */ last = s->first; for (; !EXT4_XATTR_IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { if (last->value_size) { size_t offs = to_le16(last->value_offs); if (offs < min_offs) min_offs = offs; } } /* Calculate free space in the block. */ free = min_offs - ((char *)last - (char *)s->base) - sizeof(uint32_t); if (!s->not_found) free += EXT4_XATTR_SIZE(s->here->value_size) + EXT4_XATTR_LEN(s->here->name_len); if (i->value) { /* See whether there is enough space to hold new entry */ if (free < EXT4_XATTR_SIZE(i->value_len) + EXT4_XATTR_LEN(name_len)) return ENOSPC; } /* Return EOK now if we do not intend to modify the content. */ if (dry_run) return EOK; /* First remove the old entry's data part */ if (!s->not_found) { size_t value_offs = to_le16(s->here->value_offs); void *value = (char *)s->base + value_offs; void *first_value = (char *)s->base + min_offs; size_t value_size = EXT4_XATTR_SIZE(to_le32(s->here->value_size)); if (value_offs) { /* Remove the data part. */ memmove((char *)first_value + value_size, first_value, (char *)value - (char *)first_value); /* Zero the gap created */ memset(first_value, 0, value_size); /* * Calculate the new min_offs after removal of the old * entry's data part */ min_offs += value_size; } /* * Adjust the value offset of entries which has value offset * prior to the s->here. The offset of these entries won't be * shifted if the size of the entry we removed is zero. */ for (last = s->first; !EXT4_XATTR_IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { size_t offs = to_le16(last->value_offs); /* For zero-value-length entry, offs will be zero. */ if (offs < value_offs) last->value_offs = to_le16(offs + value_size); } } /* If caller wants us to insert... */ if (i->value) { size_t value_offs; if (i->value_len) value_offs = min_offs - EXT4_XATTR_SIZE(i->value_len); else value_offs = 0; if (!s->not_found) { struct ext4_xattr_entry *here = s->here; /* Reuse the current entry we have got */ here->value_offs = to_le16(value_offs); here->value_size = to_le32(i->value_len); } else { /* Insert a new entry */ last->name_len = (uint8_t)name_len; last->name_index = i->name_index; last->value_offs = to_le16(value_offs); last->value_block = 0; last->value_size = to_le32(i->value_len); memcpy(EXT4_XATTR_NAME(last), i->name, name_len); /* Set valid last entry indicator */ *(uint32_t *)EXT4_XATTR_NEXT(last) = 0; s->here = last; } /* Insert the value's part */ if (value_offs) { memcpy((char *)s->base + value_offs, i->value, i->value_len); /* Clear the padding bytes if there is */ if (EXT4_XATTR_SIZE(i->value_len) != i->value_len) memset((char *)s->base + value_offs + i->value_len, 0, EXT4_XATTR_SIZE(i->value_len) - i->value_len); } } else { size_t shift_offs;Lines 1–500 of 1627. Load more
/* * Copyright (c) 2017 Grzegorz Kostka (kostka.grzegorz@gmail.com) * Copyright (c) 2017 Kaho Ng (ngkaho1234@gmail.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. */ /** @addtogroup lwext4 * @{ */ /** * @file ext4_xattr.c * @brief Extended Attribute manipulation. */ #include <ext4_config.h> #include <ext4_debug.h> #include <ext4_errno.h> #include <ext4_misc.h> #include <ext4_types.h> #include <ext4_balloc.h> #include <ext4_block_group.h> #include <ext4_blockdev.h> #include <ext4_crc32.h> #include <ext4_fs.h> #include <ext4_inode.h> #include <ext4_super.h> #include <ext4_trans.h> #include <ext4_xattr.h> #include <stdlib.h> #include <string.h> #if CONFIG_XATTR_ENABLE /** * @file ext4_xattr.c * @brief Extended Attribute Manipulation */ /* Extended Attribute(EA) */ /* Magic value in attribute blocks */ #define EXT4_XATTR_MAGIC 0xEA020000 /* Maximum number of references to one attribute block */ #define EXT4_XATTR_REFCOUNT_MAX 1024 /* Name indexes */ #define EXT4_XATTR_INDEX_USER 1 #define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS 2 #define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT 3 #define EXT4_XATTR_INDEX_TRUSTED 4 #define EXT4_XATTR_INDEX_LUSTRE 5 #define EXT4_XATTR_INDEX_SECURITY 6 #define EXT4_XATTR_INDEX_SYSTEM 7 #define EXT4_XATTR_INDEX_RICHACL 8 #define EXT4_XATTR_INDEX_ENCRYPTION 9 #define EXT4_XATTR_PAD_BITS 2 #define EXT4_XATTR_PAD (1 << EXT4_XATTR_PAD_BITS) #define EXT4_XATTR_ROUND (EXT4_XATTR_PAD - 1) #define EXT4_XATTR_LEN(name_len) \ (((name_len) + EXT4_XATTR_ROUND + sizeof(struct ext4_xattr_entry)) & \ ~EXT4_XATTR_ROUND) #define EXT4_XATTR_NEXT(entry) \ ((struct ext4_xattr_entry *)((char *)(entry) + \ EXT4_XATTR_LEN((entry)->name_len))) #define EXT4_XATTR_SIZE(size) (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND) #define EXT4_XATTR_NAME(entry) ((char *)((entry) + 1)) #define EXT4_XATTR_IHDR(sb, raw_inode) \ ((struct ext4_xattr_ibody_header *)((char *)raw_inode + \ EXT4_GOOD_OLD_INODE_SIZE + \ ext4_inode_get_extra_isize( \ sb, raw_inode))) #define EXT4_XATTR_IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr) + 1)) #define EXT4_XATTR_BHDR(block) ((struct ext4_xattr_header *)((block)->data)) #define EXT4_XATTR_ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr)) #define EXT4_XATTR_BFIRST(block) EXT4_XATTR_ENTRY(EXT4_XATTR_BHDR(block) + 1) #define EXT4_XATTR_IS_LAST_ENTRY(entry) (*(uint32_t *)(entry) == 0) #define EXT4_ZERO_XATTR_VALUE ((void *)-1) #ifdef __plan9__ #pragma pack on #else #pragma pack(push, 1) #endif struct ext4_xattr_header { uint32_t magic; /* magic number for identification */ uint32_t refcount; /* reference count */ uint32_t blocks; /* number of disk blocks used */ uint32_t hash; /* hash value of all attributes */ uint32_t checksum; /* crc32c(uuid+id+xattrblock) */ /* id = inum if refcount=1, blknum otherwise */ uint32_t reserved[3]; /* zero right now */ }; struct ext4_xattr_ibody_header { uint32_t magic; /* magic number for identification */ }; struct ext4_xattr_entry { uint8_t name_len; /* length of name */ uint8_t name_index; /* attribute name index */ uint16_t value_offs; /* offset in disk block of value */ uint32_t value_block; /* disk block attribute is stored on (n/i) */ uint32_t value_size; /* size of attribute value */ uint32_t hash; /* hash value of name and value */ }; #ifdef __plan9__ #pragma pack off #else #pragma pack(pop) #endif #define EXT4_XATTR_NAME_HASH_SHIFT 5 #define EXT4_XATTR_VALUE_HASH_SHIFT 16 #define EXT4_XATTR_BLOCK_HASH_SHIFT 16 /**@brief Generic hash function (1 byte as padding unit) * @param hash an input hash to be used (0 if there is no input hash) * @param buf buffer * @param buflen length of buffer * @param shift number of bits to shift for each round * @return hash of the buffer */ static inline uint32_t ext4_xattr_generic_hash(uint32_t hash, const char *buf, size_t buflen, int shift) { size_t i; uint32_t ret; /* Input hash value to be used */ ret = hash; for (i = 0; i < buflen; ++i) { /* For each round, rotate the hash from the lowest bit to the * highest bit. Then XOR the hash against the content of * buffer */ ret = (ret << shift) ^ (ret >> (8 * sizeof(ret) - shift)) ^ buf[i]; } return ret; } /**@brief Generic hash function (4 byte as padding unit) * @param hash an input hash to be used (0 if there is no input hash) * @param buf buffer * @param buflen length of buffer * @param shift number of bits to shift for each round * @return hash of the buffer */ static inline uint32_t ext4_xattr_generic_hash32(uint32_t hash, const uint32_t *buf, size_t buflen, int shift) { size_t i; uint32_t ret; /* Input hash value to be used */ ret = hash; /* Calculate the number of uint32_t blocks */ buflen = (buflen + sizeof(uint32_t) - 1) / sizeof(uint32_t); for (i = 0; i < buflen; ++i) { /* For each round, rotate the hash from the lowest bit to the * highest bit. Then XOR the hash against the content of * buffer */ ret = (ret << shift) ^ (ret >> (8 * sizeof(ret) - shift)) ^ to_le32(buf[i]); } return ret; } /**@brief Calculate and set hash value of an EA * @param header header of EA block * @param entry EA entry */ static inline void ext4_xattr_entry_hash(struct ext4_xattr_header *header, struct ext4_xattr_entry *entry) { uint32_t hash = 0; char *name; uint8_t namelen; name = EXT4_XATTR_NAME(entry); namelen = entry->name_len; /* Calculate the hash of the name of the EA entry */ hash = ext4_xattr_generic_hash(hash, name, namelen, EXT4_XATTR_NAME_HASH_SHIFT); /* Make sure that the entry is valid, or the content of the entry is * not zero in size */ if (entry->value_block == 0 && entry->value_size != 0) { uint32_t *value; value = (uint32_t *)((char *)header + to_le16(entry->value_offs)); /* Then calculate the hash of the content of the EA entry */ hash = ext4_xattr_generic_hash32(hash, value, to_le32(entry->value_size), EXT4_XATTR_VALUE_HASH_SHIFT); } entry->hash = to_le32(hash); } /**@brief Calculate and set hash value of the whole EA block * @param header header of EA block */ static void ext4_xattr_block_hash(struct ext4_xattr_header *header) { struct ext4_xattr_entry *here; uint32_t hash = 0; /* Calculate hash for all the entries and store it in * header. The hash algorithm is very similar to the above * hash routines for buffers */ for (here = EXT4_XATTR_ENTRY(header + 1); !EXT4_XATTR_IS_LAST_ENTRY(here); here = EXT4_XATTR_NEXT(here)) { hash = (hash << EXT4_XATTR_BLOCK_HASH_SHIFT) ^ (hash >> (8 * sizeof(hash) - EXT4_XATTR_BLOCK_HASH_SHIFT)) ^ to_le32(here->hash); } header->hash = to_le32(hash); } #if CONFIG_META_CSUM_ENABLE static uint32_t ext4_xattr_block_checksum(struct ext4_inode_ref *inode_ref, ext4_fsblk_t blocknr, struct ext4_xattr_header *header) { uint32_t checksum = 0; uint64_t le64_blocknr = blocknr; struct ext4_sblock *sb = &inode_ref->fs->sb; if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { uint32_t orig_checksum; /* Preparation: temporarily set bg checksum to 0 */ orig_checksum = header->checksum; header->checksum = 0; /* First calculate crc32 checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); /* Then calculate crc32 checksum block number */ checksum = ext4_crc32c(checksum, &le64_blocknr, sizeof(le64_blocknr)); /* Finally calculate crc32 checksum against * the entire xattr block */ checksum = ext4_crc32c(checksum, header, ext4_sb_get_block_size(sb)); header->checksum = orig_checksum; } return checksum; } #else #define ext4_xattr_block_checksum(...) 0 #endif static void ext4_xattr_set_block_checksum(struct ext4_inode_ref *inode_ref, ext4_fsblk_t blocknr __unused, struct ext4_xattr_header *header) { struct ext4_sblock *sb = &inode_ref->fs->sb; if (!ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) return; header->checksum = ext4_xattr_block_checksum(inode_ref, blocknr, header); } struct xattr_prefix { const char *prefix; uint8_t name_index; }; static const struct xattr_prefix prefix_tbl[] = { {"user.", EXT4_XATTR_INDEX_USER}, {"system.posix_acl_access", EXT4_XATTR_INDEX_POSIX_ACL_ACCESS}, {"system.posix_acl_default", EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT}, {"trusted.", EXT4_XATTR_INDEX_TRUSTED}, {"security.", EXT4_XATTR_INDEX_SECURITY}, {"system.", EXT4_XATTR_INDEX_SYSTEM}, {"system.richacl", EXT4_XATTR_INDEX_RICHACL}, {NULL, 0}, }; const char *ext4_extract_xattr_name(const char *full_name, size_t full_name_len, uint8_t *name_index, size_t *name_len, bool *found) { int i; ext4_assert(name_index); ext4_assert(found); *found = false; if (!full_name_len) { if (name_len) *name_len = 0; return NULL; } for (i = 0; prefix_tbl[i].prefix; i++) { size_t prefix_len = strlen(prefix_tbl[i].prefix); if (full_name_len >= prefix_len && !memcmp(full_name, prefix_tbl[i].prefix, prefix_len)) { bool require_name = prefix_tbl[i].prefix[prefix_len - 1] == '.'; *name_index = prefix_tbl[i].name_index; if (name_len) *name_len = full_name_len - prefix_len; if (!(full_name_len - prefix_len) && require_name) return NULL; *found = true; if (require_name) return full_name + prefix_len; return NULL; } } if (name_len) *name_len = 0; return NULL; } const char *ext4_get_xattr_name_prefix(uint8_t name_index, size_t *ret_prefix_len) { int i; for (i = 0; prefix_tbl[i].prefix; i++) { size_t prefix_len = strlen(prefix_tbl[i].prefix); if (prefix_tbl[i].name_index == name_index) { if (ret_prefix_len) *ret_prefix_len = prefix_len; return prefix_tbl[i].prefix; } } if (ret_prefix_len) *ret_prefix_len = 0; return NULL; } static const char ext4_xattr_empty_value; /** * @brief Insert/Remove/Modify the given entry * * @param i The information of the given EA entry * @param s Search context block * @param dry_run Do not modify the content of the buffer * * @return Return EOK when finished, ENOSPC when there is no enough space */ static int ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s, bool dry_run) { struct ext4_xattr_entry *last; size_t free, min_offs = (char *)s->end - (char *)s->base, name_len = i->name_len; /* * If the entry is going to be removed but not found, return 0 to * indicate success. */ if (!i->value && s->not_found) return EOK; /* Compute min_offs and last. */ last = s->first; for (; !EXT4_XATTR_IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { if (last->value_size) { size_t offs = to_le16(last->value_offs); if (offs < min_offs) min_offs = offs; } } /* Calculate free space in the block. */ free = min_offs - ((char *)last - (char *)s->base) - sizeof(uint32_t); if (!s->not_found) free += EXT4_XATTR_SIZE(s->here->value_size) + EXT4_XATTR_LEN(s->here->name_len); if (i->value) { /* See whether there is enough space to hold new entry */ if (free < EXT4_XATTR_SIZE(i->value_len) + EXT4_XATTR_LEN(name_len)) return ENOSPC; } /* Return EOK now if we do not intend to modify the content. */ if (dry_run) return EOK; /* First remove the old entry's data part */ if (!s->not_found) { size_t value_offs = to_le16(s->here->value_offs); void *value = (char *)s->base + value_offs; void *first_value = (char *)s->base + min_offs; size_t value_size = EXT4_XATTR_SIZE(to_le32(s->here->value_size)); if (value_offs) { /* Remove the data part. */ memmove((char *)first_value + value_size, first_value, (char *)value - (char *)first_value); /* Zero the gap created */ memset(first_value, 0, value_size); /* * Calculate the new min_offs after removal of the old * entry's data part */ min_offs += value_size; } /* * Adjust the value offset of entries which has value offset * prior to the s->here. The offset of these entries won't be * shifted if the size of the entry we removed is zero. */ for (last = s->first; !EXT4_XATTR_IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { size_t offs = to_le16(last->value_offs); /* For zero-value-length entry, offs will be zero. */ if (offs < value_offs) last->value_offs = to_le16(offs + value_size); } } /* If caller wants us to insert... */ if (i->value) { size_t value_offs; if (i->value_len) value_offs = min_offs - EXT4_XATTR_SIZE(i->value_len); else value_offs = 0; if (!s->not_found) { struct ext4_xattr_entry *here = s->here; /* Reuse the current entry we have got */ here->value_offs = to_le16(value_offs); here->value_size = to_le32(i->value_len); } else { /* Insert a new entry */ last->name_len = (uint8_t)name_len; last->name_index = i->name_index; last->value_offs = to_le16(value_offs); last->value_block = 0; last->value_size = to_le32(i->value_len); memcpy(EXT4_XATTR_NAME(last), i->name, name_len); /* Set valid last entry indicator */ *(uint32_t *)EXT4_XATTR_NEXT(last) = 0; s->here = last; } /* Insert the value's part */ if (value_offs) { memcpy((char *)s->base + value_offs, i->value, i->value_len); /* Clear the padding bytes if there is */ if (EXT4_XATTR_SIZE(i->value_len) != i->value_len) memset((char *)s->base + value_offs + i->value_len, 0, EXT4_XATTR_SIZE(i->value_len) - i->value_len); } } else { size_t shift_offs;