X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fext2%2Fxattr.c;h=b7b83fcaf05b65572d72a8f1767301f2cbe9d2dc;hb=refs%2Fheads%2Fvserver;hp=ab8308a6d602a7f61f35f3197bdaef835897a68b;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index ab8308a6d..b7b83fcaf 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -6,6 +6,9 @@ * Fix by Harrison Xing . * Extended attributes for symlinks and special files added per * suggestion of Luka Renko . + * xattr consolidation Copyright (c) 2004 James Morris , + * Red Hat Inc. + * */ /* @@ -21,7 +24,7 @@ * * +------------------+ * | header | - * ¦ entry 1 | | + * | entry 1 | | * | entry 2 | | growing downwards * | entry 3 | v * | four null bytes | @@ -62,13 +65,6 @@ #include "xattr.h" #include "acl.h" -/* These symbols may be needed by a module. */ -EXPORT_SYMBOL(ext2_xattr_register); -EXPORT_SYMBOL(ext2_xattr_unregister); -EXPORT_SYMBOL(ext2_xattr_get); -EXPORT_SYMBOL(ext2_xattr_list); -EXPORT_SYMBOL(ext2_xattr_set); - #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) @@ -100,154 +96,44 @@ static int ext2_xattr_set2(struct inode *, struct buffer_head *, 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_cache_remove(struct buffer_head *); static void ext2_xattr_rehash(struct ext2_xattr_header *, struct ext2_xattr_entry *); static struct mb_cache *ext2_xattr_cache; -static struct ext2_xattr_handler *ext2_xattr_handlers[EXT2_XATTR_INDEX_MAX]; -static rwlock_t ext2_handler_lock = RW_LOCK_UNLOCKED; - -int -ext2_xattr_register(int name_index, struct ext2_xattr_handler *handler) -{ - int error = -EINVAL; - - if (name_index > 0 && name_index <= EXT2_XATTR_INDEX_MAX) { - write_lock(&ext2_handler_lock); - if (!ext2_xattr_handlers[name_index-1]) { - ext2_xattr_handlers[name_index-1] = handler; - error = 0; - } - write_unlock(&ext2_handler_lock); - } - return error; -} -void -ext2_xattr_unregister(int name_index, struct ext2_xattr_handler *handler) -{ - if (name_index > 0 || name_index <= EXT2_XATTR_INDEX_MAX) { - write_lock(&ext2_handler_lock); - ext2_xattr_handlers[name_index-1] = NULL; - write_unlock(&ext2_handler_lock); - } -} - -static inline const char * -strcmp_prefix(const char *a, const char *a_prefix) -{ - while (*a_prefix && *a == *a_prefix) { - a++; - a_prefix++; - } - return *a_prefix ? NULL : a; -} +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 +}; -/* - * Decode the extended attribute name, and translate it into - * the name_index and name suffix. - */ -static struct ext2_xattr_handler * -ext2_xattr_resolve_name(const char **name) -{ - struct ext2_xattr_handler *handler = NULL; - int i; - - if (!*name) - return NULL; - read_lock(&ext2_handler_lock); - for (i=0; iprefix); - if (n) { - handler = ext2_xattr_handlers[i]; - *name = n; - break; - } - } - } - read_unlock(&ext2_handler_lock); - return handler; -} +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 ext2_xattr_handler * +static inline struct xattr_handler * ext2_xattr_handler(int name_index) { - struct ext2_xattr_handler *handler = NULL; - if (name_index > 0 && name_index <= EXT2_XATTR_INDEX_MAX) { - read_lock(&ext2_handler_lock); - handler = ext2_xattr_handlers[name_index-1]; - read_unlock(&ext2_handler_lock); - } - return handler; -} - -/* - * Inode operation getxattr() - * - * dentry->d_inode->i_sem: don't care - */ -ssize_t -ext2_getxattr(struct dentry *dentry, const char *name, - void *buffer, size_t size) -{ - struct ext2_xattr_handler *handler; - struct inode *inode = dentry->d_inode; + struct xattr_handler *handler = NULL; - handler = ext2_xattr_resolve_name(&name); - if (!handler) - return -EOPNOTSUPP; - return handler->get(inode, name, buffer, size); -} - -/* - * Inode operation listxattr() - * - * dentry->d_inode->i_sem: don't care - */ -ssize_t -ext2_listxattr(struct dentry *dentry, char *buffer, size_t size) -{ - return ext2_xattr_list(dentry->d_inode, buffer, size); -} - -/* - * Inode operation setxattr() - * - * dentry->d_inode->i_sem: down - */ -int -ext2_setxattr(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags) -{ - struct ext2_xattr_handler *handler; - struct inode *inode = dentry->d_inode; - - if (size == 0) - value = ""; /* empty EA, do not remove */ - handler = ext2_xattr_resolve_name(&name); - if (!handler) - return -EOPNOTSUPP; - return handler->set(inode, name, value, size, flags); -} - -/* - * Inode operation removexattr() - * - * dentry->d_inode->i_sem: down - */ -int -ext2_removexattr(struct dentry *dentry, const char *name) -{ - struct ext2_xattr_handler *handler; - struct inode *inode = dentry->d_inode; - - handler = ext2_xattr_resolve_name(&name); - if (!handler) - return -EOPNOTSUPP; - return handler->set(inode, name, NULL, 0, XATTR_REPLACE); + if (name_index > 0 && name_index < ARRAY_SIZE(ext2_xattr_handler_map)) + handler = ext2_xattr_handler_map[name_index]; + return handler; } /* @@ -363,13 +249,13 @@ cleanup: * Returns a negative error number on failure, or the number of bytes * used / required on success. */ -int +static int ext2_xattr_list(struct inode *inode, char *buffer, size_t buffer_size) { struct buffer_head *bh = NULL; struct ext2_xattr_entry *entry; - size_t size = 0; - char *buf, *end; + char *end; + size_t rest = buffer_size; int error; ea_idebug(inode, "buffer=%p, buffer_size=%ld", @@ -395,44 +281,40 @@ bad_block: ext2_error(inode->i_sb, "ext2_xattr_list", error = -EIO; goto cleanup; } - /* compute the size required for the list of attribute names */ - for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); - entry = EXT2_XATTR_NEXT(entry)) { - struct ext2_xattr_handler *handler; - struct ext2_xattr_entry *next = - EXT2_XATTR_NEXT(entry); + + /* 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; - - handler = ext2_xattr_handler(entry->e_name_index); - if (handler) - size += handler->list(NULL, inode, entry->e_name, - entry->e_name_len); + entry = next; } - if (ext2_xattr_cache_insert(bh)) ea_idebug(inode, "cache insert failed"); - if (!buffer) { - error = size; - goto cleanup; - } else { - error = -ERANGE; - if (size > buffer_size) - goto cleanup; - } /* list the attribute names */ - buf = buffer; for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); entry = EXT2_XATTR_NEXT(entry)) { - struct ext2_xattr_handler *handler; - - handler = ext2_xattr_handler(entry->e_name_index); - if (handler) - buf += handler->list(buf, inode, entry->e_name, - entry->e_name_len); + 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 = size; + error = buffer_size - rest; /* total size */ cleanup: brelse(bh); @@ -441,6 +323,17 @@ cleanup: return error; } +/* + * Inode operation listxattr() + * + * dentry->d_inode->i_mutex: don't care + */ +ssize_t +ext2_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. @@ -450,12 +343,9 @@ static void ext2_xattr_update_super_block(struct super_block *sb) if (EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR)) return; - lock_super(sb); - EXT2_SB(sb)->s_es->s_feature_compat |= - cpu_to_le32(EXT2_FEATURE_COMPAT_EXT_ATTR); + EXT2_SET_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR); sb->s_dirt = 1; mark_buffer_dirty(EXT2_SB(sb)->s_sbh); - unlock_super(sb); } /* @@ -497,10 +387,6 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name, ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld", name_index, name, value, (long)value_len); - if (IS_RDONLY(inode)) - return -EROFS; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - return -EPERM; if (value == NULL) value_len = 0; if (name == NULL) @@ -601,15 +487,22 @@ bad_block: ext2_error(sb, "ext2_xattr_set", /* 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"); - ext2_xattr_cache_remove(bh); + 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); @@ -626,11 +519,10 @@ bad_block: ext2_error(sb, "ext2_xattr_set", } } else { /* Allocate a buffer where we construct the new block. */ - header = kmalloc(sb->s_blocksize, GFP_KERNEL); + header = kzalloc(sb->s_blocksize, GFP_KERNEL); error = -ENOMEM; if (header == NULL) goto cleanup; - memset(header, 0, sb->s_blocksize); end = (char *)header + sb->s_blocksize; header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC); header->h_blocks = header->h_refcount = cpu_to_le32(1); @@ -751,11 +643,11 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, ea_bdebug(new_bh, "reusing block"); error = -ENOSPC; - if (DLIMIT_ALLOC_BLOCK(sb, inode->i_xid, 1)) + if (DLIMIT_ALLOC_BLOCK(inode, 1)) goto cleanup; error = -EDQUOT; if (DQUOT_ALLOC_BLOCK(inode, 1)) { - DLIMIT_FREE_BLOCK(sb, inode->i_xid, 1); + DLIMIT_FREE_BLOCK(inode, 1); unlock_buffer(new_bh); goto cleanup; } @@ -808,23 +700,35 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, /* Update the inode. */ EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; - inode->i_ctime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME_SEC; if (IS_SYNC(inode)) { error = ext2_sync_inode (inode); - if (error) + /* In case sync failed due to ENOSPC the inode was actually + * written (only some dirty data were not) so we just proceed + * as if nothing happened and cleanup the unused block */ + if (error && error != -ENOSPC) { + if (new_bh && new_bh != old_bh) + DQUOT_FREE_BLOCK(inode, 1); goto cleanup; + } } else mark_inode_dirty(inode); error = 0; if (old_bh && old_bh != new_bh) { + struct mb_cache_entry *ce; + /* * If there was an old block and we are no longer using it, * release the old block. */ + ce = mb_cache_entry_get(ext2_xattr_cache, old_bh->b_bdev, + old_bh->b_blocknr); lock_buffer(old_bh); if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) { /* Free the old block. */ + if (ce) + mb_cache_entry_free(ce); ea_bdebug(old_bh, "freeing"); ext2_free_blocks(inode, old_bh->b_blocknr, 1); /* We let our caller release old_bh, so we @@ -835,7 +739,9 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, /* Decrement the refcount only. */ HDR(old_bh)->h_refcount = cpu_to_le32( le32_to_cpu(HDR(old_bh)->h_refcount) - 1); - DLIMIT_FREE_BLOCK(sb, inode->i_xid, 1); + if (ce) + mb_cache_entry_release(ce); + DLIMIT_FREE_BLOCK(inode, 1); DQUOT_FREE_BLOCK(inode, 1); mark_buffer_dirty(old_bh); ea_bdebug(old_bh, "refcount now=%d", @@ -860,6 +766,7 @@ void ext2_xattr_delete_inode(struct inode *inode) { struct buffer_head *bh = NULL; + struct mb_cache_entry *ce; down_write(&EXT2_I(inode)->xattr_sem); if (!EXT2_I(inode)->i_file_acl) @@ -879,23 +786,29 @@ ext2_xattr_delete_inode(struct inode *inode) EXT2_I(inode)->i_file_acl); goto cleanup; } + ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev, bh->b_blocknr); lock_buffer(bh); if (HDR(bh)->h_refcount == cpu_to_le32(1)) { - ext2_xattr_cache_remove(bh); + if (ce) + mb_cache_entry_free(ce); ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); get_bh(bh); bforget(bh); + unlock_buffer(bh); } else { HDR(bh)->h_refcount = cpu_to_le32( le32_to_cpu(HDR(bh)->h_refcount) - 1); + if (ce) + mb_cache_entry_release(ce); + ea_bdebug(bh, "refcount now=%d", + le32_to_cpu(HDR(bh)->h_refcount)); + unlock_buffer(bh); mark_buffer_dirty(bh); if (IS_SYNC(inode)) sync_dirty_buffer(bh); - DLIMIT_FREE_BLOCK(inode->i_sb, inode->i_xid, 1); + DLIMIT_FREE_BLOCK(inode, 1); DQUOT_FREE_BLOCK(inode, 1); } - ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1); - unlock_buffer(bh); EXT2_I(inode)->i_file_acl = 0; cleanup: @@ -911,7 +824,7 @@ cleanup: void ext2_xattr_put_super(struct super_block *sb) { - mb_cache_shrink(ext2_xattr_cache, sb->s_bdev); + mb_cache_shrink(sb->s_bdev); } @@ -969,6 +882,7 @@ ext2_xattr_cmp(struct ext2_xattr_header *header1, 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)) @@ -1005,11 +919,19 @@ ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header) 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(ext2_xattr_cache, 0, inode->i_sb->s_bdev, hash); while (ce) { - struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block); + 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) { ext2_error(inode->i_sb, "ext2_xattr_cache_find", "inode %ld: block %ld read error", @@ -1036,26 +958,6 @@ ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header) return NULL; } -/* - * ext2_xattr_cache_remove() - * - * Remove the cache entry of a block from the cache. Called when a - * block becomes invalid. - */ -static void -ext2_xattr_cache_remove(struct buffer_head *bh) -{ - struct mb_cache_entry *ce; - - ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev, bh->b_blocknr); - if (ce) { - ea_bdebug(bh, "removing (%d cache entries remaining)", - atomic_read(&ext2_xattr_cache->c_entry_count)-1); - mb_cache_entry_free(ce); - } else - ea_bdebug(bh, "no cache entry"); -} - #define NAME_HASH_SHIFT 5 #define VALUE_HASH_SHIFT 16 @@ -1078,7 +980,7 @@ static inline void ext2_xattr_hash_entry(struct ext2_xattr_header *header, } if (entry->e_value_block == 0 && entry->e_value_size != 0) { - __u32 *value = (__u32 *)((char *)header + + __le32 *value = (__le32 *)((char *)header + le16_to_cpu(entry->e_value_offs)); for (n = (le32_to_cpu(entry->e_value_size) + EXT2_XATTR_ROUND) >> EXT2_XATTR_PAD_BITS; n; n--) { @@ -1127,66 +1029,16 @@ static void ext2_xattr_rehash(struct ext2_xattr_header *header, int __init init_ext2_xattr(void) { - int err; - - err = ext2_xattr_register(EXT2_XATTR_INDEX_USER, - &ext2_xattr_user_handler); - if (err) - return err; - err = ext2_xattr_register(EXT2_XATTR_INDEX_TRUSTED, - &ext2_xattr_trusted_handler); - if (err) - goto out; -#ifdef CONFIG_EXT2_FS_SECURITY - err = ext2_xattr_register(EXT2_XATTR_INDEX_SECURITY, - &ext2_xattr_security_handler); - if (err) - goto out1; -#endif -#ifdef CONFIG_EXT2_FS_POSIX_ACL - err = init_ext2_acl(); - if (err) - goto out2; -#endif ext2_xattr_cache = mb_cache_create("ext2_xattr", NULL, sizeof(struct mb_cache_entry) + - sizeof(struct mb_cache_entry_index), 1, 6); - if (!ext2_xattr_cache) { - err = -ENOMEM; - goto out3; - } + sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 6); + if (!ext2_xattr_cache) + return -ENOMEM; return 0; -out3: -#ifdef CONFIG_EXT2_FS_POSIX_ACL - exit_ext2_acl(); -out2: -#endif -#ifdef CONFIG_EXT2_FS_SECURITY - ext2_xattr_unregister(EXT2_XATTR_INDEX_SECURITY, - &ext2_xattr_security_handler); -out1: -#endif - ext2_xattr_unregister(EXT2_XATTR_INDEX_TRUSTED, - &ext2_xattr_trusted_handler); -out: - ext2_xattr_unregister(EXT2_XATTR_INDEX_USER, - &ext2_xattr_user_handler); - return err; } void exit_ext2_xattr(void) { mb_cache_destroy(ext2_xattr_cache); -#ifdef CONFIG_EXT2_FS_POSIX_ACL - exit_ext2_acl(); -#endif -#ifdef CONFIG_EXT2_FS_SECURITY - ext2_xattr_unregister(EXT2_XATTR_INDEX_SECURITY, - &ext2_xattr_security_handler); -#endif - ext2_xattr_unregister(EXT2_XATTR_INDEX_TRUSTED, - &ext2_xattr_trusted_handler); - ext2_xattr_unregister(EXT2_XATTR_INDEX_USER, - &ext2_xattr_user_handler); }