⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xattr.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
{	handle_t *handle;	int error, retries = 0;retry:	handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb));	if (IS_ERR(handle)) {		error = PTR_ERR(handle);	} else {		int error2;		error = ext4_xattr_set_handle(handle, inode, name_index, name,					      value, value_len, flags);		error2 = ext4_journal_stop(handle);		if (error == -ENOSPC &&		    ext4_should_retry_alloc(inode->i_sb, &retries))			goto retry;		if (error == 0)			error = error2;	}	return error;}/* * Shift the EA entries in the inode to create space for the increased * i_extra_isize. */static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry,				     int value_offs_shift, void *to,				     void *from, size_t n, int blocksize){	struct ext4_xattr_entry *last = entry;	int new_offs;	/* Adjust the value offsets of the entries */	for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {		if (!last->e_value_block && last->e_value_size) {			new_offs = le16_to_cpu(last->e_value_offs) +							value_offs_shift;			BUG_ON(new_offs + le32_to_cpu(last->e_value_size)				 > blocksize);			last->e_value_offs = cpu_to_le16(new_offs);		}	}	/* Shift the entries by n bytes */	memmove(to, from, n);}/* * Expand an inode by new_extra_isize bytes when EAs are present. * Returns 0 on success or negative error number on failure. */int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,			       struct ext4_inode *raw_inode, handle_t *handle){	struct ext4_xattr_ibody_header *header;	struct ext4_xattr_entry *entry, *last, *first;	struct buffer_head *bh = NULL;	struct ext4_xattr_ibody_find *is = NULL;	struct ext4_xattr_block_find *bs = NULL;	char *buffer = NULL, *b_entry_name = NULL;	size_t min_offs, free;	int total_ino, total_blk;	void *base, *start, *end;	int extra_isize = 0, error = 0, tried_min_extra_isize = 0;	int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);	down_write(&EXT4_I(inode)->xattr_sem);retry:	if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {		up_write(&EXT4_I(inode)->xattr_sem);		return 0;	}	header = IHDR(inode, raw_inode);	entry = IFIRST(header);	/*	 * Check if enough free space is available in the inode to shift the	 * entries ahead by new_extra_isize.	 */	base = start = entry;	end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;	min_offs = end - base;	last = entry;	total_ino = sizeof(struct ext4_xattr_ibody_header);	free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);	if (free >= new_extra_isize) {		entry = IFIRST(header);		ext4_xattr_shift_entries(entry,	EXT4_I(inode)->i_extra_isize				- new_extra_isize, (void *)raw_inode +				EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,				(void *)header, total_ino,				inode->i_sb->s_blocksize);		EXT4_I(inode)->i_extra_isize = new_extra_isize;		error = 0;		goto cleanup;	}	/*	 * Enough free space isn't available in the inode, check if	 * EA block can hold new_extra_isize bytes.	 */	if (EXT4_I(inode)->i_file_acl) {		bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);		error = -EIO;		if (!bh)			goto cleanup;		if (ext4_xattr_check_block(bh)) {			ext4_error(inode->i_sb, __FUNCTION__,				"inode %lu: bad block %llu", inode->i_ino,				EXT4_I(inode)->i_file_acl);			error = -EIO;			goto cleanup;		}		base = BHDR(bh);		first = BFIRST(bh);		end = bh->b_data + bh->b_size;		min_offs = end - base;		free = ext4_xattr_free_space(first, &min_offs, base,					     &total_blk);		if (free < new_extra_isize) {			if (!tried_min_extra_isize && s_min_extra_isize) {				tried_min_extra_isize++;				new_extra_isize = s_min_extra_isize;				brelse(bh);				goto retry;			}			error = -1;			goto cleanup;		}	} else {		free = inode->i_sb->s_blocksize;	}	while (new_extra_isize > 0) {		size_t offs, size, entry_size;		struct ext4_xattr_entry *small_entry = NULL;		struct ext4_xattr_info i = {			.value = NULL,			.value_len = 0,		};		unsigned int total_size;  /* EA entry size + value size */		unsigned int shift_bytes; /* No. of bytes to shift EAs by? */		unsigned int min_total_size = ~0U;		is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS);		bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS);		if (!is || !bs) {			error = -ENOMEM;			goto cleanup;		}		is->s.not_found = -ENODATA;		bs->s.not_found = -ENODATA;		is->iloc.bh = NULL;		bs->bh = NULL;		last = IFIRST(header);		/* Find the entry best suited to be pushed into EA block */		entry = NULL;		for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {			total_size =			EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +					EXT4_XATTR_LEN(last->e_name_len);			if (total_size <= free && total_size < min_total_size) {				if (total_size < new_extra_isize) {					small_entry = last;				} else {					entry = last;					min_total_size = total_size;				}			}		}		if (entry == NULL) {			if (small_entry) {				entry = small_entry;			} else {				if (!tried_min_extra_isize &&				    s_min_extra_isize) {					tried_min_extra_isize++;					new_extra_isize = s_min_extra_isize;					goto retry;				}				error = -1;				goto cleanup;			}		}		offs = le16_to_cpu(entry->e_value_offs);		size = le32_to_cpu(entry->e_value_size);		entry_size = EXT4_XATTR_LEN(entry->e_name_len);		i.name_index = entry->e_name_index,		buffer = kmalloc(EXT4_XATTR_SIZE(size), GFP_NOFS);		b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS);		if (!buffer || !b_entry_name) {			error = -ENOMEM;			goto cleanup;		}		/* Save the entry name and the entry value */		memcpy(buffer, (void *)IFIRST(header) + offs,		       EXT4_XATTR_SIZE(size));		memcpy(b_entry_name, entry->e_name, entry->e_name_len);		b_entry_name[entry->e_name_len] = '\0';		i.name = b_entry_name;		error = ext4_get_inode_loc(inode, &is->iloc);		if (error)			goto cleanup;		error = ext4_xattr_ibody_find(inode, &i, is);		if (error)			goto cleanup;		/* Remove the chosen entry from the inode */		error = ext4_xattr_ibody_set(handle, inode, &i, is);		entry = IFIRST(header);		if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize)			shift_bytes = new_extra_isize;		else			shift_bytes = entry_size + size;		/* Adjust the offsets and shift the remaining entries ahead */		ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize -			shift_bytes, (void *)raw_inode +			EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes,			(void *)header, total_ino - entry_size,			inode->i_sb->s_blocksize);		extra_isize += shift_bytes;		new_extra_isize -= shift_bytes;		EXT4_I(inode)->i_extra_isize = extra_isize;		i.name = b_entry_name;		i.value = buffer;		i.value_len = size;		error = ext4_xattr_block_find(inode, &i, bs);		if (error)			goto cleanup;		/* Add entry which was removed from the inode into the block */		error = ext4_xattr_block_set(handle, inode, &i, bs);		if (error)			goto cleanup;		kfree(b_entry_name);		kfree(buffer);		brelse(is->iloc.bh);		kfree(is);		kfree(bs);	}	brelse(bh);	up_write(&EXT4_I(inode)->xattr_sem);	return 0;cleanup:	kfree(b_entry_name);	kfree(buffer);	if (is)		brelse(is->iloc.bh);	kfree(is);	kfree(bs);	brelse(bh);	up_write(&EXT4_I(inode)->xattr_sem);	return error;}/* * ext4_xattr_delete_inode() * * Free extended attribute resources associated with this inode. This * is called immediately before an inode is freed. We have exclusive * access to the inode. */voidext4_xattr_delete_inode(handle_t *handle, struct inode *inode){	struct buffer_head *bh = NULL;	if (!EXT4_I(inode)->i_file_acl)		goto cleanup;	bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);	if (!bh) {		ext4_error(inode->i_sb, __FUNCTION__,			"inode %lu: block %llu read error", inode->i_ino,			EXT4_I(inode)->i_file_acl);		goto cleanup;	}	if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||	    BHDR(bh)->h_blocks != cpu_to_le32(1)) {		ext4_error(inode->i_sb, __FUNCTION__,			"inode %lu: bad block %llu", inode->i_ino,			EXT4_I(inode)->i_file_acl);		goto cleanup;	}	ext4_xattr_release_block(handle, inode, bh);	EXT4_I(inode)->i_file_acl = 0;cleanup:	brelse(bh);}/* * ext4_xattr_put_super() * * This is called when a file system is unmounted. */voidext4_xattr_put_super(struct super_block *sb){	mb_cache_shrink(sb->s_bdev);}/* * ext4_xattr_cache_insert() * * Create a new entry in the extended attribute cache, and insert * it unless such an entry is already in the cache. * * Returns 0, or a negative error number on failure. */static voidext4_xattr_cache_insert(struct buffer_head *bh){	__u32 hash = le32_to_cpu(BHDR(bh)->h_hash);	struct mb_cache_entry *ce;	int error;	ce = mb_cache_entry_alloc(ext4_xattr_cache);	if (!ce) {		ea_bdebug(bh, "out of memory");		return;	}	error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, &hash);	if (error) {		mb_cache_entry_free(ce);		if (error == -EBUSY) {			ea_bdebug(bh, "already in cache");			error = 0;		}	} else {		ea_bdebug(bh, "inserting [%x]", (int)hash);		mb_cache_entry_release(ce);	}}/* * ext4_xattr_cmp() * * Compare two extended attribute blocks for equality. * * Returns 0 if the blocks are equal, 1 if they differ, and * a negative error number on errors. */static intext4_xattr_cmp(struct ext4_xattr_header *header1,	       struct ext4_xattr_header *header2){	struct ext4_xattr_entry *entry1, *entry2;	entry1 = ENTRY(header1+1);	entry2 = ENTRY(header2+1);	while (!IS_LAST_ENTRY(entry1)) {		if (IS_LAST_ENTRY(entry2))			return 1;		if (entry1->e_hash != entry2->e_hash ||		    entry1->e_name_index != entry2->e_name_index ||		    entry1->e_name_len != entry2->e_name_len ||		    entry1->e_value_size != entry2->e_value_size ||		    memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len))			return 1;		if (entry1->e_value_block != 0 || entry2->e_value_block != 0)			return -EIO;		if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs),			   (char *)header2 + le16_to_cpu(entry2->e_value_offs),			   le32_to_cpu(entry1->e_value_size)))			return 1;		entry1 = EXT4_XATTR_NEXT(entry1);		entry2 = EXT4_XATTR_NEXT(entry2);	}	if (!IS_LAST_ENTRY(entry2))		return 1;	return 0;}/* * ext4_xattr_cache_find() * * Find an identical extended attribute block. * * Returns a pointer to the block found, or NULL if such a block was * not found or an error occurred. */static struct buffer_head *ext4_xattr_cache_find(struct inode *inode, struct ext4_xattr_header *header,		      struct mb_cache_entry **pce){	__u32 hash = le32_to_cpu(header->h_hash);	struct mb_cache_entry *ce;	if (!header->h_hash)		return NULL;  /* never share */	ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);again:	ce = mb_cache_entry_find_first(ext4_xattr_cache, 0,				       inode->i_sb->s_bdev, hash);	while (ce) {		struct buffer_head *bh;		if (IS_ERR(ce)) {			if (PTR_ERR(ce) == -EAGAIN)				goto again;			break;		}		bh = sb_bread(inode->i_sb, ce->e_block);		if (!bh) {			ext4_error(inode->i_sb, __FUNCTION__,				"inode %lu: block %lu read error",				inode->i_ino, (unsigned long) ce->e_block);		} else if (le32_to_cpu(BHDR(bh)->h_refcount) >=				EXT4_XATTR_REFCOUNT_MAX) {			ea_idebug(inode, "block %lu refcount %d>=%d",				  (unsigned long) ce->e_block,				  le32_to_cpu(BHDR(bh)->h_refcount),					  EXT4_XATTR_REFCOUNT_MAX);		} else if (ext4_xattr_cmp(header, BHDR(bh)) == 0) {			*pce = ce;			return bh;		}		brelse(bh);		ce = mb_cache_entry_find_next(ce, 0, inode->i_sb->s_bdev, hash);	}	return NULL;}#define NAME_HASH_SHIFT 5#define VALUE_HASH_SHIFT 16/* * ext4_xattr_hash_entry() * * Compute the hash of an extended attribute. */static inline void ext4_xattr_hash_entry(struct ext4_xattr_header *header,					 struct ext4_xattr_entry *entry){	__u32 hash = 0;	char *name = entry->e_name;	int n;	for (n=0; n < entry->e_name_len; n++) {		hash = (hash << NAME_HASH_SHIFT) ^		       (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^		       *name++;	}	if (entry->e_value_block == 0 && entry->e_value_size != 0) {		__le32 *value = (__le32 *)((char *)header +			le16_to_cpu(entry->e_value_offs));		for (n = (le32_to_cpu(entry->e_value_size) +		     EXT4_XATTR_ROUND) >> EXT4_XATTR_PAD_BITS; n; n--) {			hash = (hash << VALUE_HASH_SHIFT) ^			       (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^			       le32_to_cpu(*value++);		}	}	entry->e_hash = cpu_to_le32(hash);}#undef NAME_HASH_SHIFT#undef VALUE_HASH_SHIFT#define BLOCK_HASH_SHIFT 16/* * ext4_xattr_rehash() * * Re-compute the extended attribute hash value after an entry has changed. */static void ext4_xattr_rehash(struct ext4_xattr_header *header,			      struct ext4_xattr_entry *entry){	struct ext4_xattr_entry *here;	__u32 hash = 0;	ext4_xattr_hash_entry(header, entry);	here = ENTRY(header+1);	while (!IS_LAST_ENTRY(here)) {		if (!here->e_hash) {			/* Block is not shared if an entry's hash value == 0 */			hash = 0;			break;		}		hash = (hash << BLOCK_HASH_SHIFT) ^		       (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^		       le32_to_cpu(here->e_hash);		here = EXT4_XATTR_NEXT(here);	}	header->h_hash = cpu_to_le32(hash);}#undef BLOCK_HASH_SHIFTint __initinit_ext4_xattr(void){	ext4_xattr_cache = mb_cache_create("ext4_xattr", NULL,		sizeof(struct mb_cache_entry) +		sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 6);	if (!ext4_xattr_cache)		return -ENOMEM;	return 0;}voidexit_ext4_xattr(void){	if (ext4_xattr_cache)		mb_cache_destroy(ext4_xattr_cache);	ext4_xattr_cache = NULL;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -