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

📄 xattr.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * linux/fs/ext4/xattr.c * * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> * * Fix by Harrison Xing <harrison@mountainviewdata.com>. * Ext4 code with a lot of help from Eric Jarman <ejarman@acm.org>. * Extended attributes for symlinks and special files added per *  suggestion of Luka Renko <luka.renko@hermes.si>. * xattr consolidation Copyright (c) 2004 James Morris <jmorris@redhat.com>, *  Red Hat Inc. * ea-in-inode support by Alex Tomas <alex@clusterfs.com> aka bzzz *  and Andreas Gruenbacher <agruen@suse.de>. *//* * Extended attributes are stored directly in inodes (on file systems with * inodes bigger than 128 bytes) and on additional disk blocks. The i_file_acl * field contains the block number if an inode uses an additional block. All * attributes must fit in the inode and one additional block. Blocks that * contain the identical set of attributes may be shared among several inodes. * Identical blocks are detected by keeping a cache of blocks that have * recently been accessed. * * The attributes in inodes and on blocks have a different header; the entries * are stored in the same format: * *   +------------------+ *   | header           | *   | entry 1          | | *   | entry 2          | | growing downwards *   | entry 3          | v *   | four null bytes  | *   | . . .            | *   | value 1          | ^ *   | value 3          | | growing upwards *   | value 2          | | *   +------------------+ * * The header is followed by multiple entry descriptors. In disk blocks, the * entry descriptors are kept sorted. In inodes, they are unsorted. The * attribute values are aligned to the end of the block in no specific order. * * Locking strategy * ---------------- * EXT4_I(inode)->i_file_acl is protected by EXT4_I(inode)->xattr_sem. * EA blocks are only changed if they are exclusive to an inode, so * holding xattr_sem also means that nothing but the EA block's reference * count can change. Multiple writers to the same block are synchronized * by the buffer lock. */#include <linux/init.h>#include <linux/fs.h>#include <linux/slab.h>#include <linux/ext4_jbd2.h>#include <linux/ext4_fs.h>#include <linux/mbcache.h>#include <linux/quotaops.h>#include <linux/rwsem.h>#include "xattr.h"#include "acl.h"#define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data))#define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))#define BFIRST(bh) ENTRY(BHDR(bh)+1)#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)#ifdef EXT4_XATTR_DEBUG# define ea_idebug(inode, f...) do { \		printk(KERN_DEBUG "inode %s:%lu: ", \			inode->i_sb->s_id, inode->i_ino); \		printk(f); \		printk("\n"); \	} while (0)# define ea_bdebug(bh, f...) do { \		char b[BDEVNAME_SIZE]; \		printk(KERN_DEBUG "block %s:%lu: ", \			bdevname(bh->b_bdev, b), \			(unsigned long) bh->b_blocknr); \		printk(f); \		printk("\n"); \	} while (0)#else# define ea_idebug(f...)# define ea_bdebug(f...)#endifstatic void ext4_xattr_cache_insert(struct buffer_head *);static struct buffer_head *ext4_xattr_cache_find(struct inode *,						 struct ext4_xattr_header *,						 struct mb_cache_entry **);static void ext4_xattr_rehash(struct ext4_xattr_header *,			      struct ext4_xattr_entry *);static struct mb_cache *ext4_xattr_cache;static struct xattr_handler *ext4_xattr_handler_map[] = {	[EXT4_XATTR_INDEX_USER]		     = &ext4_xattr_user_handler,#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL	[EXT4_XATTR_INDEX_POSIX_ACL_ACCESS]  = &ext4_xattr_acl_access_handler,	[EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT] = &ext4_xattr_acl_default_handler,#endif	[EXT4_XATTR_INDEX_TRUSTED]	     = &ext4_xattr_trusted_handler,#ifdef CONFIG_EXT4DEV_FS_SECURITY	[EXT4_XATTR_INDEX_SECURITY]	     = &ext4_xattr_security_handler,#endif};struct xattr_handler *ext4_xattr_handlers[] = {	&ext4_xattr_user_handler,	&ext4_xattr_trusted_handler,#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL	&ext4_xattr_acl_access_handler,	&ext4_xattr_acl_default_handler,#endif#ifdef CONFIG_EXT4DEV_FS_SECURITY	&ext4_xattr_security_handler,#endif	NULL};static inline struct xattr_handler *ext4_xattr_handler(int name_index){	struct xattr_handler *handler = NULL;	if (name_index > 0 && name_index < ARRAY_SIZE(ext4_xattr_handler_map))		handler = ext4_xattr_handler_map[name_index];	return handler;}/* * Inode operation listxattr() * * dentry->d_inode->i_mutex: don't care */ssize_text4_listxattr(struct dentry *dentry, char *buffer, size_t size){	return ext4_xattr_list(dentry->d_inode, buffer, size);}static intext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end){	while (!IS_LAST_ENTRY(entry)) {		struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry);		if ((void *)next >= end)			return -EIO;		entry = next;	}	return 0;}static inline intext4_xattr_check_block(struct buffer_head *bh){	int error;	if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||	    BHDR(bh)->h_blocks != cpu_to_le32(1))		return -EIO;	error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size);	return error;}static inline intext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size){	size_t value_size = le32_to_cpu(entry->e_value_size);	if (entry->e_value_block != 0 || value_size > size ||	    le16_to_cpu(entry->e_value_offs) + value_size > size)		return -EIO;	return 0;}static intext4_xattr_find_entry(struct ext4_xattr_entry **pentry, int name_index,		      const char *name, size_t size, int sorted){	struct ext4_xattr_entry *entry;	size_t name_len;	int cmp = 1;	if (name == NULL)		return -EINVAL;	name_len = strlen(name);	entry = *pentry;	for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {		cmp = name_index - entry->e_name_index;		if (!cmp)			cmp = name_len - entry->e_name_len;		if (!cmp)			cmp = memcmp(name, entry->e_name, name_len);		if (cmp <= 0 && (sorted || cmp == 0))			break;	}	*pentry = entry;	if (!cmp && ext4_xattr_check_entry(entry, size))			return -EIO;	return cmp ? -ENODATA : 0;}static intext4_xattr_block_get(struct inode *inode, int name_index, const char *name,		     void *buffer, size_t buffer_size){	struct buffer_head *bh = NULL;	struct ext4_xattr_entry *entry;	size_t size;	int error;	ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",		  name_index, name, buffer, (long)buffer_size);	error = -ENODATA;	if (!EXT4_I(inode)->i_file_acl)		goto cleanup;	ea_idebug(inode, "reading block %u", EXT4_I(inode)->i_file_acl);	bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);	if (!bh)		goto cleanup;	ea_bdebug(bh, "b_count=%d, refcount=%d",		atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount));	if (ext4_xattr_check_block(bh)) {bad_block:	ext4_error(inode->i_sb, __FUNCTION__,			   "inode %lu: bad block %llu", inode->i_ino,			   EXT4_I(inode)->i_file_acl);		error = -EIO;		goto cleanup;	}	ext4_xattr_cache_insert(bh);	entry = BFIRST(bh);	error = ext4_xattr_find_entry(&entry, name_index, name, bh->b_size, 1);	if (error == -EIO)		goto bad_block;	if (error)		goto cleanup;	size = le32_to_cpu(entry->e_value_size);	if (buffer) {		error = -ERANGE;		if (size > buffer_size)			goto cleanup;		memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),		       size);	}	error = size;cleanup:	brelse(bh);	return error;}static intext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,		     void *buffer, size_t buffer_size){	struct ext4_xattr_ibody_header *header;	struct ext4_xattr_entry *entry;	struct ext4_inode *raw_inode;	struct ext4_iloc iloc;	size_t size;	void *end;	int error;	if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR))		return -ENODATA;	error = ext4_get_inode_loc(inode, &iloc);	if (error)		return error;	raw_inode = ext4_raw_inode(&iloc);	header = IHDR(inode, raw_inode);	entry = IFIRST(header);	end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;	error = ext4_xattr_check_names(entry, end);	if (error)		goto cleanup;	error = ext4_xattr_find_entry(&entry, name_index, name,				      end - (void *)entry, 0);	if (error)		goto cleanup;	size = le32_to_cpu(entry->e_value_size);	if (buffer) {		error = -ERANGE;		if (size > buffer_size)			goto cleanup;		memcpy(buffer, (void *)IFIRST(header) +		       le16_to_cpu(entry->e_value_offs), size);	}	error = size;cleanup:	brelse(iloc.bh);	return error;}/* * ext4_xattr_get() * * Copy an extended attribute into the buffer * provided, or compute the buffer size required. * Buffer is NULL to compute the size of the buffer required. * * Returns a negative error number on failure, or the number of bytes * used / required on success. */intext4_xattr_get(struct inode *inode, int name_index, const char *name,	       void *buffer, size_t buffer_size){	int error;	down_read(&EXT4_I(inode)->xattr_sem);	error = ext4_xattr_ibody_get(inode, name_index, name, buffer,				     buffer_size);	if (error == -ENODATA)		error = ext4_xattr_block_get(inode, name_index, name, buffer,					     buffer_size);	up_read(&EXT4_I(inode)->xattr_sem);	return error;}static intext4_xattr_list_entries(struct inode *inode, struct ext4_xattr_entry *entry,			char *buffer, size_t buffer_size){	size_t rest = buffer_size;	for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {		struct xattr_handler *handler =			ext4_xattr_handler(entry->e_name_index);		if (handler) {			size_t size = handler->list(inode, buffer, rest,						    entry->e_name,						    entry->e_name_len);			if (buffer) {				if (size > rest)					return -ERANGE;				buffer += size;			}			rest -= size;		}	}	return buffer_size - rest;}static intext4_xattr_block_list(struct inode *inode, char *buffer, size_t buffer_size){	struct buffer_head *bh = NULL;	int error;	ea_idebug(inode, "buffer=%p, buffer_size=%ld",		  buffer, (long)buffer_size);	error = 0;	if (!EXT4_I(inode)->i_file_acl)		goto cleanup;	ea_idebug(inode, "reading block %u", EXT4_I(inode)->i_file_acl);	bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);	error = -EIO;	if (!bh)		goto cleanup;	ea_bdebug(bh, "b_count=%d, refcount=%d",		atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount));	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;	}	ext4_xattr_cache_insert(bh);	error = ext4_xattr_list_entries(inode, BFIRST(bh), buffer, buffer_size);cleanup:	brelse(bh);	return error;}static intext4_xattr_ibody_list(struct inode *inode, char *buffer, size_t buffer_size){	struct ext4_xattr_ibody_header *header;	struct ext4_inode *raw_inode;	struct ext4_iloc iloc;	void *end;	int error;	if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR))		return 0;	error = ext4_get_inode_loc(inode, &iloc);	if (error)		return error;	raw_inode = ext4_raw_inode(&iloc);	header = IHDR(inode, raw_inode);	end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;	error = ext4_xattr_check_names(IFIRST(header), end);	if (error)		goto cleanup;	error = ext4_xattr_list_entries(inode, IFIRST(header),					buffer, buffer_size);cleanup:	brelse(iloc.bh);	return error;}/* * ext4_xattr_list() * * Copy a list of attribute names into the buffer * provided, or compute the buffer size required. * Buffer is NULL to compute the size of the buffer required. * * Returns a negative error number on failure, or the number of bytes * used / required on success. */intext4_xattr_list(struct inode *inode, char *buffer, size_t buffer_size){	int i_error, b_error;	down_read(&EXT4_I(inode)->xattr_sem);	i_error = ext4_xattr_ibody_list(inode, buffer, buffer_size);	if (i_error < 0) {		b_error = 0;	} else {		if (buffer) {			buffer += i_error;			buffer_size -= i_error;		}		b_error = ext4_xattr_block_list(inode, buffer, buffer_size);		if (b_error < 0)			i_error = 0;	}	up_read(&EXT4_I(inode)->xattr_sem);	return i_error + b_error;}/* * If the EXT4_FEATURE_COMPAT_EXT_ATTR feature of this file system is * not set, set it. */static void ext4_xattr_update_super_block(handle_t *handle,					  struct super_block *sb){	if (EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_EXT_ATTR))		return;	if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) {		EXT4_SET_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_EXT_ATTR);		sb->s_dirt = 1;		ext4_journal_dirty_metadata(handle, EXT4_SB(sb)->s_sbh);	}}/* * Release the xattr block BH: If the reference count is > 1, decrement * it; otherwise free the block. */static voidext4_xattr_release_block(handle_t *handle, struct inode *inode,			 struct buffer_head *bh){	struct mb_cache_entry *ce = NULL;	int error = 0;	ce = mb_cache_entry_get(ext4_xattr_cache, bh->b_bdev, bh->b_blocknr);	error = ext4_journal_get_write_access(handle, bh);	if (error)		goto out;	lock_buffer(bh);	if (BHDR(bh)->h_refcount == cpu_to_le32(1)) {		ea_bdebug(bh, "refcount now=0; freeing");		if (ce)			mb_cache_entry_free(ce);		ext4_free_blocks(handle, inode, bh->b_blocknr, 1);		get_bh(bh);		ext4_forget(handle, 1, inode, bh, bh->b_blocknr);	} else {		BHDR(bh)->h_refcount = cpu_to_le32(				le32_to_cpu(BHDR(bh)->h_refcount) - 1);		error = ext4_journal_dirty_metadata(handle, bh);		if (IS_SYNC(inode))			handle->h_sync = 1;		DQUOT_FREE_BLOCK(inode, 1);		ea_bdebug(bh, "refcount now=%d; releasing",			  le32_to_cpu(BHDR(bh)->h_refcount));		if (ce)			mb_cache_entry_release(ce);	}	unlock_buffer(bh);out:	ext4_std_error(inode->i_sb, error);	return;}/* * Find the available free space for EAs. This also returns the total number of * bytes used by EA entries. */static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,				    size_t *min_offs, void *base, int *total){	for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {		*total += EXT4_XATTR_LEN(last->e_name_len);		if (!last->e_value_block && last->e_value_size) {			size_t offs = le16_to_cpu(last->e_value_offs);			if (offs < *min_offs)				*min_offs = offs;		}	}	return (*min_offs - ((void *)last - base) - sizeof(__u32));}struct ext4_xattr_info {	int name_index;	const char *name;	const void *value;	size_t value_len;};

⌨️ 快捷键说明

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