1627 lines · 41.6 KB · C
RawDownload
/* * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t