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

📄 xattr.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * linux/fs/ext2/xattr.c * * Copyright (C) 2001-2003 Andreas Gruenbacher <agruen@suse.de> * * Fix by Harrison Xing <harrison@mountainviewdata.com>. * 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. * *//* * Extended attributes are stored on disk blocks allocated outside of * any inode. The i_file_acl field is then made to point to this allocated * block. If all extended attributes of an inode are identical, these * inodes may share the same extended attribute block. Such situations * are automatically detected by keeping a cache of recent attribute block * numbers and hashes over the block's contents in memory. * * * Extended attribute block layout: * *   +------------------+ *   | header           | *   | entry 1          | | *   | entry 2          | | growing downwards *   | entry 3          | v *   | four null bytes  | *   | . . .            | *   | value 1          | ^ *   | value 3          | | growing upwards *   | value 2          | | *   +------------------+ * * The block header is followed by multiple entry descriptors. These entry * descriptors are variable in size, and alligned to EXT2_XATTR_PAD * byte boundaries. The entry descriptors are sorted by attribute name, * so that two extended attribute blocks can be compared efficiently. * * Attribute values are aligned to the end of the block, stored in * no specific order. They are also padded to EXT2_XATTR_PAD byte * boundaries. No additional gaps are left between them. * * Locking strategy * ---------------- * EXT2_I(inode)->i_file_acl is protected by EXT2_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 will change. Multiple writers to an EA block are synchronized * by the bh lock. No more than a single bh lock is held at any time * to avoid deadlocks. */#include <linux/buffer_head.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/mbcache.h>#include <linux/quotaops.h>#include <linux/rwsem.h>#include "ext2.h"#include "xattr.h"#include "acl.h"#define HDR(bh) ((struct ext2_xattr_header *)((bh)->b_data))#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr))#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)#ifdef EXT2_XATTR_DEBUG# define ea_idebug(inode, f...) do { \		printk(KERN_DEBUG "inode %s:%ld: ", \			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 int ext2_xattr_set2(struct inode *, struct buffer_head *,			   struct ext2_xattr_header *);static int ext2_xattr_cache_insert(struct buffer_head *);static struct buffer_head *ext2_xattr_cache_find(struct inode *,						 struct ext2_xattr_header *);static void ext2_xattr_rehash(struct ext2_xattr_header *,			      struct ext2_xattr_entry *);static struct mb_cache *ext2_xattr_cache;static struct xattr_handler *ext2_xattr_handler_map[] = {	[EXT2_XATTR_INDEX_USER]		     = &ext2_xattr_user_handler,#ifdef CONFIG_EXT2_FS_POSIX_ACL	[EXT2_XATTR_INDEX_POSIX_ACL_ACCESS]  = &ext2_xattr_acl_access_handler,	[EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT] = &ext2_xattr_acl_default_handler,#endif	[EXT2_XATTR_INDEX_TRUSTED]	     = &ext2_xattr_trusted_handler,#ifdef CONFIG_EXT2_FS_SECURITY	[EXT2_XATTR_INDEX_SECURITY]	     = &ext2_xattr_security_handler,#endif};struct xattr_handler *ext2_xattr_handlers[] = {	&ext2_xattr_user_handler,	&ext2_xattr_trusted_handler,#ifdef CONFIG_EXT2_FS_POSIX_ACL	&ext2_xattr_acl_access_handler,	&ext2_xattr_acl_default_handler,#endif#ifdef CONFIG_EXT2_FS_SECURITY	&ext2_xattr_security_handler,#endif	NULL};static inline struct xattr_handler *ext2_xattr_handler(int name_index){	struct xattr_handler *handler = NULL;	if (name_index > 0 && name_index < ARRAY_SIZE(ext2_xattr_handler_map))		handler = ext2_xattr_handler_map[name_index];	return handler;}/* * ext2_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. */intext2_xattr_get(struct inode *inode, int name_index, const char *name,	       void *buffer, size_t buffer_size){	struct buffer_head *bh = NULL;	struct ext2_xattr_entry *entry;	size_t name_len, size;	char *end;	int error;	ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",		  name_index, name, buffer, (long)buffer_size);	if (name == NULL)		return -EINVAL;	down_read(&EXT2_I(inode)->xattr_sem);	error = -ENODATA;	if (!EXT2_I(inode)->i_file_acl)		goto cleanup;	ea_idebug(inode, "reading block %d", EXT2_I(inode)->i_file_acl);	bh = sb_bread(inode->i_sb, EXT2_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(HDR(bh)->h_refcount));	end = bh->b_data + bh->b_size;	if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||	    HDR(bh)->h_blocks != cpu_to_le32(1)) {bad_block:	ext2_error(inode->i_sb, "ext2_xattr_get",			"inode %ld: bad block %d", inode->i_ino,			EXT2_I(inode)->i_file_acl);		error = -EIO;		goto cleanup;	}	/* find named attribute */	name_len = strlen(name);	error = -ERANGE;	if (name_len > 255)		goto cleanup;	entry = FIRST_ENTRY(bh);	while (!IS_LAST_ENTRY(entry)) {		struct ext2_xattr_entry *next =			EXT2_XATTR_NEXT(entry);		if ((char *)next >= end)			goto bad_block;		if (name_index == entry->e_name_index &&		    name_len == entry->e_name_len &&		    memcmp(name, entry->e_name, name_len) == 0)			goto found;		entry = next;	}	/* Check the remaining name entries */	while (!IS_LAST_ENTRY(entry)) {		struct ext2_xattr_entry *next =			EXT2_XATTR_NEXT(entry);		if ((char *)next >= end)			goto bad_block;		entry = next;	}	if (ext2_xattr_cache_insert(bh))		ea_idebug(inode, "cache insert failed");	error = -ENODATA;	goto cleanup;found:	/* check the buffer size */	if (entry->e_value_block != 0)		goto bad_block;	size = le32_to_cpu(entry->e_value_size);	if (size > inode->i_sb->s_blocksize ||	    le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)		goto bad_block;	if (ext2_xattr_cache_insert(bh))		ea_idebug(inode, "cache insert failed");	if (buffer) {		error = -ERANGE;		if (size > buffer_size)			goto cleanup;		/* return value of attribute */		memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),			size);	}	error = size;cleanup:	brelse(bh);	up_read(&EXT2_I(inode)->xattr_sem);	return error;}/* * ext2_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. */static intext2_xattr_list(struct inode *inode, char *buffer, size_t buffer_size){	struct buffer_head *bh = NULL;	struct ext2_xattr_entry *entry;	char *end;	size_t rest = buffer_size;	int error;	ea_idebug(inode, "buffer=%p, buffer_size=%ld",		  buffer, (long)buffer_size);	down_read(&EXT2_I(inode)->xattr_sem);	error = 0;	if (!EXT2_I(inode)->i_file_acl)		goto cleanup;	ea_idebug(inode, "reading block %d", EXT2_I(inode)->i_file_acl);	bh = sb_bread(inode->i_sb, EXT2_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(HDR(bh)->h_refcount));	end = bh->b_data + bh->b_size;	if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||	    HDR(bh)->h_blocks != cpu_to_le32(1)) {bad_block:	ext2_error(inode->i_sb, "ext2_xattr_list",			"inode %ld: bad block %d", inode->i_ino,			EXT2_I(inode)->i_file_acl);		error = -EIO;		goto cleanup;	}	/* check the on-disk data structure */	entry = FIRST_ENTRY(bh);	while (!IS_LAST_ENTRY(entry)) {		struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);		if ((char *)next >= end)			goto bad_block;		entry = next;	}	if (ext2_xattr_cache_insert(bh))		ea_idebug(inode, "cache insert failed");	/* list the attribute names */	for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);	     entry = EXT2_XATTR_NEXT(entry)) {		struct xattr_handler *handler =			ext2_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) {					error = -ERANGE;					goto cleanup;				}				buffer += size;			}			rest -= size;		}	}	error = buffer_size - rest;  /* total size */cleanup:	brelse(bh);	up_read(&EXT2_I(inode)->xattr_sem);	return error;}/* * Inode operation listxattr() * * dentry->d_inode->i_mutex: don't care */ssize_text2_listxattr(struct dentry *dentry, char *buffer, size_t size){	return ext2_xattr_list(dentry->d_inode, buffer, size);}/* * If the EXT2_FEATURE_COMPAT_EXT_ATTR feature of this file system is * not set, set it. */static void ext2_xattr_update_super_block(struct super_block *sb){	if (EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR))		return;	EXT2_SET_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR);	sb->s_dirt = 1;	mark_buffer_dirty(EXT2_SB(sb)->s_sbh);}/* * ext2_xattr_set() * * Create, replace or remove an extended attribute for this inode. Buffer * is NULL to remove an existing extended attribute, and non-NULL to * either replace an existing extended attribute, or create a new extended * attribute. The flags XATTR_REPLACE and XATTR_CREATE * specify that an extended attribute must exist and must not exist * previous to the call, respectively. * * Returns 0, or a negative error number on failure. */intext2_xattr_set(struct inode *inode, int name_index, const char *name,	       const void *value, size_t value_len, int flags){	struct super_block *sb = inode->i_sb;	struct buffer_head *bh = NULL;	struct ext2_xattr_header *header = NULL;	struct ext2_xattr_entry *here, *last;	size_t name_len, free, min_offs = sb->s_blocksize;	int not_found = 1, error;	char *end;		/*	 * header -- Points either into bh, or to a temporarily	 *           allocated buffer.	 * here -- The named entry found, or the place for inserting, within	 *         the block pointed to by header.	 * last -- Points right after the last named entry within the block	 *         pointed to by header.	 * min_offs -- The offset of the first value (values are aligned	 *             towards the end of the block).	 * end -- Points right after the block pointed to by header.	 */		ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",		  name_index, name, value, (long)value_len);	if (value == NULL)		value_len = 0;	if (name == NULL)		return -EINVAL;	name_len = strlen(name);	if (name_len > 255 || value_len > sb->s_blocksize)		return -ERANGE;	down_write(&EXT2_I(inode)->xattr_sem);	if (EXT2_I(inode)->i_file_acl) {		/* The inode already has an extended attribute block. */		bh = sb_bread(sb, EXT2_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(HDR(bh)->h_refcount));		header = HDR(bh);		end = bh->b_data + bh->b_size;		if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||		    header->h_blocks != cpu_to_le32(1)) {bad_block:		ext2_error(sb, "ext2_xattr_set",				"inode %ld: bad block %d", inode->i_ino, 				   EXT2_I(inode)->i_file_acl);			error = -EIO;			goto cleanup;		}		/* Find the named attribute. */		here = FIRST_ENTRY(bh);		while (!IS_LAST_ENTRY(here)) {			struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here);			if ((char *)next >= end)				goto bad_block;			if (!here->e_value_block && here->e_value_size) {				size_t offs = le16_to_cpu(here->e_value_offs);				if (offs < min_offs)					min_offs = offs;			}			not_found = name_index - here->e_name_index;			if (!not_found)				not_found = name_len - here->e_name_len;			if (!not_found)				not_found = memcmp(name, here->e_name,name_len);			if (not_found <= 0)				break;			here = next;		}		last = here;		/* We still need to compute min_offs and last. */		while (!IS_LAST_ENTRY(last)) {			struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last);			if ((char *)next >= end)				goto bad_block;			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;			}			last = next;		}		/* Check whether we have enough space left. */		free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);	} else {		/* We will use a new extended attribute block. */		free = sb->s_blocksize -			sizeof(struct ext2_xattr_header) - sizeof(__u32);		here = last = NULL;  /* avoid gcc uninitialized warning. */	}	if (not_found) {		/* Request to remove a nonexistent attribute? */		error = -ENODATA;		if (flags & XATTR_REPLACE)			goto cleanup;		error = 0;		if (value == NULL)			goto cleanup;	} else {		/* Request to create an existing attribute? */		error = -EEXIST;		if (flags & XATTR_CREATE)			goto cleanup;		if (!here->e_value_block && here->e_value_size) {			size_t size = le32_to_cpu(here->e_value_size);			if (le16_to_cpu(here->e_value_offs) + size > 			    sb->s_blocksize || size > sb->s_blocksize)				goto bad_block;			free += EXT2_XATTR_SIZE(size);		}		free += EXT2_XATTR_LEN(name_len);	}	error = -ENOSPC;	if (free < EXT2_XATTR_LEN(name_len) + EXT2_XATTR_SIZE(value_len))		goto cleanup;	/* Here we know that we can set the new attribute. */	if (header) {		struct mb_cache_entry *ce;		/* assert(header == HDR(bh)); */		ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev,					bh->b_blocknr);		lock_buffer(bh);		if (header->h_refcount == cpu_to_le32(1)) {			ea_bdebug(bh, "modifying in-place");			if (ce)				mb_cache_entry_free(ce);			/* keep the buffer locked while modifying it. */		} else {			int offset;			if (ce)				mb_cache_entry_release(ce);			unlock_buffer(bh);			ea_bdebug(bh, "cloning");			header = kmalloc(bh->b_size, GFP_KERNEL);			error = -ENOMEM;			if (header == NULL)				goto cleanup;			memcpy(header, HDR(bh), bh->b_size);			header->h_refcount = cpu_to_le32(1);			offset = (char *)here - bh->b_data;			here = ENTRY((char *)header + offset);			offset = (char *)last - bh->b_data;			last = ENTRY((char *)header + offset);		}	} else {

⌨️ 快捷键说明

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