From 6f83a30d3c908cf45481c225c0588d274254bd63 Mon Sep 17 00:00:00 2001 From: gord-fig Date: Wed, 25 Sep 2002 17:52:29 +0000 Subject: [PATCH] Make patch generation more accurate with new files. --- lustre/extN/Makefile.am | 39 +- lustre/extN/extN.patch-2.4.18-40um | 1446 +++++++++++++++++++++++++++++++++++- 2 files changed, 1466 insertions(+), 19 deletions(-) diff --git a/lustre/extN/Makefile.am b/lustre/extN/Makefile.am index 2fba3fb..ce63db4 100644 --- a/lustre/extN/Makefile.am +++ b/lustre/extN/Makefile.am @@ -19,14 +19,17 @@ EXTNP+= ext3-2.4.18-ino_sb_macro.diff extN-misc-fixup.diff EXTNC = balloc.c bitmap.c dir.c file.c fsync.c ialloc.c inode.c ioctl.c EXTNC+= namei.c super.c symlink.c EXTNI = extN_fs.h extN_fs_i.h extN_fs_sb.h extN_jbd.h -EXTN_EXTRA = include/linux/xattr.h include/linux/extN_xattr.h fs/extN/xattr.c fs/extN/hash.c include/linux/extN_fs.h +EXTN_EXTRA = include/linux/xattr.h include/linux/extN_xattr.h fs/extN/xattr.c extN_SOURCES = $(EXTNC) xattr.c # punch.c extN_DEPENDENCIES = patch-stamp EXTRA_DIST = $(EXTNP) extN-2.4.18-ino_sb_fixup.diff extN-2.4.18-exports.diff \ $(wildcard extN.patch-*) -DISTCLEANFILES = $(extN_SOURCES) sed-stamp patch-stamp *.orig *.rej +DISTCLEANFILES = -r $(extN_SOURCES) sed-stamp patch-stamp *.orig *.rej SUB=-e "s/ext3/extN/g" -e "s/EXT3/EXTN/g" +distclean: + cd .. && rm -f $(EXTN_EXTRA) + include $(top_srcdir)/Rules # Create a fresh extN patch. @@ -39,10 +42,28 @@ include $(top_srcdir)/Rules # set works for nearly everybody. This is mainly for damage control. diff: $(RM) extN.patchT - -for f in $(EXTNC); do (cd $(top_srcdir) && diff -u extN/extN.orig/$$f extN/$$f) >> extN.patchT; done - -for f in $(EXTNI); do (cd $(top_srcdir) && diff -u extN/extN.orig-include/$$f include/linux/$$f) >> extN.patchT; done + l='$(EXTNC)'; for f in $$l; do \ + echo "$$f"; \ + (cd $(top_srcdir) && \ + diff -u extN/extN.orig/$$f extN/$$f) >> extN.patchT; \ + test $$? -le 1 || exit 1; \ + done + l='$(EXTNI)'; for f in $$l; do \ + echo "$$f"; \ + (cd $(top_srcdir) && \ + diff -u extN/extN-include.orig/$$f include/linux/$$f) >> extN.patchT; \ + test $$? -le 1 || exit 1; \ + done + l='$(EXTN_EXTRA)'; for f in $$l; do \ + f=`echo "$$f" | sed 's%^fs/%%'`; \ + echo "$$f"; \ + (cd $(top_srcdir) && \ + diff -u /dev/null $$f) >> extN.patchT; \ + test $$? -le 1 || exit 1; \ + done mv -f extN.patchT $(srcdir)/extN.patch-$(RELEASE) - f=extN.patch-$(RELEASE); if cvs update $$f 2>&1 | grep 'cvs add' >/dev/null; then \ + f=extN.patch-$(RELEASE); \ + if cvs update $$f 2>&1 | grep 'cvs add' >/dev/null; then \ cvs add $$f; \ fi .PHONY: diff @@ -50,8 +71,8 @@ diff: # Just do the SUB transformation on all our source files. sed-stamp: $(RM) $@ - rm -rf extN.orig extN.orig-include - mkdir extN.orig extN.orig-include + rm -rf extN.orig extN-include.orig + mkdir extN.orig extN-include.orig set -vx; \ list='$(EXTNC)'; for f in $$list; do \ sed $(SUB) $(LINUX)/fs/ext3/$$f > extN.orig/$$f; \ @@ -60,7 +81,7 @@ sed-stamp: list='$(EXTNI)'; for i in $$list; do \ s=`echo $$i | sed "s/extN/ext3/"`; \ sed $(SUB) $(LINUX)/include/linux/$$s > \ - extN.orig-include/$$i; \ + extN-include.orig/$$i; \ done echo timestamp > $@ @@ -72,7 +93,7 @@ sed-stamp: # We also want to preserve the pristine transformed files for the diff target. patch-stamp: sed-stamp $(EXTNP) cp -a extN.orig/* $(srcdir) - cp -a extN.orig-include/* $(top_srcdir)/include/linux + cp -a extN-include.orig/* $(top_srcdir)/include/linux test -e $(top_srcdir)/fs || ln -s . $(top_srcdir)/fs set -vx; \ if [ -f $(srcdir)/extN.patch-$(RELEASE) ]; then \ diff --git a/lustre/extN/extN.patch-2.4.18-40um b/lustre/extN/extN.patch-2.4.18-40um index 7542b8c..03f0f08f 100644 --- a/lustre/extN/extN.patch-2.4.18-40um +++ b/lustre/extN/extN.patch-2.4.18-40um @@ -1,5 +1,5 @@ --- extN/extN.orig/balloc.c Tue Sep 24 15:41:40 2002 -+++ extN/balloc.c Tue Sep 24 22:00:43 2002 ++++ extN/balloc.c Tue Sep 24 22:07:38 2002 @@ -46,18 +46,18 @@ unsigned long desc; struct extN_group_desc * gdp; @@ -331,7 +331,7 @@ sb, bh->b_data)) extN_error (sb, "extN_check_blocks_bitmap", --- extN/extN.orig/dir.c Tue Sep 24 15:41:40 2002 -+++ extN/dir.c Tue Sep 24 22:00:43 2002 ++++ extN/dir.c Tue Sep 24 22:07:38 2002 @@ -52,7 +52,7 @@ else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize) error_msg = "directory entry across blocks"; @@ -342,7 +342,7 @@ if (error_msg != NULL) --- extN/extN.orig/ialloc.c Tue Sep 24 15:41:40 2002 -+++ extN/ialloc.c Tue Sep 24 22:00:43 2002 ++++ extN/ialloc.c Tue Sep 24 22:07:38 2002 @@ -17,6 +17,7 @@ #include #include @@ -646,7 +646,7 @@ if (le16_to_cpu(gdp->bg_free_inodes_count) != x) extN_error (sb, "extN_check_inodes_bitmap", --- extN/extN.orig/inode.c Tue Sep 24 15:41:40 2002 -+++ extN/inode.c Tue Sep 24 22:00:43 2002 ++++ extN/inode.c Tue Sep 24 22:07:38 2002 @@ -39,6 +39,18 @@ */ #undef SEARCH_FROM_ZERO @@ -1247,7 +1247,7 @@ journal_unlock_updates(journal); --- extN/extN.orig/ioctl.c Tue Sep 24 15:41:40 2002 -+++ extN/ioctl.c Tue Sep 24 22:00:43 2002 ++++ extN/ioctl.c Tue Sep 24 22:07:38 2002 @@ -18,13 +18,14 @@ int extN_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) @@ -1299,7 +1299,7 @@ } #endif --- extN/extN.orig/namei.c Tue Sep 24 15:41:40 2002 -+++ extN/namei.c Tue Sep 24 22:00:43 2002 ++++ extN/namei.c Tue Sep 24 22:07:38 2002 @@ -16,6 +16,10 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 * Directory entry file type support and forward compatibility hooks @@ -2601,7 +2601,7 @@ } } --- extN/extN.orig/super.c Tue Sep 24 15:41:40 2002 -+++ extN/super.c Tue Sep 24 22:05:23 2002 ++++ extN/super.c Tue Sep 24 22:07:38 2002 @@ -24,6 +24,7 @@ #include #include @@ -2755,7 +2755,7 @@ EXPORT_SYMBOL(extN_bread); --- extN/extN.orig/symlink.c Tue Sep 24 15:41:40 2002 -+++ extN/symlink.c Tue Sep 24 22:00:43 2002 ++++ extN/symlink.c Tue Sep 24 22:07:38 2002 @@ -23,14 +23,14 @@ static int extN_readlink(struct dentry *dentry, char *buffer, int buflen) @@ -2776,7 +2776,7 @@ struct inode_operations extN_fast_symlink_inode_operations = { --- extN/extN.orig-include/extN_fs.h Tue Sep 24 15:41:40 2002 -+++ include/linux/extN_fs.h Tue Sep 24 22:01:11 2002 ++++ include/linux/extN_fs.h Tue Sep 24 22:07:38 2002 @@ -58,8 +58,6 @@ */ #define EXTN_BAD_INO 1 /* Bad blocks inode */ @@ -3000,7 +3000,7 @@ extern struct buffer_head * extN_bread (handle_t *, struct inode *, int, int, int *); --- extN/extN.orig-include/extN_jbd.h Tue Sep 24 15:41:41 2002 -+++ include/linux/extN_jbd.h Tue Sep 24 22:01:11 2002 ++++ include/linux/extN_jbd.h Tue Sep 24 22:07:38 2002 @@ -30,13 +30,19 @@ #define EXTN_SINGLEDATA_TRANS_BLOCKS 8 @@ -3040,3 +3040,1429 @@ return 1; return 0; } +--- /dev/null Tue Aug 6 08:46:29 2002 ++++ include/linux/xattr.h Tue Sep 24 21:43:47 2002 +@@ -0,0 +1,15 @@ ++/* ++ File: linux/xattr.h ++ ++ Extended attributes handling. ++ ++ Copyright (C) 2001 by Andreas Gruenbacher ++ Copyright (C) 2001 SGI - Silicon Graphics, Inc ++*/ ++#ifndef _LINUX_XATTR_H ++#define _LINUX_XATTR_H ++ ++#define XATTR_CREATE 1 /* set value, fail if attr already exists */ ++#define XATTR_REPLACE 2 /* set value, fail if attr does not exist */ ++ ++#endif /* _LINUX_XATTR_H */ +--- /dev/null Tue Aug 6 08:46:29 2002 ++++ include/linux/extN_xattr.h Tue Sep 24 21:43:47 2002 +@@ -0,0 +1,155 @@ ++/* ++ File: linux/extN_xattr.h ++ ++ On-disk format of extended attributes for the extN filesystem. ++ ++ (C) 2001 Andreas Gruenbacher, ++*/ ++ ++#include ++#include ++#include ++ ++/* Magic value in attribute blocks */ ++#define EXTN_XATTR_MAGIC 0xEA020000 ++ ++/* Maximum number of references to one attribute block */ ++#define EXTN_XATTR_REFCOUNT_MAX 1024 ++ ++/* Name indexes */ ++#define EXTN_XATTR_INDEX_MAX 10 ++#define EXTN_XATTR_INDEX_USER 1 ++ ++struct extN_xattr_header { ++ __u32 h_magic; /* magic number for identification */ ++ __u32 h_refcount; /* reference count */ ++ __u32 h_blocks; /* number of disk blocks used */ ++ __u32 h_hash; /* hash value of all attributes */ ++ __u32 h_reserved[4]; /* zero right now */ ++}; ++ ++struct extN_xattr_entry { ++ __u8 e_name_len; /* length of name */ ++ __u8 e_name_index; /* attribute name index */ ++ __u16 e_value_offs; /* offset in disk block of value */ ++ __u32 e_value_block; /* disk block attribute is stored on (n/i) */ ++ __u32 e_value_size; /* size of attribute value */ ++ __u32 e_hash; /* hash value of name and value */ ++ char e_name[0]; /* attribute name */ ++}; ++ ++#define EXTN_XATTR_PAD_BITS 2 ++#define EXTN_XATTR_PAD (1<e_name_len)) ) ++#define EXTN_XATTR_SIZE(size) \ ++ (((size) + EXTN_XATTR_ROUND) & ~EXTN_XATTR_ROUND) ++ ++#ifdef __KERNEL__ ++ ++# ifdef CONFIG_EXTN_FS_XATTR ++ ++struct extN_xattr_handler { ++ char *prefix; ++ size_t (*list)(char *list, struct inode *inode, const char *name, ++ int name_len); ++ int (*get)(struct inode *inode, const char *name, void *buffer, ++ size_t size); ++ int (*set)(struct inode *inode, const char *name, void *buffer, ++ size_t size, int flags); ++}; ++ ++extern int extN_xattr_register(int, struct extN_xattr_handler *); ++extern void extN_xattr_unregister(int, struct extN_xattr_handler *); ++ ++extern int extN_setxattr(struct dentry *, const char *, void *, size_t, int); ++extern ssize_t extN_getxattr(struct dentry *, const char *, void *, size_t); ++extern ssize_t extN_listxattr(struct dentry *, char *, size_t); ++extern int extN_removexattr(struct dentry *, const char *); ++ ++extern int extN_xattr_get(struct inode *, int, const char *, void *, size_t); ++extern int extN_xattr_list(struct inode *, char *, size_t); ++extern int extN_xattr_set(handle_t *handle, struct inode *, int, const char *, void *, size_t, int); ++ ++extern void extN_xattr_drop_inode(handle_t *, struct inode *); ++extern void extN_xattr_put_super(struct super_block *); ++ ++extern int init_extN_xattr(void) __init; ++extern void exit_extN_xattr(void); ++ ++# else /* CONFIG_EXTN_FS_XATTR */ ++# define extN_setxattr NULL ++# define extN_getxattr NULL ++# define extN_listxattr NULL ++# define extN_removexattr NULL ++ ++static inline int ++extN_xattr_get(struct inode *inode, int name_index, const char *name, ++ void *buffer, size_t size, int flags) ++{ ++ return -ENOTSUP; ++} ++ ++static inline int ++extN_xattr_list(struct inode *inode, void *buffer, size_t size, int flags) ++{ ++ return -ENOTSUP; ++} ++ ++static inline int ++extN_xattr_set(handle_t *handle, struct inode *inode, int name_index, ++ const char *name, void *value, size_t size, int flags) ++{ ++ return -ENOTSUP; ++} ++ ++static inline void ++extN_xattr_drop_inode(handle_t *handle, struct inode *inode) ++{ ++} ++ ++static inline void ++extN_xattr_put_super(struct super_block *sb) ++{ ++} ++ ++static inline int ++init_extN_xattr(void) ++{ ++ return 0; ++} ++ ++static inline void ++exit_extN_xattr(void) ++{ ++} ++ ++# endif /* CONFIG_EXTN_FS_XATTR */ ++ ++# ifdef CONFIG_EXTN_FS_XATTR_USER ++ ++extern int init_extN_xattr_user(void) __init; ++extern void exit_extN_xattr_user(void); ++ ++# else /* CONFIG_EXTN_FS_XATTR_USER */ ++ ++static inline int ++init_extN_xattr_user(void) ++{ ++ return 0; ++} ++ ++static inline void ++exit_extN_xattr_user(void) ++{ ++} ++ ++#endif /* CONFIG_EXTN_FS_XATTR_USER */ ++ ++#endif /* __KERNEL__ */ ++ +--- /dev/null Tue Aug 6 08:46:29 2002 ++++ extN/xattr.c Tue Sep 24 21:43:47 2002 +@@ -0,0 +1,1247 @@ ++/* ++ * linux/fs/extN/xattr.c ++ * ++ * Copyright (C) 2001 by Andreas Gruenbacher, ++ * ++ * Fix by Harrison Xing . ++ * Ext3 code with a lot of help from Eric Jarman . ++ * Extended attributes for symlinks and special files added per ++ * suggestion of Luka Renko . ++ */ ++ ++/* ++ * 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 EXTN_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 EXTN_XATTR_PAD byte ++ * boundaries. No additional gaps are left between them. ++ * ++ * Locking strategy ++ * ---------------- ++ * The VFS already holds the BKL and the inode->i_sem semaphore when any of ++ * the xattr inode operations are called, so we are guaranteed that only one ++ * processes accesses extended attributes of an inode at any time. ++ * ++ * For writing we also grab the extN_xattr_sem semaphore. This ensures that ++ * only a single process is modifying an extended attribute block, even ++ * if the block is shared among inodes. ++ * ++ * Note for porting to 2.5 ++ * ----------------------- ++ * The BKL will no longer be held in the xattr inode operations. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_EXTN_FS_XATTR_SHARING ++#include ++#endif ++#include ++#include ++#include ++#include ++ ++/* These symbols may be needed by a module. */ ++EXPORT_SYMBOL(extN_xattr_register); ++EXPORT_SYMBOL(extN_xattr_unregister); ++EXPORT_SYMBOL(extN_xattr_get); ++EXPORT_SYMBOL(extN_xattr_list); ++EXPORT_SYMBOL(extN_xattr_set); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) ++# define mark_buffer_dirty(bh) mark_buffer_dirty(bh, 1) ++#endif ++ ++#define HDR(bh) ((struct extN_xattr_header *)((bh)->b_data)) ++#define ENTRY(ptr) ((struct extN_xattr_entry *)(ptr)) ++#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1) ++#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) ++ ++#ifdef EXTN_XATTR_DEBUG ++# define ea_idebug(inode, f...) do { \ ++ printk(KERN_DEBUG "inode %s:%ld: ", \ ++ kdevname(inode->i_dev), inode->i_ino); \ ++ printk(f); \ ++ printk("\n"); \ ++ } while (0) ++# define ea_bdebug(bh, f...) do { \ ++ printk(KERN_DEBUG "block %s:%ld: ", \ ++ kdevname(bh->b_dev), bh->b_blocknr); \ ++ printk(f); \ ++ printk("\n"); \ ++ } while (0) ++#else ++# define ea_idebug(f...) ++# define ea_bdebug(f...) ++#endif ++ ++static int extN_xattr_set2(handle_t *, struct inode *, struct buffer_head *, ++ struct extN_xattr_header *); ++ ++#ifdef CONFIG_EXTN_FS_XATTR_SHARING ++ ++static int extN_xattr_cache_insert(struct buffer_head *); ++static struct buffer_head *extN_xattr_cache_find(struct inode *, ++ struct extN_xattr_header *); ++static void extN_xattr_cache_remove(struct buffer_head *); ++static void extN_xattr_rehash(struct extN_xattr_header *, ++ struct extN_xattr_entry *); ++ ++static struct mb_cache *extN_xattr_cache; ++ ++#else ++# define extN_xattr_cache_insert(bh) 0 ++# define extN_xattr_cache_find(inode, header) NULL ++# define extN_xattr_cache_remove(bh) do {} while(0) ++# define extN_xattr_rehash(header, entry) do {} while(0) ++#endif ++ ++/* ++ * If a file system does not share extended attributes among inodes, ++ * we should not need the extN_xattr_sem semaphore. However, the ++ * filesystem may still contain shared blocks, so we always take ++ * the lock. ++ */ ++ ++DECLARE_MUTEX(extN_xattr_sem); ++ ++static inline void ++extN_xattr_lock(void) ++{ ++ down(&extN_xattr_sem); ++} ++ ++static inline void ++extN_xattr_unlock(void) ++{ ++ up(&extN_xattr_sem); ++} ++ ++static inline int ++extN_xattr_new_block(handle_t *handle, struct inode *inode, ++ int * errp, int force) ++{ ++ struct super_block *sb = inode->i_sb; ++ int goal = le32_to_cpu(EXTN_SB(sb)->s_es->s_first_data_block) + ++ EXTN_I(inode)->i_block_group * EXTN_BLOCKS_PER_GROUP(sb); ++ ++ /* How can we enforce the allocation? */ ++ int block = extN_new_block(handle, inode, goal, 0, 0, errp); ++#ifdef OLD_QUOTAS ++ if (!*errp) ++ inode->i_blocks += inode->i_sb->s_blocksize >> 9; ++#endif ++ return block; ++} ++ ++static inline int ++extN_xattr_quota_alloc(struct inode *inode, int force) ++{ ++ /* How can we enforce the allocation? */ ++#ifdef OLD_QUOTAS ++ int error = DQUOT_ALLOC_BLOCK(inode->i_sb, inode, 1); ++ if (!error) ++ inode->i_blocks += inode->i_sb->s_blocksize >> 9; ++#else ++ int error = DQUOT_ALLOC_BLOCK(inode, 1); ++#endif ++ return error; ++} ++ ++#ifdef OLD_QUOTAS ++ ++static inline void ++extN_xattr_quota_free(struct inode *inode) ++{ ++ DQUOT_FREE_BLOCK(inode->i_sb, inode, 1); ++ inode->i_blocks -= inode->i_sb->s_blocksize >> 9; ++} ++ ++static inline void ++extN_xattr_free_block(handle_t *handle, struct inode * inode, ++ unsigned long block) ++{ ++ extN_free_blocks(handle, inode, block, 1); ++ inode->i_blocks -= inode->i_sb->s_blocksize >> 9; ++} ++ ++#else ++# define extN_xattr_quota_free(inode) \ ++ DQUOT_FREE_BLOCK(inode, 1) ++# define extN_xattr_free_block(handle, inode, block) \ ++ extN_free_blocks(handle, inode, block, 1) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18) ++ ++static inline struct buffer_head * ++sb_bread(struct super_block *sb, int block) ++{ ++ return bread(sb->s_dev, block, sb->s_blocksize); ++} ++ ++static inline struct buffer_head * ++sb_getblk(struct super_block *sb, int block) ++{ ++ return getblk(sb->s_dev, block, sb->s_blocksize); ++} ++ ++#endif ++ ++struct extN_xattr_handler *extN_xattr_handlers[EXTN_XATTR_INDEX_MAX]; ++rwlock_t extN_handler_lock = RW_LOCK_UNLOCKED; ++ ++int ++extN_xattr_register(int name_index, struct extN_xattr_handler *handler) ++{ ++ int error = -EINVAL; ++ ++ if (name_index > 0 && name_index <= EXTN_XATTR_INDEX_MAX) { ++ write_lock(&extN_handler_lock); ++ if (!extN_xattr_handlers[name_index-1]) { ++ extN_xattr_handlers[name_index-1] = handler; ++ error = 0; ++ } ++ write_unlock(&extN_handler_lock); ++ } ++ return error; ++} ++ ++void ++extN_xattr_unregister(int name_index, struct extN_xattr_handler *handler) ++{ ++ if (name_index > 0 || name_index <= EXTN_XATTR_INDEX_MAX) { ++ write_lock(&extN_handler_lock); ++ extN_xattr_handlers[name_index-1] = NULL; ++ write_unlock(&extN_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; ++} ++ ++/* ++ * Decode the extended attribute name, and translate it into ++ * the name_index and name suffix. ++ */ ++static inline struct extN_xattr_handler * ++extN_xattr_resolve_name(const char **name) ++{ ++ struct extN_xattr_handler *handler = NULL; ++ int i; ++ ++ if (!*name) ++ return NULL; ++ read_lock(&extN_handler_lock); ++ for (i=0; iprefix); ++ if (n) { ++ handler = extN_xattr_handlers[i]; ++ *name = n; ++ break; ++ } ++ } ++ } ++ read_unlock(&extN_handler_lock); ++ return handler; ++} ++ ++static inline struct extN_xattr_handler * ++extN_xattr_handler(int name_index) ++{ ++ struct extN_xattr_handler *handler = NULL; ++ if (name_index > 0 && name_index <= EXTN_XATTR_INDEX_MAX) { ++ read_lock(&extN_handler_lock); ++ handler = extN_xattr_handlers[name_index-1]; ++ read_unlock(&extN_handler_lock); ++ } ++ return handler; ++} ++ ++/* ++ * Inode operation getxattr() ++ * ++ * dentry->d_inode->i_sem down ++ * BKL held [before 2.5.x] ++ */ ++ssize_t ++extN_getxattr(struct dentry *dentry, const char *name, ++ void *buffer, size_t size) ++{ ++ struct extN_xattr_handler *handler; ++ struct inode *inode = dentry->d_inode; ++ ++ handler = extN_xattr_resolve_name(&name); ++ if (!handler) ++ return -ENOTSUP; ++ return handler->get(inode, name, buffer, size); ++} ++ ++/* ++ * Inode operation listxattr() ++ * ++ * dentry->d_inode->i_sem down ++ * BKL held [before 2.5.x] ++ */ ++ssize_t ++extN_listxattr(struct dentry *dentry, char *buffer, size_t size) ++{ ++ return extN_xattr_list(dentry->d_inode, buffer, size); ++} ++ ++/* ++ * Inode operation setxattr() ++ * ++ * dentry->d_inode->i_sem down ++ * BKL held [before 2.5.x] ++ */ ++int ++extN_setxattr(struct dentry *dentry, const char *name, ++ void *value, size_t size, int flags) ++{ ++ struct extN_xattr_handler *handler; ++ struct inode *inode = dentry->d_inode; ++ ++ if (size == 0) ++ value = ""; /* empty EA, do not remove */ ++ handler = extN_xattr_resolve_name(&name); ++ if (!handler) ++ return -ENOTSUP; ++ return handler->set(inode, name, value, size, flags); ++} ++ ++/* ++ * Inode operation removexattr() ++ * ++ * dentry->d_inode->i_sem down ++ * BKL held [before 2.5.x] ++ */ ++int ++extN_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct extN_xattr_handler *handler; ++ struct inode *inode = dentry->d_inode; ++ ++ handler = extN_xattr_resolve_name(&name); ++ if (!handler) ++ return -ENOTSUP; ++ return handler->set(inode, name, NULL, 0, XATTR_REPLACE); ++} ++ ++/* ++ * extN_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. ++ */ ++int ++extN_xattr_get(struct inode *inode, int name_index, const char *name, ++ void *buffer, size_t buffer_size) ++{ ++ struct buffer_head *bh = NULL; ++ struct extN_xattr_entry *entry; ++ unsigned int block, size; ++ char *end; ++ int name_len, error; ++ ++ ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", ++ name_index, name, buffer, (long)buffer_size); ++ ++ if (name == NULL) ++ return -EINVAL; ++ if (!EXTN_I(inode)->i_file_acl) ++ return -ENOATTR; ++ block = EXTN_I(inode)->i_file_acl; ++ ea_idebug(inode, "reading block %d", block); ++ bh = sb_bread(inode->i_sb, block); ++ if (!bh) ++ return -EIO; ++ 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(EXTN_XATTR_MAGIC) || ++ HDR(bh)->h_blocks != cpu_to_le32(1)) { ++bad_block: extN_error(inode->i_sb, "extN_xattr_get", ++ "inode %ld: bad block %d", inode->i_ino, block); ++ 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 extN_xattr_entry *next = ++ EXTN_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 extN_xattr_entry *next = ++ EXTN_XATTR_NEXT(entry); ++ if ((char *)next >= end) ++ goto bad_block; ++ entry = next; ++ } ++ if (extN_xattr_cache_insert(bh)) ++ ea_idebug(inode, "cache insert failed"); ++ error = -ENOATTR; ++ 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 (extN_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); ++ ++ return error; ++} ++ ++/* ++ * extN_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. ++ */ ++int ++extN_xattr_list(struct inode *inode, char *buffer, size_t buffer_size) ++{ ++ struct buffer_head *bh = NULL; ++ struct extN_xattr_entry *entry; ++ unsigned int block, size = 0; ++ char *buf, *end; ++ int error; ++ ++ ea_idebug(inode, "buffer=%p, buffer_size=%ld", ++ buffer, (long)buffer_size); ++ ++ if (!EXTN_I(inode)->i_file_acl) ++ return 0; ++ block = EXTN_I(inode)->i_file_acl; ++ ea_idebug(inode, "reading block %d", block); ++ bh = sb_bread(inode->i_sb, block); ++ if (!bh) ++ return -EIO; ++ 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(EXTN_XATTR_MAGIC) || ++ HDR(bh)->h_blocks != cpu_to_le32(1)) { ++bad_block: extN_error(inode->i_sb, "extN_xattr_list", ++ "inode %ld: bad block %d", inode->i_ino, block); ++ error = -EIO; ++ goto cleanup; ++ } ++ /* compute the size required for the list of attribute names */ ++ for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); ++ entry = EXTN_XATTR_NEXT(entry)) { ++ struct extN_xattr_handler *handler; ++ struct extN_xattr_entry *next = ++ EXTN_XATTR_NEXT(entry); ++ if ((char *)next >= end) ++ goto bad_block; ++ ++ handler = extN_xattr_handler(entry->e_name_index); ++ if (handler) { ++ size += handler->list(NULL, inode, entry->e_name, ++ entry->e_name_len) + 1; ++ } ++ } ++ ++ if (extN_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 = EXTN_XATTR_NEXT(entry)) { ++ struct extN_xattr_handler *handler; ++ ++ handler = extN_xattr_handler(entry->e_name_index); ++ if (handler) { ++ buf += handler->list(buf, inode, entry->e_name, ++ entry->e_name_len); ++ *buf++ = '\0'; ++ } ++ } ++ error = size; ++ ++cleanup: ++ brelse(bh); ++ ++ return error; ++} ++ ++/* ++ * If the EXTN_FEATURE_COMPAT_EXT_ATTR feature of this file system is ++ * not set, set it. ++ */ ++static void extN_xattr_update_super_block(handle_t *handle, ++ struct super_block *sb) ++{ ++ if (EXTN_HAS_COMPAT_FEATURE(sb, EXTN_FEATURE_COMPAT_EXT_ATTR)) ++ return; ++ ++ lock_super(sb); ++ extN_journal_get_write_access(handle, EXTN_SB(sb)->s_sbh); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) ++ EXTN_SB(sb)->s_feature_compat |= EXTN_FEATURE_COMPAT_EXT_ATTR; ++#endif ++ EXTN_SB(sb)->s_es->s_feature_compat |= ++ cpu_to_le32(EXTN_FEATURE_COMPAT_EXT_ATTR); ++ sb->s_dirt = 1; ++ extN_journal_dirty_metadata(handle, EXTN_SB(sb)->s_sbh); ++ unlock_super(sb); ++} ++ ++/* ++ * extN_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. ++ */ ++int ++extN_xattr_set(handle_t *handle, struct inode *inode, int name_index, ++ const char *name, void *value, size_t value_len, int flags) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct buffer_head *bh = NULL; ++ struct extN_xattr_header *header = NULL; ++ struct extN_xattr_entry *here, *last; ++ unsigned int name_len; ++ int min_offs = sb->s_blocksize, not_found = 1, free, 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 (IS_RDONLY(inode)) ++ return -EROFS; ++ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) ++ return -EPERM; ++ 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; ++ extN_xattr_lock(); ++ ++ if (EXTN_I(inode)->i_file_acl) { ++ /* The inode already has an extended attribute block. */ ++ int block = EXTN_I(inode)->i_file_acl; ++ ++ bh = sb_bread(sb, block); ++ 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(EXTN_XATTR_MAGIC) || ++ header->h_blocks != cpu_to_le32(1)) { ++bad_block: extN_error(sb, "extN_xattr_set", ++ "inode %ld: bad block %d", inode->i_ino, block); ++ error = -EIO; ++ goto cleanup; ++ } ++ /* Find the named attribute. */ ++ here = FIRST_ENTRY(bh); ++ while (!IS_LAST_ENTRY(here)) { ++ struct extN_xattr_entry *next = EXTN_XATTR_NEXT(here); ++ if ((char *)next >= end) ++ goto bad_block; ++ if (!here->e_value_block && here->e_value_size) { ++ int 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 extN_xattr_entry *next = EXTN_XATTR_NEXT(last); ++ if ((char *)next >= end) ++ goto bad_block; ++ if (!last->e_value_block && last->e_value_size) { ++ int 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 extN_xattr_header) - sizeof(__u32); ++ here = last = NULL; /* avoid gcc uninitialized warning. */ ++ } ++ ++ if (not_found) { ++ /* Request to remove a nonexistent attribute? */ ++ error = -ENOATTR; ++ if (flags & XATTR_REPLACE) ++ goto cleanup; ++ error = 0; ++ if (value == NULL) ++ goto cleanup; ++ else ++ free -= EXTN_XATTR_LEN(name_len); ++ } else { ++ /* Request to create an existing attribute? */ ++ error = -EEXIST; ++ if (flags & XATTR_CREATE) ++ goto cleanup; ++ if (!here->e_value_block && here->e_value_size) { ++ unsigned int 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 += EXTN_XATTR_SIZE(size); ++ } ++ } ++ free -= EXTN_XATTR_SIZE(value_len); ++ error = -ENOSPC; ++ if (free < 0) ++ goto cleanup; ++ ++ /* Here we know that we can set the new attribute. */ ++ ++ if (header) { ++ if (header->h_refcount == cpu_to_le32(1)) { ++ ea_bdebug(bh, "modifying in-place"); ++ extN_xattr_cache_remove(bh); ++ error = extN_journal_get_write_access(handle, bh); ++ if (error) ++ goto cleanup; ++ } else { ++ int offset; ++ ++ 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 *)header - bh->b_data; ++ here = ENTRY((char *)here + offset); ++ last = ENTRY((char *)last + offset); ++ } ++ } else { ++ /* Allocate a buffer where we construct the new block. */ ++ header = kmalloc(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(EXTN_XATTR_MAGIC); ++ header->h_blocks = header->h_refcount = cpu_to_le32(1); ++ last = here = ENTRY(header+1); ++ } ++ ++ if (not_found) { ++ /* Insert the new name. */ ++ int size = EXTN_XATTR_LEN(name_len); ++ int rest = (char *)last - (char *)here; ++ memmove((char *)here + size, here, rest); ++ memset(here, 0, size); ++ here->e_name_index = name_index; ++ here->e_name_len = name_len; ++ memcpy(here->e_name, name, name_len); ++ } else { ++ /* Remove the old value. */ ++ if (!here->e_value_block && here->e_value_size) { ++ char *first_val = (char *)header + min_offs; ++ int offs = le16_to_cpu(here->e_value_offs); ++ char *val = (char *)header + offs; ++ size_t size = EXTN_XATTR_SIZE( ++ le32_to_cpu(here->e_value_size)); ++ memmove(first_val + size, first_val, val - first_val); ++ memset(first_val, 0, size); ++ here->e_value_offs = 0; ++ min_offs += size; ++ ++ /* Adjust all value offsets. */ ++ last = ENTRY(header+1); ++ while (!IS_LAST_ENTRY(last)) { ++ int o = le16_to_cpu(last->e_value_offs); ++ if (!last->e_value_block && o < offs) ++ last->e_value_offs = ++ cpu_to_le16(o + size); ++ last = EXTN_XATTR_NEXT(last); ++ } ++ } ++ if (value == NULL) { ++ /* Remove this attribute. */ ++ if (EXTN_XATTR_NEXT(ENTRY(header+1)) == last) { ++ /* This block is now empty. */ ++ error = extN_xattr_set2(handle, inode, bh,NULL); ++ goto cleanup; ++ } else { ++ /* Remove the old name. */ ++ int size = EXTN_XATTR_LEN(name_len); ++ last = ENTRY((char *)last - size); ++ memmove(here, (char*)here + size, ++ (char*)last - (char*)here); ++ memset(last, 0, size); ++ } ++ } ++ } ++ ++ if (value != NULL) { ++ /* Insert the new value. */ ++ here->e_value_size = cpu_to_le32(value_len); ++ if (value_len) { ++ size_t size = EXTN_XATTR_SIZE(value_len); ++ char *val = (char *)header + min_offs - size; ++ here->e_value_offs = ++ cpu_to_le16((char *)val - (char *)header); ++ memset(val + size - EXTN_XATTR_PAD, 0, ++ EXTN_XATTR_PAD); /* Clear the pad bytes. */ ++ memcpy(val, value, value_len); ++ } ++ } ++ extN_xattr_rehash(header, here); ++ ++ error = extN_xattr_set2(handle, inode, bh, header); ++ ++cleanup: ++ brelse(bh); ++ if (!(bh && header == HDR(bh))) ++ kfree(header); ++ extN_xattr_unlock(); ++ ++ return error; ++} ++ ++/* ++ * Second half of extN_xattr_set(): Update the file system. ++ */ ++static int ++extN_xattr_set2(handle_t *handle, struct inode *inode, ++ struct buffer_head *old_bh, struct extN_xattr_header *header) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct buffer_head *new_bh = NULL; ++ int error; ++ ++ if (header) { ++ new_bh = extN_xattr_cache_find(inode, header); ++ if (new_bh) { ++ /* ++ * We found an identical block in the cache. ++ * The old block will be released after updating ++ * the inode. ++ */ ++ ea_bdebug(old_bh, "reusing block %ld", ++ new_bh->b_blocknr); ++ ++ error = -EDQUOT; ++ if (extN_xattr_quota_alloc(inode, 1)) ++ goto cleanup; ++ ++ error = extN_journal_get_write_access(handle, new_bh); ++ if (error) ++ goto cleanup; ++ HDR(new_bh)->h_refcount = cpu_to_le32( ++ le32_to_cpu(HDR(new_bh)->h_refcount) + 1); ++ ea_bdebug(new_bh, "refcount now=%d", ++ le32_to_cpu(HDR(new_bh)->h_refcount)); ++ } else if (old_bh && header == HDR(old_bh)) { ++ /* Keep this block. */ ++ new_bh = old_bh; ++ (void)extN_xattr_cache_insert(new_bh); ++ } else { ++ /* We need to allocate a new block */ ++ int force = EXTN_I(inode)->i_file_acl != 0; ++ int block = extN_xattr_new_block(handle, inode, ++ &error, force); ++ if (error) ++ goto cleanup; ++ ea_idebug(inode, "creating block %d", block); ++ ++ new_bh = sb_getblk(sb, block); ++ if (!new_bh) { ++getblk_failed: extN_xattr_free_block(handle, inode, block); ++ error = -EIO; ++ goto cleanup; ++ } ++ lock_buffer(new_bh); ++ error = extN_journal_get_create_access(handle, new_bh); ++ if (error) { ++ unlock_buffer(new_bh); ++ goto getblk_failed; ++ } ++ memcpy(new_bh->b_data, header, new_bh->b_size); ++ mark_buffer_uptodate(new_bh, 1); ++ unlock_buffer(new_bh); ++ (void)extN_xattr_cache_insert(new_bh); ++ extN_xattr_update_super_block(handle, sb); ++ } ++ error = extN_journal_dirty_metadata(handle, new_bh); ++ if (error) ++ goto cleanup; ++ } ++ ++ /* Update the inode. */ ++ EXTN_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; ++ inode->i_ctime = CURRENT_TIME; ++ extN_mark_inode_dirty(handle, inode); ++ if (IS_SYNC(inode)) ++ handle->h_sync = 1; ++ ++ error = 0; ++ if (old_bh && old_bh != new_bh) { ++ /* ++ * If there was an old block, and we are not still using it, ++ * we now release the old block. ++ */ ++ unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount); ++ ++ error = extN_journal_get_write_access(handle, old_bh); ++ if (error) ++ goto cleanup; ++ if (refcount == 1) { ++ /* Free the old block. */ ++ ea_bdebug(old_bh, "freeing"); ++ extN_xattr_free_block(handle, inode, old_bh->b_blocknr); ++ ++ /* extN_forget() calls bforget() for us, but we ++ let our caller release old_bh, so we need to ++ duplicate the handle before. */ ++ get_bh(old_bh); ++ extN_forget(handle, 1, inode, old_bh,old_bh->b_blocknr); ++ } else { ++ /* Decrement the refcount only. */ ++ refcount--; ++ HDR(old_bh)->h_refcount = cpu_to_le32(refcount); ++ extN_xattr_quota_free(inode); ++ extN_journal_dirty_metadata(handle, old_bh); ++ ea_bdebug(old_bh, "refcount now=%d", refcount); ++ } ++ } ++ ++cleanup: ++ if (old_bh != new_bh) ++ brelse(new_bh); ++ ++ return error; ++} ++ ++/* ++ * extN_xattr_drop_inode() ++ * ++ * Free extended attribute resources associated with this inode. This ++ * is called immediately before an inode is freed. ++ */ ++void ++extN_xattr_drop_inode(handle_t *handle, struct inode *inode) ++{ ++ struct buffer_head *bh; ++ unsigned int block = EXTN_I(inode)->i_file_acl; ++ ++ if (!block) ++ return; ++ extN_xattr_lock(); ++ ++ bh = sb_bread(inode->i_sb, block); ++ if (!bh) { ++ extN_error(inode->i_sb, "extN_xattr_drop_inode", ++ "inode %ld: block %d read error", inode->i_ino, block); ++ goto cleanup; ++ } ++ ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count))); ++ if (HDR(bh)->h_magic != cpu_to_le32(EXTN_XATTR_MAGIC) || ++ HDR(bh)->h_blocks != cpu_to_le32(1)) { ++ extN_error(inode->i_sb, "extN_xattr_drop_inode", ++ "inode %ld: bad block %d", inode->i_ino, block); ++ goto cleanup; ++ } ++ extN_journal_get_write_access(handle, bh); ++ ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1); ++ if (HDR(bh)->h_refcount == cpu_to_le32(1)) { ++ extN_xattr_cache_remove(bh); ++ extN_xattr_free_block(handle, inode, block); ++ extN_forget(handle, 1, inode, bh, block); ++ bh = NULL; ++ } else { ++ HDR(bh)->h_refcount = cpu_to_le32( ++ le32_to_cpu(HDR(bh)->h_refcount) - 1); ++ extN_journal_dirty_metadata(handle, bh); ++ if (IS_SYNC(inode)) ++ handle->h_sync = 1; ++ extN_xattr_quota_free(inode); ++ } ++ EXTN_I(inode)->i_file_acl = 0; ++ ++cleanup: ++ brelse(bh); ++ extN_xattr_unlock(); ++} ++ ++/* ++ * extN_xattr_put_super() ++ * ++ * This is called when a file system is unmounted. ++ */ ++void ++extN_xattr_put_super(struct super_block *sb) ++{ ++#ifdef CONFIG_EXTN_FS_XATTR_SHARING ++ mb_cache_shrink(extN_xattr_cache, sb->s_dev); ++#endif ++} ++ ++#ifdef CONFIG_EXTN_FS_XATTR_SHARING ++ ++/* ++ * extN_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 int ++extN_xattr_cache_insert(struct buffer_head *bh) ++{ ++ __u32 hash = le32_to_cpu(HDR(bh)->h_hash); ++ struct mb_cache_entry *ce; ++ int error; ++ ++ ce = mb_cache_entry_alloc(extN_xattr_cache); ++ if (!ce) ++ return -ENOMEM; ++ error = mb_cache_entry_insert(ce, bh->b_dev, bh->b_blocknr, &hash); ++ if (error) { ++ mb_cache_entry_free(ce); ++ if (error == -EBUSY) { ++ ea_bdebug(bh, "already in cache (%d cache entries)", ++ atomic_read(&extN_xattr_cache->c_entry_count)); ++ error = 0; ++ } ++ } else { ++ ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash, ++ atomic_read(&extN_xattr_cache->c_entry_count)); ++ mb_cache_entry_release(ce); ++ } ++ return error; ++} ++ ++/* ++ * extN_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 int ++extN_xattr_cmp(struct extN_xattr_header *header1, ++ struct extN_xattr_header *header2) ++{ ++ struct extN_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_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 = EXTN_XATTR_NEXT(entry1); ++ entry2 = EXTN_XATTR_NEXT(entry2); ++ } ++ if (!IS_LAST_ENTRY(entry2)) ++ return 1; ++ return 0; ++} ++ ++/* ++ * extN_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 * ++extN_xattr_cache_find(struct inode *inode, struct extN_xattr_header *header) ++{ ++ __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); ++ ce = mb_cache_entry_find_first(extN_xattr_cache, 0, inode->i_dev, hash); ++ while (ce) { ++ struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block); ++ ++ if (!bh) { ++ extN_error(inode->i_sb, "extN_xattr_cache_find", ++ "inode %ld: block %ld read error", ++ inode->i_ino, ce->e_block); ++ } else if (le32_to_cpu(HDR(bh)->h_refcount) > ++ EXTN_XATTR_REFCOUNT_MAX) { ++ ea_idebug(inode, "block %ld refcount %d>%d",ce->e_block, ++ le32_to_cpu(HDR(bh)->h_refcount), ++ EXTN_XATTR_REFCOUNT_MAX); ++ } else if (!extN_xattr_cmp(header, HDR(bh))) { ++ ea_bdebug(bh, "b_count=%d",atomic_read(&(bh->b_count))); ++ mb_cache_entry_release(ce); ++ return bh; ++ } ++ brelse(bh); ++ ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash); ++ } ++ return NULL; ++} ++ ++/* ++ * extN_xattr_cache_remove() ++ * ++ * Remove the cache entry of a block from the cache. Called when a ++ * block becomes invalid. ++ */ ++static void ++extN_xattr_cache_remove(struct buffer_head *bh) ++{ ++ struct mb_cache_entry *ce; ++ ++ ce = mb_cache_entry_get(extN_xattr_cache, bh->b_dev, bh->b_blocknr); ++ if (ce) { ++ ea_bdebug(bh, "removing (%d cache entries remaining)", ++ atomic_read(&extN_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 ++ ++/* ++ * extN_xattr_hash_entry() ++ * ++ * Compute the hash of an extended attribute. ++ */ ++static inline void extN_xattr_hash_entry(struct extN_xattr_header *header, ++ struct extN_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) { ++ __u32 *value = (__u32 *)((char *)header + ++ le16_to_cpu(entry->e_value_offs)); ++ for (n = (le32_to_cpu(entry->e_value_size) + ++ EXTN_XATTR_ROUND) >> EXTN_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 ++ ++/* ++ * extN_xattr_rehash() ++ * ++ * Re-compute the extended attribute hash value after an entry has changed. ++ */ ++static void extN_xattr_rehash(struct extN_xattr_header *header, ++ struct extN_xattr_entry *entry) ++{ ++ struct extN_xattr_entry *here; ++ __u32 hash = 0; ++ ++ extN_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 = EXTN_XATTR_NEXT(here); ++ } ++ header->h_hash = cpu_to_le32(hash); ++} ++ ++#undef BLOCK_HASH_SHIFT ++ ++int __init ++init_extN_xattr(void) ++{ ++ extN_xattr_cache = mb_cache_create("extN_xattr", NULL, ++ sizeof(struct mb_cache_entry) + ++ sizeof(struct mb_cache_entry_index), 1, 61); ++ if (!extN_xattr_cache) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++void ++exit_extN_xattr(void) ++{ ++ if (extN_xattr_cache) ++ mb_cache_destroy(extN_xattr_cache); ++ extN_xattr_cache = NULL; ++} ++ ++#else /* CONFIG_EXTN_FS_XATTR_SHARING */ ++ ++int __init ++init_extN_xattr(void) ++{ ++ return 0; ++} ++ ++void ++exit_extN_xattr(void) ++{ ++} ++ ++#endif /* CONFIG_EXTN_FS_XATTR_SHARING */ -- 1.8.3.1