Index: linux-2.4.29/Documentation/Configure.help
===================================================================
--- linux-2.4.29.orig/Documentation/Configure.help
+++ linux-2.4.29/Documentation/Configure.help
@@ -16679,6 +16679,30 @@ CONFIG_EXT2_FS
be compiled as a module, and so this could be dangerous. Most
everyone wants to say Y here.
+Ext2 extended attributes
+CONFIG_EXT2_FS_XATTR
+ Extended attributes are name:value pairs associated with inodes by
+ the kernel or by users (see the attr(5) manual page, or visit
+ for details).
+
+ If unsure, say N.
+
+Ext2 extended attribute block sharing
+CONFIG_EXT2_FS_XATTR_SHARING
+ This options enables code for sharing identical extended attribute
+ blocks among multiple inodes.
+
+ Usually, say Y.
+
+Ext2 extended user attributes
+CONFIG_EXT2_FS_XATTR_USER
+ This option enables extended user attributes on ext2. Processes can
+ associate extended user attributes with inodes to store additional
+ information such as the character encoding of files, etc. (see the
+ attr(5) manual page, or visit for details).
+
+ If unsure, say N.
+
Ext3 journalling file system support (EXPERIMENTAL)
CONFIG_EXT3_FS
This is the journalling version of the Second extended file system
@@ -16711,6 +16735,30 @@ CONFIG_EXT3_FS
of your root partition (the one containing the directory /) cannot
be compiled as a module, and so this may be dangerous.
+Ext3 extended attributes
+CONFIG_EXT3_FS_XATTR
+ Extended attributes are name:value pairs associated with inodes by
+ the kernel or by users (see the attr(5) manual page, or visit
+ for details).
+
+ If unsure, say N.
+
+Ext3 extended attribute block sharing
+CONFIG_EXT3_FS_XATTR_SHARING
+ This options enables code for sharing identical extended attribute
+ blocks among multiple inodes.
+
+ Usually, say Y.
+
+Ext3 extended user attributes
+CONFIG_EXT3_FS_XATTR_USER
+ This option enables extended user attributes on ext3. Processes can
+ associate extended user attributes with inodes to store additional
+ information such as the character encoding of files, etc. (see the
+ attr(5) manual page, or visit for details).
+
+ If unsure, say N.
+
Journal Block Device support (JBD for ext3) (EXPERIMENTAL)
CONFIG_JBD
This is a generic journalling layer for block devices. It is
Index: linux-2.4.29/Documentation/filesystems/Locking
===================================================================
--- linux-2.4.29.orig/Documentation/filesystems/Locking
+++ linux-2.4.29/Documentation/filesystems/Locking
@@ -69,8 +69,8 @@ permission: yes no no
getattr: (see below)
revalidate: no (see below)
setxattr: yes yes no
-getxattr: yes yes no
-listxattr: yes yes no
+getxattr: yes no no
+listxattr: yes no no
removexattr: yes yes no
Additionally, ->rmdir() has i_zombie on victim and so does ->rename()
in case when target exists and is a directory.
Index: linux-2.4.29/arch/alpha/kernel/entry.S
===================================================================
--- linux-2.4.29.orig/arch/alpha/kernel/entry.S
+++ linux-2.4.29/arch/alpha/kernel/entry.S
@@ -10,7 +10,7 @@
#define SIGCHLD 20
-#define NR_SYSCALLS 382
+#define NR_SYSCALLS 394
/*
* These offsets must match with alpha_mv in .
@@ -1154,6 +1154,18 @@ sys_call_table:
.quad sys_readahead
.quad sys_ni_syscall /* 380, sys_security */
.quad sys_tkill
+ .quad sys_setxattr
+ .quad sys_lsetxattr
+ .quad sys_fsetxattr
+ .quad sys_getxattr /* 385 */
+ .quad sys_lgetxattr
+ .quad sys_fgetxattr
+ .quad sys_listxattr
+ .quad sys_llistxattr
+ .quad sys_flistxattr /* 390 */
+ .quad sys_removexattr
+ .quad sys_lremovexattr
+ .quad sys_fremovexattr
/* Remember to update everything, kids. */
.ifne (. - sys_call_table) - (NR_SYSCALLS * 8)
Index: linux-2.4.29/arch/arm/kernel/calls.S
===================================================================
--- linux-2.4.29.orig/arch/arm/kernel/calls.S
+++ linux-2.4.29/arch/arm/kernel/calls.S
@@ -240,18 +240,18 @@ __syscall_start:
.long SYMBOL_NAME(sys_ni_syscall) /* Security */
.long SYMBOL_NAME(sys_gettid)
/* 225 */ .long SYMBOL_NAME(sys_readahead)
- .long SYMBOL_NAME(sys_ni_syscall) /* setxattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* lsetxattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* fsetxattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* getxattr */
-/* 230 */ .long SYMBOL_NAME(sys_ni_syscall) /* lgetxattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* fgetxattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* listxattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* llistxattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* flistxattr */
-/* 235 */ .long SYMBOL_NAME(sys_ni_syscall) /* removexattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* lremovexattr */
- .long SYMBOL_NAME(sys_ni_syscall) /* fremovexattr */
+ .long SYMBOL_NAME(sys_setxattr)
+ .long SYMBOL_NAME(sys_lsetxattr)
+ .long SYMBOL_NAME(sys_fsetxattr)
+ .long SYMBOL_NAME(sys_getxattr)
+/* 230 */ .long SYMBOL_NAME(sys_lgetxattr)
+ .long SYMBOL_NAME(sys_fgetxattr)
+ .long SYMBOL_NAME(sys_listxattr)
+ .long SYMBOL_NAME(sys_llistxattr)
+ .long SYMBOL_NAME(sys_flistxattr)
+/* 235 */ .long SYMBOL_NAME(sys_removexattr)
+ .long SYMBOL_NAME(sys_lremovexattr)
+ .long SYMBOL_NAME(sys_fremovexattr)
.long SYMBOL_NAME(sys_tkill)
.long SYMBOL_NAME(sys_ni_syscall) /* sendfile64 */
/* 240 */ .long SYMBOL_NAME(sys_ni_syscall) /* futex */
Index: linux-2.4.29/arch/ppc64/kernel/misc.S
===================================================================
--- linux-2.4.29.orig/arch/ppc64/kernel/misc.S
+++ linux-2.4.29/arch/ppc64/kernel/misc.S
@@ -807,7 +807,6 @@ _GLOBAL(sys_call_table32)
.llong .sys_madvise /* 205 */
.llong .sys_mincore /* 206 */
.llong .sys_gettid /* 207 */
-#if 0 /* Reserved syscalls */
.llong .sys_tkill /* 208 */
.llong .sys_setxattr
.llong .sys_lsetxattr /* 210 */
@@ -821,10 +820,7 @@ _GLOBAL(sys_call_table32)
.llong .sys_removexattr
.llong .sys_lremovexattr
.llong .sys_fremovexattr /* 220 */
- .llong .sys_futex
-#endif
- .llong .sys_perfmonctl /* Put this here for now ... */
- .rept NR_syscalls-222
+ .rept NR_syscalls-221
.llong .sys_ni_syscall
.endr
#endif
@@ -1038,7 +1034,6 @@ _GLOBAL(sys_call_table)
.llong .sys_madvise /* 205 */
.llong .sys_mincore /* 206 */
.llong .sys_gettid /* 207 */
-#if 0 /* Reserved syscalls */
.llong .sys_tkill /* 208 */
.llong .sys_setxattr
.llong .sys_lsetxattr /* 210 */
@@ -1052,9 +1047,6 @@ _GLOBAL(sys_call_table)
.llong .sys_removexattr
.llong .sys_lremovexattr
.llong .sys_fremovexattr /* 220 */
- .llong .sys_futex
-#endif
- .llong .sys_perfmonctl /* Put this here for now ... */
- .rept NR_syscalls-222
+ .rept NR_syscalls-221
.llong .sys_ni_syscall
.endr
Index: linux-2.4.29/arch/s390/kernel/entry.S
===================================================================
--- linux-2.4.29.orig/arch/s390/kernel/entry.S
+++ linux-2.4.29/arch/s390/kernel/entry.S
@@ -558,18 +558,18 @@ sys_call_table:
.long sys_fcntl64
.long sys_readahead
.long sys_ni_syscall
- .long sys_ni_syscall /* 224 - reserved for setxattr */
- .long sys_ni_syscall /* 225 - reserved for lsetxattr */
- .long sys_ni_syscall /* 226 - reserved for fsetxattr */
- .long sys_ni_syscall /* 227 - reserved for getxattr */
- .long sys_ni_syscall /* 228 - reserved for lgetxattr */
- .long sys_ni_syscall /* 229 - reserved for fgetxattr */
- .long sys_ni_syscall /* 230 - reserved for listxattr */
- .long sys_ni_syscall /* 231 - reserved for llistxattr */
- .long sys_ni_syscall /* 232 - reserved for flistxattr */
- .long sys_ni_syscall /* 233 - reserved for removexattr */
- .long sys_ni_syscall /* 234 - reserved for lremovexattr */
- .long sys_ni_syscall /* 235 - reserved for fremovexattr */
+ .long sys_setxattr
+ .long sys_lsetxattr /* 225 */
+ .long sys_fsetxattr
+ .long sys_getxattr
+ .long sys_lgetxattr
+ .long sys_fgetxattr
+ .long sys_listxattr /* 230 */
+ .long sys_llistxattr
+ .long sys_flistxattr
+ .long sys_removexattr
+ .long sys_lremovexattr
+ .long sys_fremovexattr /* 235 */
.long sys_gettid
.long sys_tkill
.rept 255-237
Index: linux-2.4.29/arch/s390x/kernel/entry.S
===================================================================
--- linux-2.4.29.orig/arch/s390x/kernel/entry.S
+++ linux-2.4.29/arch/s390x/kernel/entry.S
@@ -591,18 +591,18 @@ sys_call_table:
.long SYSCALL(sys_ni_syscall,sys32_fcntl64_wrapper)
.long SYSCALL(sys_readahead,sys32_readahead)
.long SYSCALL(sys_ni_syscall,sys_ni_syscall)
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 224 - reserved for setxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 225 - reserved for lsetxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 226 - reserved for fsetxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 227 - reserved for getxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 228 - reserved for lgetxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 229 - reserved for fgetxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 230 - reserved for listxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 231 - reserved for llistxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 232 - reserved for flistxattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 233 - reserved for removexattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 234 - reserved for lremovexattr */
- .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 235 - reserved for fremovexattr */
+ .long SYSCALL(sys_setxattr,sys32_setxattr_wrapper)
+ .long SYSCALL(sys_lsetxattr,sys32_lsetxattr_wrapper) /* 225 */
+ .long SYSCALL(sys_fsetxattr,sys32_fsetxattr_wrapper)
+ .long SYSCALL(sys_getxattr,sys32_getxattr_wrapper)
+ .long SYSCALL(sys_lgetxattr,sys32_lgetxattr_wrapper)
+ .long SYSCALL(sys_fgetxattr,sys32_fgetxattr_wrapper)
+ .long SYSCALL(sys_listxattr,sys32_listxattr_wrapper) /* 230 */
+ .long SYSCALL(sys_llistxattr,sys32_llistxattr_wrapper)
+ .long SYSCALL(sys_flistxattr,sys32_flistxattr_wrapper)
+ .long SYSCALL(sys_removexattr,sys32_removexattr_wrapper)
+ .long SYSCALL(sys_lremovexattr,sys32_lremovexattr_wrapper)
+ .long SYSCALL(sys_fremovexattr,sys32_fremovexattr_wrapper)/* 235 */
.long SYSCALL(sys_gettid,sys_gettid)
.long SYSCALL(sys_tkill,sys_tkill)
.rept 255-237
Index: linux-2.4.29/arch/s390x/kernel/wrapper32.S
===================================================================
--- linux-2.4.29.orig/arch/s390x/kernel/wrapper32.S
+++ linux-2.4.29/arch/s390x/kernel/wrapper32.S
@@ -1107,3 +1107,93 @@ sys32_stime_wrapper:
sys32_sysctl_wrapper:
llgtr %r2,%r2 # struct __sysctl_args32 *
jg sys32_sysctl
+
+ .globl sys32_setxattr_wrapper
+sys32_setxattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ lgfr %r6,%r6 # int
+ jg sys_setxattr
+
+ .globl sys32_lsetxattr_wrapper
+sys32_lsetxattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ lgfr %r6,%r6 # int
+ jg sys_lsetxattr
+
+ .globl sys32_fsetxattr_wrapper
+sys32_fsetxattr_wrapper:
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ lgfr %r6,%r6 # int
+ jg sys_fsetxattr
+
+ .globl sys32_getxattr_wrapper
+sys32_getxattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ jg sys_getxattr
+
+ .globl sys32_lgetxattr_wrapper
+sys32_lgetxattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ jg sys_lgetxattr
+
+ .globl sys32_fgetxattr_wrapper
+sys32_fgetxattr_wrapper:
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ jg sys_fgetxattr
+
+ .globl sys32_listxattr_wrapper
+sys32_listxattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ jg sys_listxattr
+
+ .globl sys32_llistxattr_wrapper
+sys32_llistxattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ jg sys_llistxattr
+
+ .globl sys32_flistxattr_wrapper
+sys32_flistxattr_wrapper:
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ jg sys_flistxattr
+
+ .globl sys32_removexattr_wrapper
+sys32_removexattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ jg sys_removexattr
+
+ .globl sys32_lremovexattr_wrapper
+sys32_lremovexattr_wrapper:
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ jg sys_lremovexattr
+
+ .globl sys32_fremovexattr_wrapper
+sys32_fremovexattr_wrapper:
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ jg sys_fremovexattr
Index: linux-2.4.29/fs/Config.in
===================================================================
--- linux-2.4.29.orig/fs/Config.in
+++ linux-2.4.29/fs/Config.in
@@ -29,6 +29,13 @@ dep_mbool ' Debug Befs' CONFIG_BEFS_DEB
dep_tristate 'BFS file system support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL
tristate 'Ext3 journalling file system support' CONFIG_EXT3_FS
+dep_mbool ' Ext3 extended attributes' CONFIG_EXT3_FS_XATTR $CONFIG_EXT3_FS
+dep_bool ' Ext3 extended attribute block sharing' \
+ CONFIG_EXT3_FS_XATTR_SHARING $CONFIG_EXT3_FS_XATTR
+dep_bool ' Ext3 extended user attributes' \
+ CONFIG_EXT3_FS_XATTR_USER $CONFIG_EXT3_FS_XATTR
+dep_bool ' Ext3 trusted extended attributes' \
+ CONFIG_EXT3_FS_XATTR_TRUSTED $CONFIG_EXT3_FS_XATTR
# CONFIG_JBD could be its own option (even modular), but until there are
# other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS
# dep_tristate ' Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS
@@ -92,6 +99,13 @@ dep_mbool ' QNX4FS write support (DANGE
tristate 'ROM file system support' CONFIG_ROMFS_FS
tristate 'Second extended fs support' CONFIG_EXT2_FS
+dep_mbool ' Ext2 extended attributes' CONFIG_EXT2_FS_XATTR $CONFIG_EXT2_FS
+dep_bool ' Ext2 extended attribute block sharing' \
+ CONFIG_EXT2_FS_XATTR_SHARING $CONFIG_EXT2_FS_XATTR
+dep_bool ' Ext2 extended user attributes' \
+ CONFIG_EXT2_FS_XATTR_USER $CONFIG_EXT2_FS_XATTR
+dep_bool ' Ext2 trusted extended attributes' \
+ CONFIG_EXT2_FS_XATTR_TRUSTED $CONFIG_EXT2_FS_XATTR
tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS
@@ -171,6 +185,10 @@ else
define_tristate CONFIG_ZISOFS_FS n
fi
+# Meta block cache for Extended Attributes (ext2/ext3)
+#tristate 'Meta block cache' CONFIG_FS_MBCACHE
+define_tristate CONFIG_FS_MBCACHE y
+
mainmenu_option next_comment
comment 'Partition Types'
source fs/partitions/Config.in
Index: linux-2.4.29/fs/Makefile
===================================================================
--- linux-2.4.29.orig/fs/Makefile
+++ linux-2.4.29/fs/Makefile
@@ -77,6 +77,9 @@ obj-y += binfmt_script.o
obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o
+export-objs += mbcache.o
+obj-$(CONFIG_FS_MBCACHE) += mbcache.o
+
# persistent filesystems
obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
Index: linux-2.4.29/fs/ext2/Makefile
===================================================================
--- linux-2.4.29.orig/fs/ext2/Makefile
+++ linux-2.4.29/fs/ext2/Makefile
@@ -13,4 +13,9 @@ obj-y := balloc.o bitmap.o dir.o file
ioctl.o namei.o super.o symlink.o
obj-m := $(O_TARGET)
+export-objs += xattr.o
+obj-$(CONFIG_EXT2_FS_XATTR) += xattr.o
+obj-$(CONFIG_EXT2_FS_XATTR_USER) += xattr_user.o
+obj-$(CONFIG_EXT2_FS_XATTR_TRUSTED) += xattr_trusted.o
+
include $(TOPDIR)/Rules.make
Index: linux-2.4.29/fs/ext2/file.c
===================================================================
--- linux-2.4.29.orig/fs/ext2/file.c
+++ linux-2.4.29/fs/ext2/file.c
@@ -20,6 +20,7 @@
#include
#include
+#include
#include
/*
@@ -51,4 +52,8 @@ struct file_operations ext2_file_operati
struct inode_operations ext2_file_inode_operations = {
truncate: ext2_truncate,
+ setxattr: ext2_setxattr,
+ getxattr: ext2_getxattr,
+ listxattr: ext2_listxattr,
+ removexattr: ext2_removexattr,
};
Index: linux-2.4.29/fs/ext2/ialloc.c
===================================================================
--- linux-2.4.29.orig/fs/ext2/ialloc.c
+++ linux-2.4.29/fs/ext2/ialloc.c
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
@@ -167,6 +168,7 @@ void ext2_free_inode (struct inode * ino
*/
if (!is_bad_inode(inode)) {
/* Quota is already initialized in iput() */
+ ext2_xattr_delete_inode(inode);
DQUOT_FREE_INODE(inode);
DQUOT_DROP(inode);
}
@@ -395,6 +397,10 @@ repeat:
inode->i_generation = event++;
mark_inode_dirty(inode);
+#ifdef CONFIG_EXT2_FS_XATTR
+ init_rwsem(&inode->u.ext2_i.xattr_sem);
+#endif
+
unlock_super (sb);
if(DQUOT_ALLOC_INODE(inode)) {
DQUOT_DROP(inode);
Index: linux-2.4.29/fs/ext2/inode.c
===================================================================
--- linux-2.4.29.orig/fs/ext2/inode.c
+++ linux-2.4.29/fs/ext2/inode.c
@@ -64,9 +64,7 @@ void ext2_delete_inode (struct inode * i
{
lock_kernel();
- if (is_bad_inode(inode) ||
- inode->i_ino == EXT2_ACL_IDX_INO ||
- inode->i_ino == EXT2_ACL_DATA_INO)
+ if (is_bad_inode(inode))
goto no_delete;
inode->u.ext2_i.i_dtime = CURRENT_TIME;
mark_inode_dirty(inode);
@@ -917,8 +915,7 @@ void ext2_read_inode (struct inode * ino
unsigned long offset;
struct ext2_group_desc * gdp;
- if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
- inode->i_ino != EXT2_ACL_DATA_INO &&
+ if ((inode->i_ino != EXT2_ROOT_INO &&
inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (inode->i_sb, "ext2_read_inode",
@@ -1004,10 +1001,7 @@ void ext2_read_inode (struct inode * ino
for (block = 0; block < EXT2_N_BLOCKS; block++)
inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
- if (inode->i_ino == EXT2_ACL_IDX_INO ||
- inode->i_ino == EXT2_ACL_DATA_INO)
- /* Nothing to do */ ;
- else if (S_ISREG(inode->i_mode)) {
+ if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext2_file_inode_operations;
inode->i_fop = &ext2_file_operations;
inode->i_mapping->a_ops = &ext2_aops;
@@ -1019,15 +1013,20 @@ void ext2_read_inode (struct inode * ino
if (ext2_inode_is_fast_symlink(inode))
inode->i_op = &ext2_fast_symlink_inode_operations;
else {
- inode->i_op = &page_symlink_inode_operations;
+ inode->i_op = &ext2_symlink_inode_operations;
inode->i_mapping->a_ops = &ext2_aops;
}
- } else
+ } else {
+ inode->i_op = &ext2_special_inode_operations;
init_special_inode(inode, inode->i_mode,
le32_to_cpu(raw_inode->i_block[0]));
+ }
brelse (bh);
inode->i_attr_flags = 0;
ext2_set_inode_flags(inode);
+#ifdef CONFIG_EXT2_FS_XATTR
+ init_rwsem(&inode->u.ext2_i.xattr_sem);
+#endif
return;
bad_inode:
Index: linux-2.4.29/fs/ext2/namei.c
===================================================================
--- linux-2.4.29.orig/fs/ext2/namei.c
+++ linux-2.4.29/fs/ext2/namei.c
@@ -31,6 +31,7 @@
#include
#include
+#include
#include
/*
@@ -136,7 +137,7 @@ static int ext2_symlink (struct inode *
if (l > sizeof (inode->u.ext2_i.i_data)) {
/* slow symlink */
- inode->i_op = &page_symlink_inode_operations;
+ inode->i_op = &ext2_symlink_inode_operations;
inode->i_mapping->a_ops = &ext2_aops;
err = block_symlink(inode, symname, l);
if (err)
@@ -345,4 +346,15 @@ struct inode_operations ext2_dir_inode_o
rmdir: ext2_rmdir,
mknod: ext2_mknod,
rename: ext2_rename,
+ setxattr: ext2_setxattr,
+ getxattr: ext2_getxattr,
+ listxattr: ext2_listxattr,
+ removexattr: ext2_removexattr,
+};
+
+struct inode_operations ext2_special_inode_operations = {
+ setxattr: ext2_setxattr,
+ getxattr: ext2_getxattr,
+ listxattr: ext2_listxattr,
+ removexattr: ext2_removexattr,
};
Index: linux-2.4.29/fs/ext2/super.c
===================================================================
--- linux-2.4.29.orig/fs/ext2/super.c
+++ linux-2.4.29/fs/ext2/super.c
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -125,6 +126,7 @@ void ext2_put_super (struct super_block
int db_count;
int i;
+ ext2_xattr_put_super(sb);
if (!(sb->s_flags & MS_RDONLY)) {
struct ext2_super_block *es = EXT2_SB(sb)->s_es;
@@ -175,6 +177,13 @@ static int parse_options (char * options
this_char = strtok (NULL, ",")) {
if ((value = strchr (this_char, '=')) != NULL)
*value++ = 0;
+#ifdef CONFIG_EXT2_FS_XATTR_USER
+ if (!strcmp (this_char, "user_xattr"))
+ set_opt (*mount_options, XATTR_USER);
+ else if (!strcmp (this_char, "nouser_xattr"))
+ clear_opt (*mount_options, XATTR_USER);
+ else
+#endif
if (!strcmp (this_char, "bsddf"))
clear_opt (*mount_options, MINIX_DF);
else if (!strcmp (this_char, "nouid32")) {
@@ -446,6 +455,9 @@ struct super_block * ext2_read_super (st
blocksize = BLOCK_SIZE;
sb->u.ext2_sb.s_mount_opt = 0;
+#ifdef CONFIG_EXT2_FS_XATTR_USER
+ /* set_opt (sb->u.ext2_sb.s_mount_opt, XATTR_USER); */
+#endif
if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
&sb->u.ext2_sb.s_mount_opt)) {
return NULL;
@@ -840,11 +852,21 @@ static DECLARE_FSTYPE_DEV(ext2_fs_type,
static int __init init_ext2_fs(void)
{
- return register_filesystem(&ext2_fs_type);
+ int error = init_ext2_xattr();
+ if (error)
+ return error;
+ error = init_ext2_xattr_user();
+ if (error)
+ return error;
+ error = init_ext2_xattr_trusted();
+ if (error)
+ return error;
+ return register_filesystem(&ext2_fs_type);
}
static void __exit exit_ext2_fs(void)
{
+ exit_ext2_xattr();
unregister_filesystem(&ext2_fs_type);
}
Index: linux-2.4.29/fs/ext2/symlink.c
===================================================================
--- linux-2.4.29.orig/fs/ext2/symlink.c
+++ linux-2.4.29/fs/ext2/symlink.c
@@ -19,6 +19,7 @@
#include
#include
+#include
static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen)
{
@@ -32,7 +33,20 @@ static int ext2_follow_link(struct dentr
return vfs_follow_link(nd, s);
}
+struct inode_operations ext2_symlink_inode_operations = {
+ readlink: page_readlink,
+ follow_link: page_follow_link,
+ setxattr: ext2_setxattr,
+ getxattr: ext2_getxattr,
+ listxattr: ext2_listxattr,
+ removexattr: ext2_removexattr,
+};
+
struct inode_operations ext2_fast_symlink_inode_operations = {
readlink: ext2_readlink,
follow_link: ext2_follow_link,
+ setxattr: ext2_setxattr,
+ getxattr: ext2_getxattr,
+ listxattr: ext2_listxattr,
+ removexattr: ext2_removexattr,
};
Index: linux-2.4.29/fs/ext2/xattr.c
===================================================================
--- /dev/null
+++ linux-2.4.29/fs/ext2/xattr.c
@@ -0,0 +1,1145 @@
+/*
+ * linux/fs/ext2/xattr.c
+ *
+ * Copyright (C) 2001-2003 by Andreas Gruenbacher,
+ *
+ * Fix by Harrison Xing .
+ * 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 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,
+ * which avoids deadlocks.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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)
+#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+
+#ifdef EXT2_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 ext2_xattr_set2(struct inode *, struct buffer_head *,
+ struct ext2_xattr_header *);
+
+#ifdef CONFIG_EXT2_FS_XATTR_SHARING
+
+static int ext2_xattr_cache_insert(struct buffer_head *);
+static struct buffer_head *ext2_xattr_cache_find(struct inode *,
+ struct ext2_xattr_header *,
+ struct mb_cache_entry **);
+static void ext2_xattr_rehash(struct ext2_xattr_header *,
+ struct ext2_xattr_entry *);
+
+static struct mb_cache *ext2_xattr_cache;
+
+#else
+# define ext2_xattr_cache_insert(bh) 0
+# define ext2_xattr_cache_find(inode, header, pce) NULL
+# define ext2_xattr_rehash(header, entry) while(0) {}
+#endif
+
+struct ext2_xattr_handler *ext2_xattr_handlers[EXT2_XATTR_INDEX_MAX];
+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;
+}
+
+/*
+ * 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;
+}
+
+static inline struct ext2_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
+ * BKL: held
+ */
+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;
+
+ 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
+ * BKL: held
+ */
+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
+ * BKL: held
+ */
+int
+ext2_setxattr(struct dentry *dentry, const char *name, 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
+ * BKL: held
+ */
+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);
+}
+
+/*
+ * 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.
+ */
+int
+ext2_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 size, name_len;
+ 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.
+ */
+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;
+ 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;
+ }
+ /* 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);
+ 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);
+ }
+
+ 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);
+ }
+ error = size;
+
+cleanup:
+ brelse(bh);
+ up_read(&EXT2_I(inode)->xattr_sem);
+
+ return error;
+}
+
+/*
+ * 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;
+
+ lock_super(sb);
+ EXT2_SB(sb)->s_es->s_feature_compat |=
+ cpu_to_le32(EXT2_FEATURE_COMPAT_EXT_ATTR);
+ sb->s_dirt = 1;
+ mark_buffer_dirty(EXT2_SB(sb)->s_sbh);
+ unlock_super(sb);
+}
+
+/*
+ * Release the xattr block BH: If the reference count is > 1, decrement
+ * it; otherwise free the block.
+ */
+static void
+ext2_xattr_release_block(struct inode *inode, struct buffer_head *bh)
+{
+ struct mb_cache_entry *ce;
+
+ ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_dev, bh->b_blocknr);
+ if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
+ ea_bdebug(bh, "freeing");
+ if (ce)
+ mb_cache_entry_free(ce);
+ ext2_free_blocks(inode, bh->b_blocknr, 1);
+ } else {
+ lock_buffer(bh);
+ HDR(bh)->h_refcount = cpu_to_le32(
+ le32_to_cpu(HDR(bh)->h_refcount) - 1);
+ mark_buffer_dirty(bh);
+ ea_bdebug(bh, "releasing; refcount now=%d",
+ le32_to_cpu(HDR(bh)->h_refcount));
+ unlock_buffer(bh);
+ if (ce)
+ mb_cache_entry_release(ce);
+ DQUOT_FREE_BLOCK(inode, 1);
+ }
+}
+
+/*
+ * 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.
+ */
+int
+ext2_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 (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;
+ 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;
+
+ ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_dev,
+ bh->b_blocknr);
+ if (header->h_refcount == cpu_to_le32(1)) {
+ if (ce)
+ mb_cache_entry_free(ce);
+ ea_bdebug(bh, "modifying in-place");
+ lock_buffer(bh);
+ /* keep the buffer locked while modifying it. */
+ } else {
+ int offset;
+
+ if (ce)
+ mb_cache_entry_release(ce);
+ 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 {
+ /* 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(EXT2_XATTR_MAGIC);
+ header->h_blocks = header->h_refcount = cpu_to_le32(1);
+ last = here = ENTRY(header+1);
+ }
+
+ /* Iff we are modifying the block in-place, bh is locked here. */
+
+ if (not_found) {
+ /* Insert the new name. */
+ int size = EXT2_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 {
+ 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 = EXT2_XATTR_SIZE(
+ le32_to_cpu(here->e_value_size));
+
+ if (size == EXT2_XATTR_SIZE(value_len)) {
+ /* The old and the new value have the same
+ size. Just replace. */
+ here->e_value_size = cpu_to_le32(value_len);
+ memset(val + size - EXT2_XATTR_PAD, 0,
+ EXT2_XATTR_PAD); /* Clear pad bytes. */
+ memcpy(val, value, value_len);
+ goto skip_replace;
+ }
+
+ /* Remove the old value. */
+ 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 = EXT2_XATTR_NEXT(last);
+ }
+ }
+ if (value == NULL) {
+ /* Remove the old name. */
+ int size = EXT2_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 = EXT2_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 - EXT2_XATTR_PAD, 0,
+ EXT2_XATTR_PAD); /* Clear the pad bytes. */
+ memcpy(val, value, value_len);
+ }
+ }
+
+skip_replace:
+ if (!IS_LAST_ENTRY(ENTRY(header+1)))
+ ext2_xattr_rehash(header, here);
+ if (bh && header == HDR(bh)) {
+ /* we were modifying in-place. */
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ }
+ error = ext2_xattr_set2(inode, bh, IS_LAST_ENTRY(ENTRY(header+1)) ?
+ NULL : header);
+
+cleanup:
+ brelse(bh);
+ if (!(bh && header == HDR(bh)))
+ kfree(header);
+ up_write(&EXT2_I(inode)->xattr_sem);
+
+ return error;
+}
+
+/*
+ * Second half of ext2_xattr_set(): Update the file system.
+ */
+static int
+ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
+ struct ext2_xattr_header *header)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *new_bh = NULL;
+ struct mb_cache_entry *ce = NULL;
+ int error;
+
+ if (header) {
+ new_bh = ext2_xattr_cache_find(inode, header, &ce);
+ if (new_bh) {
+ /* We found an identical block in the cache. */
+ if (new_bh == old_bh)
+ ea_bdebug(new_bh, "keeping this block");
+ else {
+ error = -EDQUOT;
+ /* How can we enforce the allocation? */
+ if (DQUOT_ALLOC_BLOCK(inode, 1))
+ goto cleanup;
+ /* The old block is released after updating
+ the inode. */
+ lock_buffer(new_bh);
+ HDR(new_bh)->h_refcount = cpu_to_le32(1 +
+ le32_to_cpu(HDR(new_bh)->h_refcount));
+ ea_bdebug(new_bh, "reusing; refcount now=%d",
+ le32_to_cpu(HDR(new_bh)->h_refcount));
+ unlock_buffer(new_bh);
+ mark_buffer_dirty(new_bh);
+ }
+ mb_cache_entry_release(ce);
+ ce = NULL;
+ } else if (old_bh && header == HDR(old_bh)) {
+ /* We were modifying this block in-place. */
+ ea_bdebug(bs->bh, "keeping this block");
+ new_bh = old_bh;
+ get_bh(new_bh);
+ ext2_xattr_cache_insert(new_bh);
+ } else {
+ /* We need to allocate a new block */
+ int goal = le32_to_cpu(EXT2_SB(inode->i_sb)->s_es->
+ s_first_data_block) +
+ EXT2_I(inode)->i_block_group *
+ EXT2_BLOCKS_PER_GROUP(inode->i_sb);
+ /* How can we enforce the allocation? */
+ int block = ext2_new_block(inode, goal, 0, 0, &error);
+ if (error)
+ goto cleanup;
+ ea_idebug(inode, "creating block %d", block);
+
+ new_bh = sb_getblk(sb, block);
+ if (!new_bh) {
+ ext2_free_blocks(inode, block, 1);
+ error = -EIO;
+ goto cleanup;
+ }
+ lock_buffer(new_bh);
+ memcpy(new_bh->b_data, header, new_bh->b_size);
+ mark_buffer_uptodate(new_bh, 1);
+ unlock_buffer(new_bh);
+ ext2_xattr_cache_insert(new_bh);
+
+ ext2_xattr_update_super_block(sb);
+ mark_buffer_dirty(new_bh);
+ }
+ if (IS_SYNC(inode)) {
+ ll_rw_block(WRITE, 1, &new_bh);
+ wait_on_buffer(new_bh);
+ error = -EIO;
+ if (buffer_req(new_bh) && !buffer_uptodate(new_bh))
+ goto cleanup;
+ }
+ }
+
+ /* Update the inode. */
+ EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
+ inode->i_ctime = CURRENT_TIME;
+ if (IS_SYNC(inode)) {
+ error = ext2_sync_inode (inode);
+ if (error)
+ goto cleanup;
+ } else
+ mark_inode_dirty(inode);
+
+ error = 0;
+ if (old_bh && old_bh != new_bh) {
+ /*
+ * If there was an old block and we are no longer using it,
+ * release the old block.
+ */
+ ext2_xattr_release_block(inode, old_bh);
+ }
+
+cleanup:
+ if (ce)
+ mb_cache_entry_release(ce);
+ brelse(new_bh);
+
+ return error;
+}
+
+/*
+ * ext2_xattr_delete_inode()
+ *
+ * Free extended attribute resources associated with this inode. This
+ * is called immediately before an inode is freed.
+ */
+void
+ext2_xattr_delete_inode(struct inode *inode)
+{
+ struct buffer_head *bh = NULL;
+
+ if (!EXT2_I(inode)->i_file_acl)
+ goto cleanup;
+ bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl);
+ if (!bh) {
+ ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
+ "inode %ld: block %d read error", inode->i_ino,
+ EXT2_I(inode)->i_file_acl);
+ goto cleanup;
+ }
+ ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count)));
+ if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
+ HDR(bh)->h_blocks != cpu_to_le32(1)) {
+ ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
+ "inode %ld: bad block %d", inode->i_ino,
+ EXT2_I(inode)->i_file_acl);
+ goto cleanup;
+ }
+ ext2_xattr_release_block(inode, bh);
+ EXT2_I(inode)->i_file_acl = 0;
+
+cleanup:
+ brelse(bh);
+}
+
+/*
+ * ext2_xattr_put_super()
+ *
+ * This is called when a file system is unmounted.
+ */
+void
+ext2_xattr_put_super(struct super_block *sb)
+{
+#ifdef CONFIG_EXT2_FS_XATTR_SHARING
+ mb_cache_shrink(ext2_xattr_cache, sb->s_dev);
+#endif
+}
+
+#ifdef CONFIG_EXT2_FS_XATTR_SHARING
+
+/*
+ * ext2_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
+ext2_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(ext2_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(&ext2_xattr_cache->c_entry_count));
+ error = 0;
+ }
+ } else {
+ ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash,
+ atomic_read(&ext2_xattr_cache->c_entry_count));
+ mb_cache_entry_release(ce);
+ }
+ return error;
+}
+
+/*
+ * ext2_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
+ext2_xattr_cmp(struct ext2_xattr_header *header1,
+ struct ext2_xattr_header *header2)
+{
+ struct ext2_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 = EXT2_XATTR_NEXT(entry1);
+ entry2 = EXT2_XATTR_NEXT(entry2);
+ }
+ if (!IS_LAST_ENTRY(entry2))
+ return 1;
+ return 0;
+}
+
+/*
+ * ext2_xattr_cache_find()
+ *
+ * Find an identical extended attribute block.
+ *
+ * Returns a locked buffer head to the block found, or NULL if such
+ * a block was not found or an error occurred.
+ */
+static struct buffer_head *
+ext2_xattr_cache_find(struct inode *inode, struct ext2_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(ext2_xattr_cache, 0, inode->i_dev, 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) {
+ ext2_error(inode->i_sb, "ext2_xattr_cache_find",
+ "inode %ld: block %ld read error",
+ inode->i_ino, ce->e_block);
+ } else if (le32_to_cpu(HDR(bh)->h_refcount) >=
+ EXT2_XATTR_REFCOUNT_MAX) {
+ ea_idebug(inode, "block %ld refcount %d>=%d",
+ (unsigned long) ce->e_block,
+ le32_to_cpu(HDR(bh)->h_refcount),
+ EXT2_XATTR_REFCOUNT_MAX);
+ } else if (ext2_xattr_cmp(header, HDR(bh)) == 0) {
+ *pce = ce;
+ return bh;
+ }
+ brelse(bh);
+
+ ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash);
+ }
+ return NULL;
+}
+
+#define NAME_HASH_SHIFT 5
+#define VALUE_HASH_SHIFT 16
+
+/*
+ * ext2_xattr_hash_entry()
+ *
+ * Compute the hash of an extended attribute.
+ */
+static inline void ext2_xattr_hash_entry(struct ext2_xattr_header *header,
+ struct ext2_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) +
+ EXT2_XATTR_ROUND) >> EXT2_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
+
+/*
+ * ext2_xattr_rehash()
+ *
+ * Re-compute the extended attribute hash value after an entry has changed.
+ */
+static void ext2_xattr_rehash(struct ext2_xattr_header *header,
+ struct ext2_xattr_entry *entry)
+{
+ struct ext2_xattr_entry *here;
+ __u32 hash = 0;
+
+ ext2_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 = EXT2_XATTR_NEXT(here);
+ }
+ header->h_hash = cpu_to_le32(hash);
+}
+
+#undef BLOCK_HASH_SHIFT
+
+int __init
+init_ext2_xattr(void)
+{
+ ext2_xattr_cache = mb_cache_create("ext2_xattr", NULL,
+ sizeof(struct mb_cache_entry) +
+ sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 61);
+ if (!ext2_xattr_cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void
+exit_ext2_xattr(void)
+{
+ mb_cache_destroy(ext2_xattr_cache);
+}
+
+#else /* CONFIG_EXT2_FS_XATTR_SHARING */
+
+int __init
+init_ext2_xattr(void)
+{
+ return 0;
+}
+
+#endif /* CONFIG_EXT2_FS_XATTR_SHARING */
Index: linux-2.4.29/fs/ext2/xattr_trusted.c
===================================================================
--- /dev/null
+++ linux-2.4.29/fs/ext2/xattr_trusted.c
@@ -0,0 +1,69 @@
+/*
+ * linux/fs/ext2/xattr_trusted.c
+ * Handler for trusted extended attributes.
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher,
+ */
+
+#include
+#include
+#include
+#include
+
+#define XATTR_TRUSTED_PREFIX "trusted."
+
+static size_t
+ext2_xattr_trusted_list(char *list, struct inode *inode,
+ const char *name, int name_len)
+{
+ const int prefix_len = sizeof(XATTR_TRUSTED_PREFIX)-1;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return 0;
+
+ if (list) {
+ memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
+ memcpy(list+prefix_len, name, name_len);
+ list[prefix_len + name_len] = '\0';
+ }
+ return prefix_len + name_len + 1;
+}
+
+static int
+ext2_xattr_trusted_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ext2_xattr_get(inode, EXT2_XATTR_INDEX_TRUSTED, name,
+ buffer, size);
+}
+
+static int
+ext2_xattr_trusted_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ext2_xattr_set(inode, EXT2_XATTR_INDEX_TRUSTED, name,
+ value, size, flags);
+}
+
+struct ext2_xattr_handler ext2_xattr_trusted_handler = {
+ prefix: XATTR_TRUSTED_PREFIX,
+ list: ext2_xattr_trusted_list,
+ get: ext2_xattr_trusted_get,
+ set: ext2_xattr_trusted_set,
+};
+
+int __init
+init_ext2_xattr_trusted(void)
+{
+ return ext2_xattr_register(EXT2_XATTR_INDEX_TRUSTED,
+ &ext2_xattr_trusted_handler);
+}
Index: linux-2.4.29/fs/ext2/xattr_user.c
===================================================================
--- /dev/null
+++ linux-2.4.29/fs/ext2/xattr_user.c
@@ -0,0 +1,88 @@
+/*
+ * linux/fs/ext2/xattr_user.c
+ * Handler for extended user attributes.
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher,
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+# include
+#endif
+
+#define XATTR_USER_PREFIX "user."
+
+static size_t
+ext2_xattr_user_list(char *list, struct inode *inode,
+ const char *name, int name_len)
+{
+ const int prefix_len = sizeof(XATTR_USER_PREFIX)-1;
+
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return 0;
+
+ if (list) {
+ memcpy(list, XATTR_USER_PREFIX, prefix_len);
+ memcpy(list+prefix_len, name, name_len);
+ list[prefix_len + name_len] = '\0';
+ }
+ return prefix_len + name_len + 1;
+}
+
+static int
+ext2_xattr_user_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ int error;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return -EOPNOTSUPP;
+ error = permission(inode, MAY_READ);
+ if (error)
+ return error;
+
+ return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name,
+ buffer, size);
+}
+
+static int
+ext2_xattr_user_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ int error;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return -EOPNOTSUPP;
+ if (!S_ISREG(inode->i_mode) &&
+ (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
+ return -EPERM;
+ error = permission(inode, MAY_WRITE);
+ if (error)
+ return error;
+
+ return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name,
+ value, size, flags);
+}
+
+struct ext2_xattr_handler ext2_xattr_user_handler = {
+ prefix: XATTR_USER_PREFIX,
+ list: ext2_xattr_user_list,
+ get: ext2_xattr_user_get,
+ set: ext2_xattr_user_set,
+};
+
+int __init
+init_ext2_xattr_user(void)
+{
+ return ext2_xattr_register(EXT2_XATTR_INDEX_USER,
+ &ext2_xattr_user_handler);
+}
Index: linux-2.4.29/fs/ext3/Makefile
===================================================================
--- linux-2.4.29.orig/fs/ext3/Makefile
+++ linux-2.4.29/fs/ext3/Makefile
@@ -1,5 +1,5 @@
#
-# Makefile for the linux ext2-filesystem routines.
+# Makefile for the linux ext3-filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
@@ -13,4 +13,9 @@ obj-y := balloc.o bitmap.o dir.o file
ioctl.o namei.o super.o symlink.o
obj-m := $(O_TARGET)
+export-objs += xattr.o
+obj-$(CONFIG_EXT3_FS_XATTR) += xattr.o
+obj-$(CONFIG_EXT3_FS_XATTR_USER) += xattr_user.o
+obj-$(CONFIG_EXT3_FS_XATTR_TRUSTED) += xattr_trusted.o
+
include $(TOPDIR)/Rules.make
Index: linux-2.4.29/fs/ext3/file.c
===================================================================
--- linux-2.4.29.orig/fs/ext3/file.c
+++ linux-2.4.29/fs/ext3/file.c
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
@@ -124,5 +125,9 @@ struct file_operations ext3_file_operati
struct inode_operations ext3_file_inode_operations = {
truncate: ext3_truncate, /* BKL held */
setattr: ext3_setattr, /* BKL held */
+ setxattr: ext3_setxattr, /* BKL held */
+ getxattr: ext3_getxattr, /* BKL held */
+ listxattr: ext3_listxattr, /* BKL held */
+ removexattr: ext3_removexattr, /* BKL held */
};
Index: linux-2.4.29/fs/ext3/ialloc.c
===================================================================
--- linux-2.4.29.orig/fs/ext3/ialloc.c
+++ linux-2.4.29/fs/ext3/ialloc.c
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -216,6 +217,7 @@ void ext3_free_inode (handle_t *handle,
* as writing the quota to disk may need the lock as well.
*/
DQUOT_INIT(inode);
+ ext3_xattr_delete_inode(handle, inode);
DQUOT_FREE_INODE(inode);
DQUOT_DROP(inode);
@@ -509,6 +511,10 @@ repeat:
inode->u.ext3_i.i_state = EXT3_STATE_NEW;
err = ext3_mark_inode_dirty(handle, inode);
if (err) goto fail;
+
+#ifdef CONFIG_EXT3_FS_XATTR
+ init_rwsem(&inode->u.ext3_i.xattr_sem);
+#endif
unlock_super (sb);
if(DQUOT_ALLOC_INODE(inode)) {
Index: linux-2.4.29/fs/ext3/inode.c
===================================================================
--- linux-2.4.29.orig/fs/ext3/inode.c
+++ linux-2.4.29/fs/ext3/inode.c
@@ -60,7 +60,7 @@ static inline int ext3_inode_is_fast_sym
* still needs to be revoked.
*/
-static int ext3_forget(handle_t *handle, int is_metadata,
+int ext3_forget(handle_t *handle, int is_metadata,
struct inode *inode, struct buffer_head *bh,
int blocknr)
{
@@ -191,9 +191,7 @@ void ext3_delete_inode (struct inode * i
{
handle_t *handle;
- if (is_bad_inode(inode) ||
- inode->i_ino == EXT3_ACL_IDX_INO ||
- inode->i_ino == EXT3_ACL_DATA_INO)
+ if (is_bad_inode(inode))
goto no_delete;
lock_kernel();
@@ -2032,8 +2030,6 @@ int ext3_get_inode_loc (struct inode *in
struct ext3_group_desc * gdp;
if ((inode->i_ino != EXT3_ROOT_INO &&
- inode->i_ino != EXT3_ACL_IDX_INO &&
- inode->i_ino != EXT3_ACL_DATA_INO &&
inode->i_ino != EXT3_JOURNAL_INO &&
inode->i_ino < EXT3_FIRST_INO(inode->i_sb)) ||
inode->i_ino > le32_to_cpu(
@@ -2174,10 +2170,7 @@ void ext3_read_inode(struct inode * inod
inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block];
INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
- if (inode->i_ino == EXT3_ACL_IDX_INO ||
- inode->i_ino == EXT3_ACL_DATA_INO)
- /* Nothing to do */ ;
- else if (S_ISREG(inode->i_mode)) {
+ if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext3_file_inode_operations;
inode->i_fop = &ext3_file_operations;
inode->i_mapping->a_ops = &ext3_aops;
@@ -2188,14 +2181,19 @@ void ext3_read_inode(struct inode * inod
if (ext3_inode_is_fast_symlink(inode))
inode->i_op = &ext3_fast_symlink_inode_operations;
else {
- inode->i_op = &page_symlink_inode_operations;
+ inode->i_op = &ext3_symlink_inode_operations;
inode->i_mapping->a_ops = &ext3_aops;
}
- } else
+ } else {
+ inode->i_op = &ext3_special_inode_operations;
init_special_inode(inode, inode->i_mode,
le32_to_cpu(iloc.raw_inode->i_block[0]));
+ }
brelse(iloc.bh);
ext3_set_inode_flags(inode);
+#ifdef CONFIG_EXT3_FS_XATTR
+ init_rwsem(&inode->u.ext3_i.xattr_sem);
+#endif
return;
bad_inode:
Index: linux-2.4.29/fs/ext3/namei.c
===================================================================
--- linux-2.4.29.orig/fs/ext3/namei.c
+++ linux-2.4.29/fs/ext3/namei.c
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -515,7 +516,7 @@ static int ext3_mkdir(struct inode * dir
if (IS_SYNC(dir))
handle->h_sync = 1;
- inode = ext3_new_inode (handle, dir, S_IFDIR);
+ inode = ext3_new_inode (handle, dir, S_IFDIR | mode);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_stop;
@@ -523,7 +524,6 @@ static int ext3_mkdir(struct inode * dir
inode->i_op = &ext3_dir_inode_operations;
inode->i_fop = &ext3_dir_operations;
inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize;
- inode->i_blocks = 0;
dir_block = ext3_bread (handle, inode, 0, 1, &err);
if (!dir_block) {
inode->i_nlink--; /* is this nlink == 0? */
@@ -550,9 +550,6 @@ static int ext3_mkdir(struct inode * dir
BUFFER_TRACE(dir_block, "call ext3_journal_dirty_metadata");
ext3_journal_dirty_metadata(handle, dir_block);
brelse (dir_block);
- inode->i_mode = S_IFDIR | mode;
- if (dir->i_mode & S_ISGID)
- inode->i_mode |= S_ISGID;
ext3_mark_inode_dirty(handle, inode);
err = ext3_add_entry (handle, dentry, inode);
if (err)
@@ -918,7 +915,7 @@ static int ext3_symlink (struct inode *
goto out_stop;
if (l > sizeof (inode->u.ext3_i.i_data)) {
- inode->i_op = &page_symlink_inode_operations;
+ inode->i_op = &ext3_symlink_inode_operations;
inode->i_mapping->a_ops = &ext3_aops;
/*
* block_symlink() calls back into ext3_prepare/commit_write.
@@ -1121,4 +1118,16 @@ struct inode_operations ext3_dir_inode_o
rmdir: ext3_rmdir, /* BKL held */
mknod: ext3_mknod, /* BKL held */
rename: ext3_rename, /* BKL held */
+ setxattr: ext3_setxattr, /* BKL held */
+ getxattr: ext3_getxattr, /* BKL held */
+ listxattr: ext3_listxattr, /* BKL held */
+ removexattr: ext3_removexattr, /* BKL held */
};
+
+struct inode_operations ext3_special_inode_operations = {
+ setxattr: ext3_setxattr, /* BKL held */
+ getxattr: ext3_getxattr, /* BKL held */
+ listxattr: ext3_listxattr, /* BKL held */
+ removexattr: ext3_removexattr, /* BKL held */
+};
+
Index: linux-2.4.29/fs/ext3/super.c
===================================================================
--- linux-2.4.29.orig/fs/ext3/super.c
+++ linux-2.4.29/fs/ext3/super.c
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -406,6 +407,7 @@ void ext3_put_super (struct super_block
kdev_t j_dev = sbi->s_journal->j_dev;
int i;
+ ext3_xattr_put_super(sb);
journal_destroy(sbi->s_journal);
if (!(sb->s_flags & MS_RDONLY)) {
EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
@@ -504,6 +506,7 @@ static int parse_options (char * options
int is_remount)
{
unsigned long *mount_options = &sbi->s_mount_opt;
+
uid_t *resuid = &sbi->s_resuid;
gid_t *resgid = &sbi->s_resgid;
char * this_char;
@@ -516,6 +519,13 @@ static int parse_options (char * options
this_char = strtok (NULL, ",")) {
if ((value = strchr (this_char, '=')) != NULL)
*value++ = 0;
+#ifdef CONFIG_EXT3_FS_XATTR_USER
+ if (!strcmp (this_char, "user_xattr"))
+ set_opt (*mount_options, XATTR_USER);
+ else if (!strcmp (this_char, "nouser_xattr"))
+ clear_opt (*mount_options, XATTR_USER);
+ else
+#endif
if (!strcmp (this_char, "bsddf"))
clear_opt (*mount_options, MINIX_DF);
else if (!strcmp (this_char, "nouid32")) {
@@ -951,6 +961,12 @@ struct super_block * ext3_read_super (st
sbi->s_mount_opt = 0;
sbi->s_resuid = EXT3_DEF_RESUID;
sbi->s_resgid = EXT3_DEF_RESGID;
+
+ /* Default extended attribute flags */
+#ifdef CONFIG_EXT3_FS_XATTR_USER
+ /* set_opt(sbi->s_mount_opt, XATTR_USER); */
+#endif
+
if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0)) {
sb->s_dev = 0;
goto out_fail;
@@ -1832,16 +1848,28 @@ static DECLARE_FSTYPE_DEV(ext3_fs_type,
static int __init init_ext3_fs(void)
{
+ int error;
+
#ifdef CONFIG_QUOTA
init_dquot_operations(&ext3_qops);
old_write_dquot = ext3_qops.write_dquot;
ext3_qops.write_dquot = ext3_write_dquot;
#endif
- return register_filesystem(&ext3_fs_type);
+ error = init_ext3_xattr();
+ if (error)
+ return error;
+ error = init_ext3_xattr_user();
+ if (error)
+ return error;
+ error = init_ext3_xattr_trusted();
+ if (error)
+ return error;
+ return register_filesystem(&ext3_fs_type);
}
static void __exit exit_ext3_fs(void)
{
+ exit_ext3_xattr();
unregister_filesystem(&ext3_fs_type);
}
Index: linux-2.4.29/fs/ext3/symlink.c
===================================================================
--- linux-2.4.29.orig/fs/ext3/symlink.c
+++ linux-2.4.29/fs/ext3/symlink.c
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen)
{
@@ -33,7 +34,20 @@ static int ext3_follow_link(struct dentr
return vfs_follow_link(nd, s);
}
+struct inode_operations ext3_symlink_inode_operations = {
+ readlink: page_readlink, /* BKL not held. Don't need */
+ follow_link: page_follow_link, /* BKL not held. Don't need */
+ setxattr: ext3_setxattr, /* BKL held */
+ getxattr: ext3_getxattr, /* BKL held */
+ listxattr: ext3_listxattr, /* BKL held */
+ removexattr: ext3_removexattr, /* BKL held */
+};
+
struct inode_operations ext3_fast_symlink_inode_operations = {
readlink: ext3_readlink, /* BKL not held. Don't need */
follow_link: ext3_follow_link, /* BKL not held. Don't need */
+ setxattr: ext3_setxattr, /* BKL held */
+ getxattr: ext3_getxattr, /* BKL held */
+ listxattr: ext3_listxattr, /* BKL held */
+ removexattr: ext3_removexattr, /* BKL held */
};
Index: linux-2.4.29/fs/ext3/xattr.c
===================================================================
--- /dev/null
+++ linux-2.4.29/fs/ext3/xattr.c
@@ -0,0 +1,1188 @@
+/*
+ * linux/fs/ext3/xattr.c
+ *
+ * Copyright (C) 2001-2003 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 EXT3_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 EXT3_XATTR_PAD byte
+ * boundaries. No additional gaps are left between them.
+ *
+ * Locking strategy
+ * ----------------
+ * EXT3_I(inode)->i_file_acl is protected by EXT3_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,
+ * which avoids deadlocks.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define HDR(bh) ((struct ext3_xattr_header *)((bh)->b_data))
+#define ENTRY(ptr) ((struct ext3_xattr_entry *)(ptr))
+#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)
+#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+
+#ifdef EXT3_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 ext3_xattr_set_handle2(handle_t *, struct inode *,
+ struct buffer_head *,
+ struct ext3_xattr_header *);
+
+#ifdef CONFIG_EXT3_FS_XATTR_SHARING
+
+static int ext3_xattr_cache_insert(struct buffer_head *);
+static struct buffer_head *ext3_xattr_cache_find(struct inode *,
+ struct ext3_xattr_header *,
+ struct mb_cache_entry **);
+static void ext3_xattr_rehash(struct ext3_xattr_header *,
+ struct ext3_xattr_entry *);
+
+static struct mb_cache *ext3_xattr_cache;
+
+#else
+# define ext3_xattr_cache_insert(bh) 0
+# define ext3_xattr_cache_find(inode, header, pce) NULL
+# define ext3_xattr_rehash(header, entry) while(0) {}
+#endif
+
+struct ext3_xattr_handler *ext3_xattr_handlers[EXT3_XATTR_INDEX_MAX];
+rwlock_t ext3_handler_lock = RW_LOCK_UNLOCKED;
+
+int
+ext3_xattr_register(int name_index, struct ext3_xattr_handler *handler)
+{
+ int error = -EINVAL;
+
+ if (name_index > 0 && name_index <= EXT3_XATTR_INDEX_MAX) {
+ write_lock(&ext3_handler_lock);
+ if (!ext3_xattr_handlers[name_index-1]) {
+ ext3_xattr_handlers[name_index-1] = handler;
+ error = 0;
+ }
+ write_unlock(&ext3_handler_lock);
+ }
+ return error;
+}
+
+void
+ext3_xattr_unregister(int name_index, struct ext3_xattr_handler *handler)
+{
+ if (name_index > 0 || name_index <= EXT3_XATTR_INDEX_MAX) {
+ write_lock(&ext3_handler_lock);
+ ext3_xattr_handlers[name_index-1] = NULL;
+ write_unlock(&ext3_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 ext3_xattr_handler *
+ext3_xattr_resolve_name(const char **name)
+{
+ struct ext3_xattr_handler *handler = NULL;
+ int i;
+
+ if (!*name)
+ return NULL;
+ read_lock(&ext3_handler_lock);
+ for (i=0; iprefix);
+ if (n) {
+ handler = ext3_xattr_handlers[i];
+ *name = n;
+ break;
+ }
+ }
+ }
+ read_unlock(&ext3_handler_lock);
+ return handler;
+}
+
+static inline struct ext3_xattr_handler *
+ext3_xattr_handler(int name_index)
+{
+ struct ext3_xattr_handler *handler = NULL;
+ if (name_index > 0 && name_index <= EXT3_XATTR_INDEX_MAX) {
+ read_lock(&ext3_handler_lock);
+ handler = ext3_xattr_handlers[name_index-1];
+ read_unlock(&ext3_handler_lock);
+ }
+ return handler;
+}
+
+/*
+ * Inode operation getxattr()
+ *
+ * dentry->d_inode->i_sem: don't care
+ * BKL: held
+ */
+ssize_t
+ext3_getxattr(struct dentry *dentry, const char *name,
+ void *buffer, size_t size)
+{
+ struct ext3_xattr_handler *handler;
+ struct inode *inode = dentry->d_inode;
+
+ handler = ext3_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
+ * BKL: held
+ */
+ssize_t
+ext3_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+ return ext3_xattr_list(dentry->d_inode, buffer, size);
+}
+
+/*
+ * Inode operation setxattr()
+ *
+ * dentry->d_inode->i_sem: down
+ * BKL: held
+ */
+int
+ext3_setxattr(struct dentry *dentry, const char *name, void *value,
+ size_t size, int flags)
+{
+ struct ext3_xattr_handler *handler;
+ struct inode *inode = dentry->d_inode;
+
+ if (size == 0)
+ value = ""; /* empty EA, do not remove */
+ handler = ext3_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
+ * BKL: held
+ */
+int
+ext3_removexattr(struct dentry *dentry, const char *name)
+{
+ struct ext3_xattr_handler *handler;
+ struct inode *inode = dentry->d_inode;
+
+ handler = ext3_xattr_resolve_name(&name);
+ if (!handler)
+ return -EOPNOTSUPP;
+ return handler->set(inode, name, NULL, 0, XATTR_REPLACE);
+}
+
+/*
+ * ext3_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
+ext3_xattr_get(struct inode *inode, int name_index, const char *name,
+ void *buffer, size_t buffer_size)
+{
+ struct buffer_head *bh = NULL;
+ struct ext3_xattr_entry *entry;
+ size_t size, name_len;
+ 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(&EXT3_I(inode)->xattr_sem);
+ error = -ENODATA;
+ if (!EXT3_I(inode)->i_file_acl)
+ goto cleanup;
+ ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl);
+ bh = sb_bread(inode->i_sb, EXT3_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(EXT3_XATTR_MAGIC) ||
+ HDR(bh)->h_blocks != cpu_to_le32(1)) {
+bad_block: ext3_error(inode->i_sb, "ext3_xattr_get",
+ "inode %ld: bad block %d", inode->i_ino,
+ EXT3_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 ext3_xattr_entry *next =
+ EXT3_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 ext3_xattr_entry *next =
+ EXT3_XATTR_NEXT(entry);
+ if ((char *)next >= end)
+ goto bad_block;
+ entry = next;
+ }
+ if (ext3_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 (ext3_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(&EXT3_I(inode)->xattr_sem);
+
+ return error;
+}
+
+/*
+ * ext3_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
+ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
+{
+ struct buffer_head *bh = NULL;
+ struct ext3_xattr_entry *entry;
+ size_t size = 0;
+ char *buf, *end;
+ int error;
+
+ ea_idebug(inode, "buffer=%p, buffer_size=%ld",
+ buffer, (long)buffer_size);
+
+ down_read(&EXT3_I(inode)->xattr_sem);
+ error = 0;
+ if (!EXT3_I(inode)->i_file_acl)
+ goto cleanup;
+ ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl);
+ bh = sb_bread(inode->i_sb, EXT3_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(EXT3_XATTR_MAGIC) ||
+ HDR(bh)->h_blocks != cpu_to_le32(1)) {
+bad_block: ext3_error(inode->i_sb, "ext3_xattr_list",
+ "inode %ld: bad block %d", inode->i_ino,
+ EXT3_I(inode)->i_file_acl);
+ error = -EIO;
+ goto cleanup;
+ }
+ /* compute the size required for the list of attribute names */
+ for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
+ entry = EXT3_XATTR_NEXT(entry)) {
+ struct ext3_xattr_handler *handler;
+ struct ext3_xattr_entry *next =
+ EXT3_XATTR_NEXT(entry);
+ if ((char *)next >= end)
+ goto bad_block;
+
+ handler = ext3_xattr_handler(entry->e_name_index);
+ if (handler)
+ size += handler->list(NULL, inode, entry->e_name,
+ entry->e_name_len);
+ }
+
+ if (ext3_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 = EXT3_XATTR_NEXT(entry)) {
+ struct ext3_xattr_handler *handler;
+
+ handler = ext3_xattr_handler(entry->e_name_index);
+ if (handler)
+ buf += handler->list(buf, inode, entry->e_name,
+ entry->e_name_len);
+ }
+ error = size;
+
+cleanup:
+ brelse(bh);
+ up_read(&EXT3_I(inode)->xattr_sem);
+
+ return error;
+}
+
+/*
+ * If the EXT3_FEATURE_COMPAT_EXT_ATTR feature of this file system is
+ * not set, set it.
+ */
+static void ext3_xattr_update_super_block(handle_t *handle,
+ struct super_block *sb)
+{
+ if (EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_EXT_ATTR))
+ return;
+
+ lock_super(sb);
+ if (ext3_journal_get_write_access(handle, EXT3_SB(sb)->s_sbh) == 0) {
+ EXT3_SB(sb)->s_es->s_feature_compat |=
+ cpu_to_le32(EXT3_FEATURE_COMPAT_EXT_ATTR);
+ sb->s_dirt = 1;
+ ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh);
+ }
+ unlock_super(sb);
+}
+
+/*
+ * Release the xattr block BH: If the reference count is > 1, decrement
+ * it; otherwise free the block.
+ */
+static void
+ext3_xattr_release_block(handle_t *handle, struct inode *inode,
+ struct buffer_head *bh)
+{
+ struct mb_cache_entry *ce;
+
+ ce = mb_cache_entry_get(ext3_xattr_cache, bh->b_dev, bh->b_blocknr);
+ if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
+ ea_bdebug(bh, "freeing");
+ if (ce)
+ mb_cache_entry_free(ce);
+ ext3_free_blocks(handle, inode, bh->b_blocknr, 1);
+ get_bh(bh);
+ ext3_forget(handle, 1, inode, bh, bh->b_blocknr);
+ } else {
+ if (ext3_journal_get_write_access(handle, bh) == 0) {
+ lock_buffer(bh);
+ HDR(bh)->h_refcount = cpu_to_le32(
+ le32_to_cpu(HDR(bh)->h_refcount) - 1);
+ ext3_journal_dirty_metadata(handle, bh);
+ if (IS_SYNC(inode))
+ handle->h_sync = 1;
+ DQUOT_FREE_BLOCK(inode, 1);
+ unlock_buffer(bh);
+ ea_bdebug(bh, "releasing; refcount now=%d",
+ le32_to_cpu(HDR(bh)->h_refcount));
+ }
+ if (ce)
+ mb_cache_entry_release(ce);
+ }
+}
+
+/*
+ * ext3_xattr_set_handle()
+ *
+ * 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
+ext3_xattr_set_handle(handle_t *handle, 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 ext3_xattr_header *header = NULL;
+ struct ext3_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 (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;
+ down_write(&EXT3_I(inode)->xattr_sem);
+ if (EXT3_I(inode)->i_file_acl) {
+ /* The inode already has an extended attribute block. */
+ bh = sb_bread(sb, EXT3_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(EXT3_XATTR_MAGIC) ||
+ header->h_blocks != cpu_to_le32(1)) {
+bad_block: ext3_error(sb, "ext3_xattr_set",
+ "inode %ld: bad block %d", inode->i_ino,
+ EXT3_I(inode)->i_file_acl);
+ error = -EIO;
+ goto cleanup;
+ }
+ /* Find the named attribute. */
+ here = FIRST_ENTRY(bh);
+ while (!IS_LAST_ENTRY(here)) {
+ struct ext3_xattr_entry *next = EXT3_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 ext3_xattr_entry *next = EXT3_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 ext3_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 += EXT3_XATTR_SIZE(size);
+ }
+ free += EXT3_XATTR_LEN(name_len);
+ }
+ error = -ENOSPC;
+ if (free < EXT3_XATTR_LEN(name_len) + EXT3_XATTR_SIZE(value_len))
+ goto cleanup;
+
+ /* Here we know that we can set the new attribute. */
+
+ if (header) {
+ struct mb_cache_entry *ce;
+
+ ce = mb_cache_entry_get(ext3_xattr_cache, bh->b_dev,
+ bh->b_blocknr);
+ if (header->h_refcount == cpu_to_le32(1)) {
+ if (ce)
+ mb_cache_entry_free(ce);
+ ea_bdebug(bh, "modifying in-place");
+ error = ext3_journal_get_write_access(handle, bh);
+ if (error)
+ goto cleanup;
+ lock_buffer(bh);
+ /* keep the buffer locked while modifying it. */
+ } else {
+ int offset;
+
+ if (ce)
+ mb_cache_entry_release(ce);
+ 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 {
+ /* 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(EXT3_XATTR_MAGIC);
+ header->h_blocks = header->h_refcount = cpu_to_le32(1);
+ last = here = ENTRY(header+1);
+ }
+
+ /* Iff we are modifying the block in-place, bh is locked here. */
+
+ if (not_found) {
+ /* Insert the new name. */
+ int size = EXT3_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 {
+ 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 = EXT3_XATTR_SIZE(
+ le32_to_cpu(here->e_value_size));
+
+ if (size == EXT3_XATTR_SIZE(value_len)) {
+ /* The old and the new value have the same
+ size. Just replace. */
+ here->e_value_size = cpu_to_le32(value_len);
+ memset(val + size - EXT3_XATTR_PAD, 0,
+ EXT3_XATTR_PAD); /* Clear pad bytes. */
+ memcpy(val, value, value_len);
+ goto skip_replace;
+ }
+
+ /* Remove the old value. */
+ 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 = EXT3_XATTR_NEXT(last);
+ }
+ }
+ if (value == NULL) {
+ /* Remove the old name. */
+ int size = EXT3_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 = EXT3_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 - EXT3_XATTR_PAD, 0,
+ EXT3_XATTR_PAD); /* Clear the pad bytes. */
+ memcpy(val, value, value_len);
+ }
+ }
+
+skip_replace:
+ if (!IS_LAST_ENTRY(ENTRY(header+1)))
+ ext3_xattr_rehash(header, here);
+ if (bh && header == HDR(bh)) {
+ /* we were modifying in-place. */
+ unlock_buffer(bh);
+ error = ext3_journal_dirty_metadata(handle, bh);
+ if (error)
+ goto cleanup;
+ }
+ error = ext3_xattr_set_handle2(handle, inode, bh,
+ IS_LAST_ENTRY(ENTRY(header+1)) ?
+ NULL : header);
+
+cleanup:
+ brelse(bh);
+ if (!(bh && header == HDR(bh)))
+ kfree(header);
+ up_write(&EXT3_I(inode)->xattr_sem);
+
+ return error;
+}
+
+/*
+ * Second half of ext3_xattr_set_handle(): Update the file system.
+ */
+static int
+ext3_xattr_set_handle2(handle_t *handle, struct inode *inode,
+ struct buffer_head *old_bh, struct ext3_xattr_header *header)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *new_bh = NULL;
+ struct mb_cache_entry *ce = NULL;
+ int error;
+
+ if (header) {
+ new_bh = ext3_xattr_cache_find(inode, header, &ce);
+ if (new_bh) {
+ /* We found an identical block in the cache. */
+ if (new_bh == old_bh)
+ ea_bdebug(new_bh, "keeping");
+ else {
+ /* The old block is released after updating
+ the inode. */
+ error = -EDQUOT;
+ if (DQUOT_ALLOC_BLOCK(inode, 1))
+ goto cleanup;
+ error = ext3_journal_get_write_access(handle,
+ new_bh);
+ if (error)
+ goto cleanup;
+ lock_buffer(new_bh);
+ HDR(new_bh)->h_refcount = cpu_to_le32(1 +
+ le32_to_cpu(HDR(new_bh)->h_refcount));
+ ea_bdebug(new_bh, "reusing; refcount now=%d",
+ le32_to_cpu(HDR(new_bh)->h_refcount));
+ unlock_buffer(new_bh);
+ error = ext3_journal_dirty_metadata(handle,
+ new_bh);
+ if (error)
+ goto cleanup;
+ }
+ mb_cache_entry_release(ce);
+ ce = NULL;
+ } else if (old_bh && header == HDR(old_bh)) {
+ /* We were modifying this block in-place. */
+ ea_bdebug(bs->bh, "keeping this block");
+ new_bh = old_bh;
+ get_bh(new_bh);
+ ext3_xattr_cache_insert(new_bh);
+ } else {
+ /* We need to allocate a new block */
+ int goal = le32_to_cpu(EXT3_SB(inode->i_sb)->s_es->
+ s_first_data_block) +
+ EXT3_I(inode)->i_block_group *
+ EXT3_BLOCKS_PER_GROUP(inode->i_sb);
+ /* How can we enforce the allocation? */
+ int block = ext3_new_block(handle, inode, goal, 0, 0,
+ &error);
+ if (error)
+ goto cleanup;
+ ea_idebug(inode, "creating block %d", block);
+
+ new_bh = sb_getblk(sb, block);
+ if (!new_bh) {
+getblk_failed: ext3_free_blocks(handle, inode, block, 1);
+ error = -EIO;
+ goto cleanup;
+ }
+ lock_buffer(new_bh);
+ error = ext3_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);
+ ext3_xattr_cache_insert(new_bh);
+
+ ext3_xattr_update_super_block(handle, sb);
+ error = ext3_journal_dirty_metadata(handle, new_bh);
+ if (error)
+ goto cleanup;
+ }
+ }
+
+ /* Update the inode. */
+ EXT3_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
+ inode->i_ctime = CURRENT_TIME;
+ ext3_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 no longer using it,
+ * release the old block.
+ */
+ ext3_xattr_release_block(handle, inode, old_bh);
+ }
+
+cleanup:
+ if (ce)
+ mb_cache_entry_release(ce);
+ brelse(new_bh);
+
+ return error;
+}
+
+/*
+ * ext3_xattr_set()
+ *
+ * Like ext3_xattr_set_handle, but start from an inode. This extended
+ * attribute modification is a filesystem transaction by itself.
+ *
+ * Returns 0, or a negative error number on failure.
+ */
+int
+ext3_xattr_set(struct inode *inode, int name_index, const char *name,
+ const void *value, size_t value_len, int flags)
+{
+ handle_t *handle;
+ int error, error2;
+
+ lock_kernel();
+ handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS);
+ error = PTR_ERR(handle);
+ if (IS_ERR(handle))
+ goto cleanup;
+ error = ext3_xattr_set_handle(handle, inode, name_index, name,
+ value, value_len, flags);
+ error2 = ext3_journal_stop(handle, inode);
+ if (!error)
+ error = error2;
+
+cleanup:
+ unlock_kernel();
+ return error;
+}
+
+/*
+ * ext3_xattr_delete_inode()
+ *
+ * Free extended attribute resources associated with this inode. This
+ * is called immediately before an inode is freed.
+ */
+void
+ext3_xattr_delete_inode(handle_t *handle, struct inode *inode)
+{
+ struct buffer_head *bh = NULL;
+
+ if (!EXT3_I(inode)->i_file_acl)
+ goto cleanup;
+ bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl);
+ if (!bh) {
+ ext3_error(inode->i_sb, "ext3_xattr_delete_inode",
+ "inode %ld: block %d read error", inode->i_ino,
+ EXT3_I(inode)->i_file_acl);
+ goto cleanup;
+ }
+ ext3_xattr_release_block(handle, inode, bh);
+ EXT3_I(inode)->i_file_acl = 0;
+
+cleanup:
+ brelse(bh);
+}
+
+/*
+ * ext3_xattr_put_super()
+ *
+ * This is called when a file system is unmounted.
+ */
+void
+ext3_xattr_put_super(struct super_block *sb)
+{
+#ifdef CONFIG_EXT3_FS_XATTR_SHARING
+ mb_cache_shrink(ext3_xattr_cache, sb->s_dev);
+#endif
+}
+
+#ifdef CONFIG_EXT3_FS_XATTR_SHARING
+
+/*
+ * ext3_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
+ext3_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(ext3_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(&ext3_xattr_cache->c_entry_count));
+ error = 0;
+ }
+ } else {
+ ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash,
+ atomic_read(&ext3_xattr_cache->c_entry_count));
+ mb_cache_entry_release(ce);
+ }
+ return error;
+}
+
+/*
+ * ext3_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
+ext3_xattr_cmp(struct ext3_xattr_header *header1,
+ struct ext3_xattr_header *header2)
+{
+ struct ext3_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 = EXT3_XATTR_NEXT(entry1);
+ entry2 = EXT3_XATTR_NEXT(entry2);
+ }
+ if (!IS_LAST_ENTRY(entry2))
+ return 1;
+ return 0;
+}
+
+/*
+ * ext3_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 *
+ext3_xattr_cache_find(struct inode *inode, struct ext3_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(ext3_xattr_cache, 0, inode->i_dev, 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) {
+ ext3_error(inode->i_sb, "ext3_xattr_cache_find",
+ "inode %ld: block %ld read error",
+ inode->i_ino, ce->e_block);
+ } else if (le32_to_cpu(HDR(bh)->h_refcount) >=
+ EXT3_XATTR_REFCOUNT_MAX) {
+ ea_idebug(inode, "block %ld refcount %d>=%d",
+ (unsigned long) ce->e_block,
+ le32_to_cpu(HDR(bh)->h_refcount),
+ EXT3_XATTR_REFCOUNT_MAX);
+ } else if (ext3_xattr_cmp(header, HDR(bh)) == 0) {
+ *pce = ce;
+ return bh;
+ }
+ brelse(bh);
+ ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash);
+ }
+ return NULL;
+}
+
+#define NAME_HASH_SHIFT 5
+#define VALUE_HASH_SHIFT 16
+
+/*
+ * ext3_xattr_hash_entry()
+ *
+ * Compute the hash of an extended attribute.
+ */
+static inline void ext3_xattr_hash_entry(struct ext3_xattr_header *header,
+ struct ext3_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) +
+ EXT3_XATTR_ROUND) >> EXT3_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
+
+/*
+ * ext3_xattr_rehash()
+ *
+ * Re-compute the extended attribute hash value after an entry has changed.
+ */
+static void ext3_xattr_rehash(struct ext3_xattr_header *header,
+ struct ext3_xattr_entry *entry)
+{
+ struct ext3_xattr_entry *here;
+ __u32 hash = 0;
+
+ ext3_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 = EXT3_XATTR_NEXT(here);
+ }
+ header->h_hash = cpu_to_le32(hash);
+}
+
+#undef BLOCK_HASH_SHIFT
+
+int __init
+init_ext3_xattr(void)
+{
+ ext3_xattr_cache = mb_cache_create("ext3_xattr", NULL,
+ sizeof(struct mb_cache_entry) +
+ sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 61);
+ if (!ext3_xattr_cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void
+exit_ext3_xattr(void)
+{
+ if (ext3_xattr_cache)
+ mb_cache_destroy(ext3_xattr_cache);
+ ext3_xattr_cache = NULL;
+}
+
+#else /* CONFIG_EXT3_FS_XATTR_SHARING */
+
+int __init
+init_ext3_xattr(void)
+{
+ return 0;
+}
+
+void
+exit_ext3_xattr(void)
+{
+}
+
+#endif /* CONFIG_EXT3_FS_XATTR_SHARING */
Index: linux-2.4.29/fs/ext3/xattr_trusted.c
===================================================================
--- /dev/null
+++ linux-2.4.29/fs/ext3/xattr_trusted.c
@@ -0,0 +1,69 @@
+/*
+ * linux/fs/ext3/xattr_trusted.c
+ * Handler for trusted extended attributes.
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher,
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#define XATTR_TRUSTED_PREFIX "trusted."
+
+static size_t
+ext3_xattr_trusted_list(char *list, struct inode *inode,
+ const char *name, int name_len)
+{
+ const int prefix_len = sizeof(XATTR_TRUSTED_PREFIX)-1;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return 0;
+
+ if (list) {
+ memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
+ memcpy(list+prefix_len, name, name_len);
+ list[prefix_len + name_len] = '\0';
+ }
+ return prefix_len + name_len + 1;
+}
+
+static int
+ext3_xattr_trusted_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ext3_xattr_get(inode, EXT3_XATTR_INDEX_TRUSTED, name,
+ buffer, size);
+}
+
+static int
+ext3_xattr_trusted_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ext3_xattr_set(inode, EXT3_XATTR_INDEX_TRUSTED, name,
+ value, size, flags);
+}
+
+struct ext3_xattr_handler ext3_xattr_trusted_handler = {
+ prefix: XATTR_TRUSTED_PREFIX,
+ list: ext3_xattr_trusted_list,
+ get: ext3_xattr_trusted_get,
+ set: ext3_xattr_trusted_set,
+};
+
+int __init
+init_ext3_xattr_trusted(void)
+{
+ return ext3_xattr_register(EXT3_XATTR_INDEX_TRUSTED,
+ &ext3_xattr_trusted_handler);
+}
Index: linux-2.4.29/fs/ext3/xattr_user.c
===================================================================
--- /dev/null
+++ linux-2.4.29/fs/ext3/xattr_user.c
@@ -0,0 +1,89 @@
+/*
+ * linux/fs/ext3/xattr_user.c
+ * Handler for extended user attributes.
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher,
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef CONFIG_EXT3_FS_POSIX_ACL
+# include
+#endif
+
+#define XATTR_USER_PREFIX "user."
+
+static size_t
+ext3_xattr_user_list(char *list, struct inode *inode,
+ const char *name, int name_len)
+{
+ const int prefix_len = sizeof(XATTR_USER_PREFIX)-1;
+
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return 0;
+
+ if (list) {
+ memcpy(list, XATTR_USER_PREFIX, prefix_len);
+ memcpy(list+prefix_len, name, name_len);
+ list[prefix_len + name_len] = '\0';
+ }
+ return prefix_len + name_len + 1;
+}
+
+static int
+ext3_xattr_user_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ int error;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return -EOPNOTSUPP;
+ error = permission(inode, MAY_READ);
+ if (error)
+ return error;
+
+ return ext3_xattr_get(inode, EXT3_XATTR_INDEX_USER, name,
+ buffer, size);
+}
+
+static int
+ext3_xattr_user_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ int error;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ if ( !S_ISREG(inode->i_mode) &&
+ (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
+ return -EPERM;
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return -EOPNOTSUPP;
+ error = permission(inode, MAY_WRITE);
+ if (error)
+ return error;
+
+ return ext3_xattr_set(inode, EXT3_XATTR_INDEX_USER, name,
+ value, size, flags);
+}
+
+struct ext3_xattr_handler ext3_xattr_user_handler = {
+ prefix: XATTR_USER_PREFIX,
+ list: ext3_xattr_user_list,
+ get: ext3_xattr_user_get,
+ set: ext3_xattr_user_set,
+};
+
+int __init
+init_ext3_xattr_user(void)
+{
+ return ext3_xattr_register(EXT3_XATTR_INDEX_USER,
+ &ext3_xattr_user_handler);
+}
Index: linux-2.4.29/fs/jfs/xattr.c
===================================================================
--- linux-2.4.29.orig/fs/jfs/xattr.c
+++ linux-2.4.29/fs/jfs/xattr.c
@@ -892,12 +892,18 @@ ssize_t __jfs_getxattr(struct inode *ino
ssize_t jfs_getxattr(struct dentry *dentry, const char *name, void *data,
size_t buf_size)
{
- return __jfs_getxattr(dentry->d_inode, name, data, buf_size);
+ int err;
+
+ down(&dentry->d_inode->i_sem);
+ err = __jfs_getxattr(dentry->d_inode, name, data, buf_size);
+ up(&dentry->d_inode->i_sem);
+
+ return err;
}
-ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size)
+static ssize_t __jfs_listxattr(struct inode *inode, char *data,
+ size_t buf_size)
{
- struct inode *inode = dentry->d_inode;
char *buffer;
ssize_t size = 0;
int xattr_size;
@@ -941,6 +947,17 @@ ssize_t jfs_listxattr(struct dentry * de
return size;
}
+ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size)
+{
+ int err;
+
+ down(&dentry->d_inode->i_sem);
+ err = __jfs_listxattr(dentry->d_inode, data, buf_size);
+ up(&dentry->d_inode->i_sem);
+
+ return err;
+}
+
int jfs_removexattr(struct dentry *dentry, const char *name)
{
return __jfs_setxattr(dentry->d_inode, name, 0, 0, XATTR_REPLACE);
Index: linux-2.4.29/fs/mbcache.c
===================================================================
--- /dev/null
+++ linux-2.4.29/fs/mbcache.c
@@ -0,0 +1,677 @@
+/*
+ * linux/fs/mbcache.c
+ * (C) 2001-2002 Andreas Gruenbacher,
+ */
+
+/*
+ * Filesystem Meta Information Block Cache (mbcache)
+ *
+ * The mbcache caches blocks of block devices that need to be located
+ * by their device/block number, as well as by other criteria (such
+ * as the block's contents).
+ *
+ * There can only be one cache entry in a cache per device and block number.
+ * Additional indexes need not be unique in this sense. The number of
+ * additional indexes (=other criteria) can be hardwired at compile time
+ * or specified at cache create time.
+ *
+ * Each cache entry is of fixed size. An entry may be `valid' or `invalid'
+ * in the cache. A valid entry is in the main hash tables of the cache,
+ * and may also be in the lru list. An invalid entry is not in any hashes
+ * or lists.
+ *
+ * A valid cache entry is only in the lru list if no handles refer to it.
+ * Invalid cache entries will be freed when the last handle to the cache
+ * entry is released. Entries that cannot be freed immediately are put
+ * back on the lru list.
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#ifdef MB_CACHE_DEBUG
+# define mb_debug(f...) do { \
+ printk(KERN_DEBUG f); \
+ printk("\n"); \
+ } while (0)
+#define mb_assert(c) do { if (!(c)) \
+ printk(KERN_ERR "assertion " #c " failed\n"); \
+ } while(0)
+#else
+# define mb_debug(f...) do { } while(0)
+# define mb_assert(c) do { } while(0)
+#endif
+#define mb_error(f...) do { \
+ printk(KERN_ERR f); \
+ printk("\n"); \
+ } while(0)
+
+#define MB_CACHE_WRITER ((unsigned short)~0U >> 1)
+
+DECLARE_WAIT_QUEUE_HEAD(mb_cache_queue);
+
+MODULE_AUTHOR("Andreas Gruenbacher ");
+MODULE_DESCRIPTION("Meta block cache (for extended attributes)");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+MODULE_LICENSE("GPL");
+#endif
+
+EXPORT_SYMBOL(mb_cache_create);
+EXPORT_SYMBOL(mb_cache_shrink);
+EXPORT_SYMBOL(mb_cache_destroy);
+EXPORT_SYMBOL(mb_cache_entry_alloc);
+EXPORT_SYMBOL(mb_cache_entry_insert);
+EXPORT_SYMBOL(mb_cache_entry_release);
+EXPORT_SYMBOL(mb_cache_entry_free);
+EXPORT_SYMBOL(mb_cache_entry_get);
+#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)
+EXPORT_SYMBOL(mb_cache_entry_find_first);
+EXPORT_SYMBOL(mb_cache_entry_find_next);
+#endif
+
+struct mb_cache {
+ struct list_head c_cache_list;
+ const char *c_name;
+ struct mb_cache_op c_op;
+ atomic_t c_entry_count;
+ int c_bucket_count;
+#ifndef MB_CACHE_INDEXES_COUNT
+ int c_indexes_count;
+#endif
+ kmem_cache_t *c_entry_cache;
+ struct list_head *c_block_hash;
+ struct list_head *c_indexes_hash[0];
+};
+
+
+/*
+ * Global data: list of all mbcache's, lru list, and a spinlock for
+ * accessing cache data structures on SMP machines. The lru list is
+ * global across all mbcaches.
+ */
+
+static LIST_HEAD(mb_cache_list);
+static LIST_HEAD(mb_cache_lru_list);
+static spinlock_t mb_cache_spinlock = SPIN_LOCK_UNLOCKED;
+
+static inline int
+mb_cache_indexes(struct mb_cache *cache)
+{
+#ifdef MB_CACHE_INDEXES_COUNT
+ return MB_CACHE_INDEXES_COUNT;
+#else
+ return cache->c_indexes_count;
+#endif
+}
+
+/*
+ * What the mbcache registers as to get shrunk dynamically.
+ */
+
+static void
+mb_cache_memory_pressure(int priority, unsigned int gfp_mask);
+
+static struct cache_definition mb_cache_definition = {
+ "mb_cache",
+ mb_cache_memory_pressure
+};
+
+
+static inline int
+__mb_cache_entry_is_hashed(struct mb_cache_entry *ce)
+{
+ return !list_empty(&ce->e_block_list);
+}
+
+
+static inline void
+__mb_cache_entry_unhash(struct mb_cache_entry *ce)
+{
+ int n;
+
+ if (__mb_cache_entry_is_hashed(ce)) {
+ list_del_init(&ce->e_block_list);
+ for (n=0; ne_cache); n++)
+ list_del(&ce->e_indexes[n].o_list);
+ }
+}
+
+
+static inline void
+__mb_cache_entry_forget(struct mb_cache_entry *ce, int gfp_mask)
+{
+ struct mb_cache *cache = ce->e_cache;
+
+ mb_assert(!(ce->e_used || ce->e_queued));
+ if (cache->c_op.free && cache->c_op.free(ce, gfp_mask)) {
+ /* free failed -- put back on the lru list
+ for freeing later. */
+ spin_lock(&mb_cache_spinlock);
+ list_add(&ce->e_lru_list, &mb_cache_lru_list);
+ spin_unlock(&mb_cache_spinlock);
+ } else {
+ kmem_cache_free(cache->c_entry_cache, ce);
+ atomic_dec(&cache->c_entry_count);
+ }
+}
+
+
+static inline void
+__mb_cache_entry_release_unlock(struct mb_cache_entry *ce)
+{
+ /* Wake up all processes queuing for this cache entry. */
+ if (ce->e_queued)
+ wake_up_all(&mb_cache_queue);
+ if (ce->e_used >= MB_CACHE_WRITER)
+ ce->e_used -= MB_CACHE_WRITER;
+ ce->e_used--;
+ if (!(ce->e_used || ce->e_queued)) {
+ if (!__mb_cache_entry_is_hashed(ce)) {
+ spin_unlock(&mb_cache_spinlock);
+ __mb_cache_entry_forget(ce, GFP_KERNEL);
+ return;
+ }
+ mb_assert(list_empty(&ce->e_lru_list));
+ list_add_tail(&ce->e_lru_list, &mb_cache_lru_list);
+ }
+ spin_unlock(&mb_cache_spinlock);
+}
+
+
+/*
+ * mb_cache_memory_pressure() memory pressure callback
+ *
+ * This function is called by the kernel memory management when memory
+ * gets low.
+ *
+ * @priority: Amount by which to shrink the cache (0 = highes priority)
+ * @gfp_mask: (ignored)
+ */
+static void
+mb_cache_memory_pressure(int priority, unsigned int gfp_mask)
+{
+ LIST_HEAD(free_list);
+ struct list_head *l, *ltmp;
+ int count = 0;
+
+ spin_lock(&mb_cache_spinlock);
+ list_for_each(l, &mb_cache_list) {
+ struct mb_cache *cache =
+ list_entry(l, struct mb_cache, c_cache_list);
+ mb_debug("cache %s (%d)", cache->c_name,
+ atomic_read(&cache->c_entry_count));
+ count += atomic_read(&cache->c_entry_count);
+ }
+ mb_debug("trying to free %d of %d entries",
+ count / (priority ? priority : 1), count);
+ if (priority)
+ count /= priority;
+ while (count-- && !list_empty(&mb_cache_lru_list)) {
+ struct mb_cache_entry *ce =
+ list_entry(mb_cache_lru_list.next,
+ struct mb_cache_entry, e_lru_list);
+ list_move_tail(&ce->e_lru_list, &free_list);
+ __mb_cache_entry_unhash(ce);
+ }
+ spin_unlock(&mb_cache_spinlock);
+ list_for_each_safe(l, ltmp, &free_list) {
+ __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
+ e_lru_list), gfp_mask);
+ }
+}
+
+
+/*
+ * mb_cache_create() create a new cache
+ *
+ * All entries in one cache are equal size. Cache entries may be from
+ * multiple devices. If this is the first mbcache created, registers
+ * the cache with kernel memory management. Returns NULL if no more
+ * memory was available.
+ *
+ * @name: name of the cache (informal)
+ * @cache_op: contains the callback called when freeing a cache entry
+ * @entry_size: The size of a cache entry, including
+ * struct mb_cache_entry
+ * @indexes_count: number of additional indexes in the cache. Must equal
+ * MB_CACHE_INDEXES_COUNT if the number of indexes is
+ * hardwired.
+ * @bucket_count: number of hash buckets
+ */
+struct mb_cache *
+mb_cache_create(const char *name, struct mb_cache_op *cache_op,
+ size_t entry_size, int indexes_count, int bucket_count)
+{
+ int m=0, n;
+ struct mb_cache *cache = NULL;
+
+ if(entry_size < sizeof(struct mb_cache_entry) +
+ indexes_count * sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]))
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+ cache = kmalloc(sizeof(struct mb_cache) +
+ indexes_count * sizeof(struct list_head), GFP_KERNEL);
+ if (!cache)
+ goto fail;
+ cache->c_name = name;
+ cache->c_op.free = NULL;
+ if (cache_op)
+ cache->c_op.free = cache_op->free;
+ atomic_set(&cache->c_entry_count, 0);
+ cache->c_bucket_count = bucket_count;
+#ifdef MB_CACHE_INDEXES_COUNT
+ mb_assert(indexes_count == MB_CACHE_INDEXES_COUNT);
+#else
+ cache->c_indexes_count = indexes_count;
+#endif
+ cache->c_block_hash = kmalloc(bucket_count * sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!cache->c_block_hash)
+ goto fail;
+ for (n=0; nc_block_hash[n]);
+ for (m=0; mc_indexes_hash[m] = kmalloc(bucket_count *
+ sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!cache->c_indexes_hash[m])
+ goto fail;
+ for (n=0; nc_indexes_hash[m][n]);
+ }
+ cache->c_entry_cache = kmem_cache_create(name, entry_size, 0,
+ 0 /*SLAB_POISON | SLAB_RED_ZONE*/, NULL, NULL);
+ if (!cache->c_entry_cache)
+ goto fail;
+
+ spin_lock(&mb_cache_spinlock);
+ list_add(&cache->c_cache_list, &mb_cache_list);
+ spin_unlock(&mb_cache_spinlock);
+ return cache;
+
+fail:
+ if (cache) {
+ while (--m >= 0)
+ kfree(cache->c_indexes_hash[m]);
+ if (cache->c_block_hash)
+ kfree(cache->c_block_hash);
+ kfree(cache);
+ }
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+
+/*
+ * mb_cache_shrink()
+ *
+ * Removes all cache entires of a device from the cache. All cache entries
+ * currently in use cannot be freed, and thus remain in the cache.
+ *
+ * @cache: which cache to shrink
+ * @dev: which device's cache entries to shrink
+ */
+void
+mb_cache_shrink(struct mb_cache *cache, kdev_t dev)
+{
+ LIST_HEAD(free_list);
+ struct list_head *l, *ltmp;
+
+ spin_lock(&mb_cache_spinlock);
+ list_for_each_safe(l, ltmp, &mb_cache_lru_list) {
+ struct mb_cache_entry *ce =
+ list_entry(l, struct mb_cache_entry, e_lru_list);
+ if (ce->e_dev == dev) {
+ list_move_tail(&ce->e_lru_list, &free_list);
+ __mb_cache_entry_unhash(ce);
+ }
+ }
+ spin_unlock(&mb_cache_spinlock);
+ list_for_each_safe(l, ltmp, &free_list) {
+ __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
+ e_lru_list), GFP_KERNEL);
+ }
+}
+
+
+/*
+ * mb_cache_destroy()
+ *
+ * Shrinks the cache to its minimum possible size (hopefully 0 entries),
+ * and then destroys it. If this was the last mbcache, un-registers the
+ * mbcache from kernel memory management.
+ */
+void
+mb_cache_destroy(struct mb_cache *cache)
+{
+ LIST_HEAD(free_list);
+ struct list_head *l, *ltmp;
+ int n;
+
+ spin_lock(&mb_cache_spinlock);
+ list_for_each_safe(l, ltmp, &mb_cache_lru_list) {
+ struct mb_cache_entry *ce =
+ list_entry(l, struct mb_cache_entry, e_lru_list);
+ if (ce->e_cache == cache) {
+ list_move_tail(&ce->e_lru_list, &free_list);
+ __mb_cache_entry_unhash(ce);
+ }
+ }
+ list_del(&cache->c_cache_list);
+ spin_unlock(&mb_cache_spinlock);
+ list_for_each_safe(l, ltmp, &free_list) {
+ __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry,
+ e_lru_list), GFP_KERNEL);
+ }
+
+ if (atomic_read(&cache->c_entry_count) > 0) {
+ mb_error("cache %s: %d orphaned entries",
+ cache->c_name,
+ atomic_read(&cache->c_entry_count));
+ }
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0))
+ /* We don't have kmem_cache_destroy() in 2.2.x */
+ kmem_cache_shrink(cache->c_entry_cache);
+#else
+ kmem_cache_destroy(cache->c_entry_cache);
+#endif
+ for (n=0; n < mb_cache_indexes(cache); n++)
+ kfree(cache->c_indexes_hash[n]);
+ kfree(cache->c_block_hash);
+ kfree(cache);
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * mb_cache_entry_alloc()
+ *
+ * Allocates a new cache entry. The new entry will not be valid initially,
+ * and thus cannot be looked up yet. It should be filled with data, and
+ * then inserted into the cache using mb_cache_entry_insert(). Returns NULL
+ * if no more memory was available.
+ */
+struct mb_cache_entry *
+mb_cache_entry_alloc(struct mb_cache *cache)
+{
+ struct mb_cache_entry *ce;
+
+ atomic_inc(&cache->c_entry_count);
+ ce = kmem_cache_alloc(cache->c_entry_cache, GFP_KERNEL);
+ if (ce) {
+ INIT_LIST_HEAD(&ce->e_lru_list);
+ INIT_LIST_HEAD(&ce->e_block_list);
+ ce->e_cache = cache;
+ ce->e_used = 1 + MB_CACHE_WRITER;
+ ce->e_queued = 0;
+ }
+ return ce;
+}
+
+
+/*
+ * mb_cache_entry_insert()
+ *
+ * Inserts an entry that was allocated using mb_cache_entry_alloc() into
+ * the cache. After this, the cache entry can be looked up, but is not yet
+ * in the lru list as the caller still holds a handle to it. Returns 0 on
+ * success, or -EBUSY if a cache entry for that device + inode exists
+ * already (this may happen after a failed lookup, if another process has
+ * inserted the same cache entry in the meantime).
+ *
+ * @dev: device the cache entry belongs to
+ * @block: block number
+ * @keys: array of additional keys. There must be indexes_count entries
+ * in the array (as specified when creating the cache).
+ */
+int
+mb_cache_entry_insert(struct mb_cache_entry *ce, kdev_t dev,
+ unsigned long block, unsigned int keys[])
+{
+ struct mb_cache *cache = ce->e_cache;
+ unsigned int bucket = (HASHDEV(dev) + block) % cache->c_bucket_count;
+ struct list_head *l;
+ int error = -EBUSY, n;
+
+ spin_lock(&mb_cache_spinlock);
+ list_for_each_prev(l, &cache->c_block_hash[bucket]) {
+ struct mb_cache_entry *ce =
+ list_entry(l, struct mb_cache_entry, e_block_list);
+ if (ce->e_dev == dev && ce->e_block == block)
+ goto out;
+ }
+ __mb_cache_entry_unhash(ce);
+ ce->e_dev = dev;
+ ce->e_block = block;
+ list_add(&ce->e_block_list, &cache->c_block_hash[bucket]);
+ for (n=0; ne_indexes[n].o_key = keys[n];
+ bucket = keys[n] % cache->c_bucket_count;
+ list_add(&ce->e_indexes[n].o_list,
+ &cache->c_indexes_hash[n][bucket]);
+ }
+ error = 0;
+out:
+ spin_unlock(&mb_cache_spinlock);
+ return error;
+}
+
+
+/*
+ * mb_cache_entry_release()
+ *
+ * Release a handle to a cache entry. When the last handle to a cache entry
+ * is released it is either freed (if it is invalid) or otherwise inserted
+ * in to the lru list.
+ */
+void
+mb_cache_entry_release(struct mb_cache_entry *ce)
+{
+ spin_lock(&mb_cache_spinlock);
+ __mb_cache_entry_release_unlock(ce);
+}
+
+
+/*
+ * mb_cache_entry_free()
+ */
+void
+mb_cache_entry_free(struct mb_cache_entry *ce)
+{
+ spin_lock(&mb_cache_spinlock);
+ mb_assert(list_empty(&ce->e_lru_list));
+ __mb_cache_entry_unhash(ce);
+ __mb_cache_entry_release_unlock(ce);
+}
+
+
+/*
+ * mb_cache_entry_get()
+ *
+ * Get a cache entry by device / block number. (There can only be one entry
+ * in the cache per device and block.) Returns NULL if no such cache entry
+ * exists. The returned cache entry is locked for exclusive access ("single
+ * writer").
+ */
+struct mb_cache_entry *
+mb_cache_entry_get(struct mb_cache *cache, kdev_t dev, unsigned long block)
+{
+ unsigned int bucket = (HASHDEV(dev) + block) % cache->c_bucket_count;
+ struct list_head *l;
+ struct mb_cache_entry *ce;
+
+ spin_lock(&mb_cache_spinlock);
+ list_for_each(l, &cache->c_block_hash[bucket]) {
+ ce = list_entry(l, struct mb_cache_entry, e_block_list);
+ if (ce->e_dev == dev && ce->e_block == block) {
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (!list_empty(&ce->e_lru_list))
+ list_del_init(&ce->e_lru_list);
+
+ while (ce->e_used > 0) {
+ ce->e_queued++;
+ add_wait_queue(&mb_cache_queue, &wait);
+ spin_unlock(&mb_cache_spinlock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&mb_cache_queue, &wait);
+ spin_lock(&mb_cache_spinlock);
+ ce->e_queued--;
+ }
+ ce->e_used += 1 + MB_CACHE_WRITER;
+
+ if (!__mb_cache_entry_is_hashed(ce)) {
+ __mb_cache_entry_release_unlock(ce);
+ return NULL;
+ }
+ goto cleanup;
+ }
+ }
+ ce = NULL;
+
+cleanup:
+ spin_unlock(&mb_cache_spinlock);
+ return ce;
+}
+
+#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)
+
+static struct mb_cache_entry *
+__mb_cache_entry_find(struct list_head *l, struct list_head *head,
+ int index, kdev_t dev, unsigned int key)
+{
+ while (l != head) {
+ struct mb_cache_entry *ce =
+ list_entry(l, struct mb_cache_entry,
+ e_indexes[index].o_list);
+ if (ce->e_dev == dev && ce->e_indexes[index].o_key == key) {
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (!list_empty(&ce->e_lru_list))
+ list_del_init(&ce->e_lru_list);
+
+ /* Incrementing before holding the lock gives readers
+ priority over writers. */
+ ce->e_used++;
+ while (ce->e_used >= MB_CACHE_WRITER) {
+ ce->e_queued++;
+ add_wait_queue(&mb_cache_queue, &wait);
+ spin_unlock(&mb_cache_spinlock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&mb_cache_queue, &wait);
+ spin_lock(&mb_cache_spinlock);
+ ce->e_queued--;
+ }
+
+ if (!__mb_cache_entry_is_hashed(ce)) {
+ __mb_cache_entry_release_unlock(ce);
+ spin_lock(&mb_cache_spinlock);
+ return ERR_PTR(-EAGAIN);
+ }
+ return ce;
+ }
+ l = l->next;
+ }
+ return NULL;
+}
+
+
+/*
+ * mb_cache_entry_find_first()
+ *
+ * Find the first cache entry on a given device with a certain key in
+ * an additional index. Additonal matches can be found with
+ * mb_cache_entry_find_next(). Returns NULL if no match was found.
+ *
+ * @cache: the cache to search
+ * @index: the number of the additonal index to search (0<=indexc_bucket_count;
+ struct list_head *l;
+ struct mb_cache_entry *ce;
+
+ mb_assert(index < mb_cache_indexes(cache));
+ spin_lock(&mb_cache_spinlock);
+ l = cache->c_indexes_hash[index][bucket].next;
+ ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket],
+ index, dev, key);
+ spin_unlock(&mb_cache_spinlock);
+ return ce;
+}
+
+
+/*
+ * mb_cache_entry_find_next()
+ *
+ * Find the next cache entry on a given device with a certain key in an
+ * additional index. Returns NULL if no match could be found. The previous
+ * entry is atomatically released, so that mb_cache_entry_find_next() can
+ * be called like this:
+ *
+ * entry = mb_cache_entry_find_first();
+ * while (entry) {
+ * ...
+ * entry = mb_cache_entry_find_next(entry, ...);
+ * }
+ *
+ * @prev: The previous match
+ * @index: the number of the additonal index to search (0<=indexe_cache;
+ unsigned int bucket = key % cache->c_bucket_count;
+ struct list_head *l;
+ struct mb_cache_entry *ce;
+
+ mb_assert(index < mb_cache_indexes(cache));
+ spin_lock(&mb_cache_spinlock);
+ l = prev->e_indexes[index].o_list.next;
+ ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket],
+ index, dev, key);
+ __mb_cache_entry_release_unlock(prev);
+ return ce;
+}
+
+#endif /* !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0) */
+
+static int __init init_mbcache(void)
+{
+ register_cache(&mb_cache_definition);
+ return 0;
+}
+
+static void __exit exit_mbcache(void)
+{
+ unregister_cache(&mb_cache_definition);
+}
+
+module_init(init_mbcache)
+module_exit(exit_mbcache)
+
Index: linux-2.4.29/fs/xattr.c
===================================================================
--- linux-2.4.29.orig/fs/xattr.c
+++ linux-2.4.29/fs/xattr.c
@@ -159,11 +159,9 @@ getxattr(struct dentry *d, char *name, v
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->getxattr) {
- down(&d->d_inode->i_sem);
lock_kernel();
error = d->d_inode->i_op->getxattr(d, kname, kvalue, size);
unlock_kernel();
- up(&d->d_inode->i_sem);
}
if (kvalue && error > 0)
@@ -230,11 +228,9 @@ listxattr(struct dentry *d, char *list,
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->listxattr) {
- down(&d->d_inode->i_sem);
lock_kernel();
error = d->d_inode->i_op->listxattr(d, klist, size);
unlock_kernel();
- up(&d->d_inode->i_sem);
}
if (klist && error > 0)
Index: linux-2.4.29/include/asm-arm/unistd.h
===================================================================
--- linux-2.4.29.orig/include/asm-arm/unistd.h
+++ linux-2.4.29/include/asm-arm/unistd.h
@@ -250,7 +250,6 @@
#define __NR_security (__NR_SYSCALL_BASE+223)
#define __NR_gettid (__NR_SYSCALL_BASE+224)
#define __NR_readahead (__NR_SYSCALL_BASE+225)
-#if 0 /* allocated in 2.5 */
#define __NR_setxattr (__NR_SYSCALL_BASE+226)
#define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
#define __NR_fsetxattr (__NR_SYSCALL_BASE+228)
@@ -263,7 +262,6 @@
#define __NR_removexattr (__NR_SYSCALL_BASE+235)
#define __NR_lremovexattr (__NR_SYSCALL_BASE+236)
#define __NR_fremovexattr (__NR_SYSCALL_BASE+237)
-#endif
#define __NR_tkill (__NR_SYSCALL_BASE+238)
#if 0 /* allocated in 2.5 */
#define __NR_sendfile64 (__NR_SYSCALL_BASE+239)
Index: linux-2.4.29/include/asm-s390/unistd.h
===================================================================
--- linux-2.4.29.orig/include/asm-s390/unistd.h
+++ linux-2.4.29/include/asm-s390/unistd.h
@@ -213,9 +213,19 @@
#define __NR_getdents64 220
#define __NR_fcntl64 221
#define __NR_readahead 222
-/*
- * Numbers 224-235 are reserved for posix acl
- */
+
+#define __NR_setxattr 224
+#define __NR_lsetxattr 225
+#define __NR_fsetxattr 226
+#define __NR_getxattr 227
+#define __NR_lgetxattr 228
+#define __NR_fgetxattr 229
+#define __NR_listxattr 230
+#define __NR_llistxattr 231
+#define __NR_flistxattr 232
+#define __NR_removexattr 233
+#define __NR_lremovexattr 234
+#define __NR_fremovexattr 235
#define __NR_gettid 236
#define __NR_tkill 237
Index: linux-2.4.29/include/asm-s390x/unistd.h
===================================================================
--- linux-2.4.29.orig/include/asm-s390x/unistd.h
+++ linux-2.4.29/include/asm-s390x/unistd.h
@@ -181,9 +181,19 @@
#define __NR_mincore 218
#define __NR_madvise 219
#define __NR_readahead 222
-/*
- * Numbers 224-235 are reserved for posix acl
- */
+
+#define __NR_setxattr 224
+#define __NR_lsetxattr 225
+#define __NR_fsetxattr 226
+#define __NR_getxattr 227
+#define __NR_lgetxattr 228
+#define __NR_fgetxattr 229
+#define __NR_listxattr 230
+#define __NR_llistxattr 231
+#define __NR_flistxattr 232
+#define __NR_removexattr 233
+#define __NR_lremovexattr 234
+#define __NR_fremovexattr 235
#define __NR_gettid 236
#define __NR_tkill 237
Index: linux-2.4.29/include/linux/cache_def.h
===================================================================
--- /dev/null
+++ linux-2.4.29/include/linux/cache_def.h
@@ -0,0 +1,15 @@
+/*
+ * linux/cache_def.h
+ * Handling of caches defined in drivers, filesystems, ...
+ *
+ * Copyright (C) 2002 by Andreas Gruenbacher,
+ */
+
+struct cache_definition {
+ const char *name;
+ void (*shrink)(int, unsigned int);
+ struct list_head link;
+};
+
+extern void register_cache(struct cache_definition *);
+extern void unregister_cache(struct cache_definition *);
Index: linux-2.4.29/include/linux/ext2_fs.h
===================================================================
--- linux-2.4.29.orig/include/linux/ext2_fs.h
+++ linux-2.4.29/include/linux/ext2_fs.h
@@ -57,8 +57,6 @@
*/
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
-#define EXT2_ACL_IDX_INO 3 /* ACL inode */
-#define EXT2_ACL_DATA_INO 4 /* ACL inode */
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
@@ -86,7 +84,6 @@
#else
# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#endif
-#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
#ifdef __KERNEL__
# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
@@ -121,28 +118,6 @@
#endif
/*
- * ACL structures
- */
-struct ext2_acl_header /* Header of Access Control Lists */
-{
- __u32 aclh_size;
- __u32 aclh_file_count;
- __u32 aclh_acle_count;
- __u32 aclh_first_acle;
-};
-
-struct ext2_acl_entry /* Access Control List Entry */
-{
- __u32 acle_size;
- __u16 acle_perms; /* Access permissions */
- __u16 acle_type; /* Type of entry */
- __u16 acle_tag; /* User or group identity */
- __u16 acle_pad1;
- __u32 acle_next; /* Pointer on next entry for the */
- /* same inode or on next free entry */
-};
-
-/*
* Structure of a blocks group descriptor
*/
struct ext2_group_desc
@@ -314,6 +289,7 @@ struct ext2_inode {
#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
+#define EXT2_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
@@ -410,6 +386,7 @@ struct ext2_super_block {
#ifdef __KERNEL__
#define EXT2_SB(sb) (&((sb)->u.ext2_sb))
+#define EXT2_I(inode) (&((inode)->u.ext2_i))
#else
/* Assume that user mode programs are passing in an ext2fs superblock, not
* a kernel struct super_block. This will allow us to call the feature-test
@@ -480,7 +457,7 @@ struct ext2_super_block {
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff
-#define EXT2_FEATURE_COMPAT_SUPP 0
+#define EXT2_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
EXT2_FEATURE_INCOMPAT_META_BG)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
@@ -650,8 +627,10 @@ extern struct address_space_operations e
/* namei.c */
extern struct inode_operations ext2_dir_inode_operations;
+extern struct inode_operations ext2_special_inode_operations;
/* symlink.c */
+extern struct inode_operations ext2_symlink_inode_operations;
extern struct inode_operations ext2_fast_symlink_inode_operations;
#endif /* __KERNEL__ */
Index: linux-2.4.29/include/linux/ext2_fs_i.h
===================================================================
--- linux-2.4.29.orig/include/linux/ext2_fs_i.h
+++ linux-2.4.29/include/linux/ext2_fs_i.h
@@ -35,6 +35,16 @@ struct ext2_inode_info {
__u32 i_prealloc_block;
__u32 i_prealloc_count;
__u32 i_dir_start_lookup;
+#ifdef CONFIG_EXT2_FS_XATTR
+ /*
+ * Extended attributes can be read independently of the main file
+ * data. Taking i_sem even when reading would cause contention
+ * between readers of EAs and writers of regular file data, so
+ * instead we synchronize on xattr_sem when reading or changing
+ * EAs.
+ */
+ struct rw_semaphore xattr_sem;
+#endif
};
/*
Index: linux-2.4.29/include/linux/ext2_xattr.h
===================================================================
--- /dev/null
+++ linux-2.4.29/include/linux/ext2_xattr.h
@@ -0,0 +1,167 @@
+/*
+ File: linux/ext2_xattr.h
+
+ On-disk format of extended attributes for the ext2 filesystem.
+
+ (C) 2001 Andreas Gruenbacher,
+*/
+
+#include
+#include
+#include
+
+/* Magic value in attribute blocks */
+#define EXT2_XATTR_MAGIC 0xEA020000
+
+/* Maximum number of references to one attribute block */
+#define EXT2_XATTR_REFCOUNT_MAX 1024
+
+/* Name indexes */
+#define EXT2_XATTR_INDEX_MAX 10
+#define EXT2_XATTR_INDEX_USER 1
+#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS 2
+#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT 3
+#define EXT2_XATTR_INDEX_TRUSTED 4
+
+struct ext2_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 ext2_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 EXT2_XATTR_PAD_BITS 2
+#define EXT2_XATTR_PAD (1<e_name_len)) )
+#define EXT2_XATTR_SIZE(size) \
+ (((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND)
+
+#ifdef __KERNEL__
+
+# ifdef CONFIG_EXT2_FS_XATTR
+
+struct ext2_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, const void *buffer,
+ size_t size, int flags);
+};
+
+extern int ext2_xattr_register(int, struct ext2_xattr_handler *);
+extern void ext2_xattr_unregister(int, struct ext2_xattr_handler *);
+
+extern int ext2_setxattr(struct dentry *, const char *, void *, size_t, int);
+extern ssize_t ext2_getxattr(struct dentry *, const char *, void *, size_t);
+extern ssize_t ext2_listxattr(struct dentry *, char *, size_t);
+extern int ext2_removexattr(struct dentry *, const char *);
+
+extern int ext2_xattr_get(struct inode *, int, const char *, void *, size_t);
+extern int ext2_xattr_list(struct inode *, char *, size_t);
+extern int ext2_xattr_set(struct inode *, int, const char *, const void *,
+ size_t, int);
+
+extern void ext2_xattr_delete_inode(struct inode *);
+extern void ext2_xattr_put_super(struct super_block *);
+
+extern int init_ext2_xattr(void) __init;
+extern void exit_ext2_xattr(void);
+
+# else /* CONFIG_EXT2_FS_XATTR */
+# define ext2_setxattr NULL
+# define ext2_getxattr NULL
+# define ext2_listxattr NULL
+# define ext2_removexattr NULL
+
+static inline int
+ext2_xattr_get(struct inode *inode, int name_index,
+ const char *name, void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int
+ext2_xattr_list(struct inode *inode, char *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int
+ext2_xattr_set(struct inode *inode, int name_index, const char *name,
+ const void *value, size_t size, int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void
+ext2_xattr_delete_inode(struct inode *inode)
+{
+}
+
+static inline void
+ext2_xattr_put_super(struct super_block *sb)
+{
+}
+
+static inline int
+init_ext2_xattr(void)
+{
+ return 0;
+}
+
+static inline void
+exit_ext2_xattr(void)
+{
+}
+
+# endif /* CONFIG_EXT2_FS_XATTR */
+
+# ifdef CONFIG_EXT2_FS_XATTR_USER
+
+extern int init_ext2_xattr_user(void) __init;
+
+# else /* CONFIG_EXT2_FS_XATTR_USER */
+
+static inline int
+init_ext2_xattr_user(void)
+{
+ return 0;
+}
+
+# endif /* CONFIG_EXT2_FS_XATTR_USER */
+
+# ifdef CONFIG_EXT2_FS_XATTR_TRUSTED
+
+extern int init_ext2_xattr_trusted(void) __init;
+
+# else /* CONFIG_EXT2_FS_XATTR_TRUSTED */
+
+static inline int
+init_ext2_xattr_trusted(void)
+{
+ return 0;
+}
+
+# endif /* CONFIG_EXT2_FS_XATTR_TRUSTED */
+
+#endif /* __KERNEL__ */
+
Index: linux-2.4.29/include/linux/ext3_fs.h
===================================================================
--- linux-2.4.29.orig/include/linux/ext3_fs.h
+++ linux-2.4.29/include/linux/ext3_fs.h
@@ -58,8 +58,6 @@
*/
#define EXT3_BAD_INO 1 /* Bad blocks inode */
#define EXT3_ROOT_INO 2 /* Root inode */
-#define EXT3_ACL_IDX_INO 3 /* ACL inode */
-#define EXT3_ACL_DATA_INO 4 /* ACL inode */
#define EXT3_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT3_UNDEL_DIR_INO 6 /* Undelete directory inode */
#define EXT3_RESIZE_INO 7 /* Reserved group descriptors inode */
@@ -89,7 +87,6 @@
#else
# define EXT3_BLOCK_SIZE(s) (EXT3_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#endif
-#define EXT3_ACLE_PER_BLOCK(s) (EXT3_BLOCK_SIZE(s) / sizeof (struct ext3_acl_entry))
#define EXT3_ADDR_PER_BLOCK(s) (EXT3_BLOCK_SIZE(s) / sizeof (__u32))
#ifdef __KERNEL__
# define EXT3_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
@@ -124,28 +121,6 @@
#endif
/*
- * ACL structures
- */
-struct ext3_acl_header /* Header of Access Control Lists */
-{
- __u32 aclh_size;
- __u32 aclh_file_count;
- __u32 aclh_acle_count;
- __u32 aclh_first_acle;
-};
-
-struct ext3_acl_entry /* Access Control List Entry */
-{
- __u32 acle_size;
- __u16 acle_perms; /* Access permissions */
- __u16 acle_type; /* Type of entry */
- __u16 acle_tag; /* User or group identity */
- __u16 acle_pad1;
- __u32 acle_next; /* Pointer on next entry for the */
- /* same inode or on next free entry */
-};
-
-/*
* Structure of a blocks group descriptor
*/
struct ext3_group_desc
@@ -339,6 +314,7 @@ struct ext3_inode {
#define EXT3_MOUNT_WRITEBACK_DATA 0x0C00 /* No data ordering */
#define EXT3_MOUNT_UPDATE_JOURNAL 0x1000 /* Update the journal format */
#define EXT3_MOUNT_NO_UID32 0x2000 /* Disable 32-bit UIDs */
+#define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */
/* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
#ifndef _LINUX_EXT2_FS_H
@@ -519,7 +495,7 @@ struct ext3_super_block {
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */
#define EXT3_FEATURE_INCOMPAT_META_BG 0x0010
-#define EXT3_FEATURE_COMPAT_SUPP 0
+#define EXT3_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
#define EXT3_FEATURE_INCOMPAT_SUPP (EXT3_FEATURE_INCOMPAT_FILETYPE| \
EXT3_FEATURE_INCOMPAT_RECOVER| \
EXT3_FEATURE_INCOMPAT_META_BG)
@@ -646,6 +622,7 @@ extern void ext3_check_inodes_bitmap (st
extern unsigned long ext3_count_free (struct buffer_head *, unsigned);
/* inode.c */
+extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int);
extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
@@ -713,8 +690,10 @@ extern struct address_space_operations e
/* namei.c */
extern struct inode_operations ext3_dir_inode_operations;
+extern struct inode_operations ext3_special_inode_operations;
/* symlink.c */
+extern struct inode_operations ext3_symlink_inode_operations;
extern struct inode_operations ext3_fast_symlink_inode_operations;
Index: linux-2.4.29/include/linux/ext3_fs_i.h
===================================================================
--- linux-2.4.29.orig/include/linux/ext3_fs_i.h
+++ linux-2.4.29/include/linux/ext3_fs_i.h
@@ -42,6 +42,16 @@ struct ext3_inode_info {
__u32 i_prealloc_count;
#endif
__u32 i_dir_start_lookup;
+#ifdef CONFIG_EXT3_FS_XATTR
+ /*
+ * Extended attributes can be read independently of the main file
+ * data. Taking i_sem even when reading would cause contention
+ * between readers of EAs and writers of regular file data, so
+ * instead we synchronize on xattr_sem when reading or changing
+ * EAs.
+ */
+ struct rw_semaphore xattr_sem;
+#endif
struct list_head i_orphan; /* unlinked but open inodes */
Index: linux-2.4.29/include/linux/ext3_jbd.h
===================================================================
--- linux-2.4.29.orig/include/linux/ext3_jbd.h
+++ linux-2.4.29/include/linux/ext3_jbd.h
@@ -30,13 +30,19 @@
#define EXT3_SINGLEDATA_TRANS_BLOCKS 8U
+/* Extended attributes may touch two data buffers, two bitmap buffers,
+ * and two group and summaries. */
+
+#define EXT3_XATTR_TRANS_BLOCKS 8
+
/* Define the minimum size for a transaction which modifies data. This
* needs to take into account the fact that we may end up modifying two
* quota files too (one for the group, one for the user quota). The
* superblock only gets updated once, of course, so don't bother
* counting that again for the quota updates. */
-#define EXT3_DATA_TRANS_BLOCKS (3 * EXT3_SINGLEDATA_TRANS_BLOCKS - 2)
+#define EXT3_DATA_TRANS_BLOCKS (3 * EXT3_SINGLEDATA_TRANS_BLOCKS + \
+ EXT3_XATTR_TRANS_BLOCKS - 2)
extern int ext3_writepage_trans_blocks(struct inode *inode);
Index: linux-2.4.29/include/linux/ext3_xattr.h
===================================================================
--- /dev/null
+++ linux-2.4.29/include/linux/ext3_xattr.h
@@ -0,0 +1,169 @@
+/*
+ File: linux/ext3_xattr.h
+
+ On-disk format of extended attributes for the ext3 filesystem.
+
+ (C) 2001 Andreas Gruenbacher,
+*/
+
+#include
+#include
+#include
+
+/* Magic value in attribute blocks */
+#define EXT3_XATTR_MAGIC 0xEA020000
+
+/* Maximum number of references to one attribute block */
+#define EXT3_XATTR_REFCOUNT_MAX 1024
+
+/* Name indexes */
+#define EXT3_XATTR_INDEX_MAX 10
+#define EXT3_XATTR_INDEX_USER 1
+#define EXT3_XATTR_INDEX_POSIX_ACL_ACCESS 2
+#define EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT 3
+#define EXT3_XATTR_INDEX_TRUSTED 4
+
+struct ext3_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 ext3_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 EXT3_XATTR_PAD_BITS 2
+#define EXT3_XATTR_PAD (1<e_name_len)) )
+#define EXT3_XATTR_SIZE(size) \
+ (((size) + EXT3_XATTR_ROUND) & ~EXT3_XATTR_ROUND)
+
+#ifdef __KERNEL__
+
+# ifdef CONFIG_EXT3_FS_XATTR
+
+struct ext3_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, const void *buffer,
+ size_t size, int flags);
+};
+
+extern int ext3_xattr_register(int, struct ext3_xattr_handler *);
+extern void ext3_xattr_unregister(int, struct ext3_xattr_handler *);
+
+extern int ext3_setxattr(struct dentry *, const char *, void *, size_t, int);
+extern ssize_t ext3_getxattr(struct dentry *, const char *, void *, size_t);
+extern ssize_t ext3_listxattr(struct dentry *, char *, size_t);
+extern int ext3_removexattr(struct dentry *, const char *);
+
+extern int ext3_xattr_get(struct inode *, int, const char *, void *, size_t);
+extern int ext3_xattr_list(struct inode *, char *, size_t);
+extern int ext3_xattr_set_handle(handle_t *handle, struct inode *, int,
+ const char *, const void *, size_t, int);
+extern int ext3_xattr_set(struct inode *, int, const char *, const void *,
+ size_t, int);
+
+extern void ext3_xattr_delete_inode(handle_t *, struct inode *);
+extern void ext3_xattr_put_super(struct super_block *);
+
+extern int init_ext3_xattr(void) __init;
+extern void exit_ext3_xattr(void);
+
+# else /* CONFIG_EXT3_FS_XATTR */
+# define ext3_setxattr NULL
+# define ext3_getxattr NULL
+# define ext3_listxattr NULL
+# define ext3_removexattr NULL
+
+static inline int
+ext3_xattr_get(struct inode *inode, int name_index, const char *name,
+ void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int
+ext3_xattr_list(struct inode *inode, void *buffer, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int
+ext3_xattr_set(handle_t *handle, struct inode *inode, int name_index,
+ const char *name, const void *value, size_t size)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void
+ext3_xattr_delete_inode(handle_t *handle, struct inode *inode)
+{
+}
+
+static inline void
+ext3_xattr_put_super(struct super_block *sb)
+{
+}
+
+static inline int
+init_ext3_xattr(void)
+{
+ return 0;
+}
+
+static inline void
+exit_ext3_xattr(void)
+{
+}
+
+# endif /* CONFIG_EXT3_FS_XATTR */
+
+# ifdef CONFIG_EXT3_FS_XATTR_USER
+
+extern int init_ext3_xattr_user(void) __init;
+
+# else /* CONFIG_EXT3_FS_XATTR_USER */
+
+static inline int
+init_ext3_xattr_user(void)
+{
+ return 0;
+}
+
+#endif /* CONFIG_EXT3_FS_XATTR_USER */
+
+# ifdef CONFIG_EXT3_FS_XATTR_TRUSTED
+
+extern int init_ext3_xattr_trusted(void) __init;
+
+# else /* CONFIG_EXT3_FS_XATTR_TRUSTED */
+
+static inline int
+init_ext3_xattr_trusted(void)
+{
+ return 0;
+}
+
+#endif /* CONFIG_EXT3_FS_XATTR_TRUSTED */
+
+#endif /* __KERNEL__ */
+
Index: linux-2.4.29/include/linux/mbcache.h
===================================================================
--- /dev/null
+++ linux-2.4.29/include/linux/mbcache.h
@@ -0,0 +1,51 @@
+/*
+ File: linux/mbcache.h
+
+ (C) 2001 by Andreas Gruenbacher,
+*/
+
+/* Hardwire the number of additional indexes */
+#define MB_CACHE_INDEXES_COUNT 1
+
+struct mb_cache_entry;
+
+struct mb_cache_entry {
+ struct list_head e_lru_list;
+ struct mb_cache *e_cache;
+ unsigned short e_used;
+ unsigned short e_queued;
+ kdev_t e_dev;
+ unsigned long e_block;
+ struct list_head e_block_list;
+ struct {
+ struct list_head o_list;
+ unsigned int o_key;
+ } e_indexes[0];
+};
+
+struct mb_cache_op {
+ int (*free)(struct mb_cache_entry *, int);
+};
+
+/* Functions on caches */
+
+struct mb_cache * mb_cache_create(const char *, struct mb_cache_op *, size_t,
+ int, int);
+void mb_cache_shrink(struct mb_cache *, kdev_t);
+void mb_cache_destroy(struct mb_cache *);
+
+/* Functions on cache entries */
+
+struct mb_cache_entry *mb_cache_entry_alloc(struct mb_cache *);
+int mb_cache_entry_insert(struct mb_cache_entry *, kdev_t, unsigned long,
+ unsigned int[]);
+void mb_cache_entry_release(struct mb_cache_entry *);
+void mb_cache_entry_free(struct mb_cache_entry *);
+struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *, kdev_t,
+ unsigned long);
+#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0)
+struct mb_cache_entry *mb_cache_entry_find_first(struct mb_cache *cache, int,
+ kdev_t, unsigned int);
+struct mb_cache_entry *mb_cache_entry_find_next(struct mb_cache_entry *, int,
+ kdev_t, unsigned int);
+#endif
Index: linux-2.4.29/kernel/ksyms.c
===================================================================
--- linux-2.4.29.orig/kernel/ksyms.c
+++ linux-2.4.29/kernel/ksyms.c
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -109,6 +110,8 @@ EXPORT_SYMBOL(kmem_cache_shrink);
EXPORT_SYMBOL(kmem_cache_alloc);
EXPORT_SYMBOL(kmem_cache_free);
EXPORT_SYMBOL(kmem_cache_size);
+EXPORT_SYMBOL(register_cache);
+EXPORT_SYMBOL(unregister_cache);
EXPORT_SYMBOL(kmalloc);
EXPORT_SYMBOL(kfree);
EXPORT_SYMBOL(vfree);
Index: linux-2.4.29/mm/vmscan.c
===================================================================
--- linux-2.4.29.orig/mm/vmscan.c
+++ linux-2.4.29/mm/vmscan.c
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -86,6 +87,49 @@ int vm_vfs_scan_ratio = 6;
int vm_anon_lru = 0;
/*
+ * Handling of caches defined in drivers, filesystems, ...
+ *
+ * The cache definition contains a callback for shrinking
+ * the cache.
+ *
+ * The [un]register_cache() functions may only be called when
+ * the kernel lock is held. The shrink() functions are also
+ * called with the kernel lock held.
+ */
+static DECLARE_MUTEX(other_caches_sem);
+static LIST_HEAD(other_caches);
+
+void register_cache(struct cache_definition *cache)
+{
+ down(&other_caches_sem);
+ list_add(&cache->link, &other_caches);
+ up(&other_caches_sem);
+}
+
+void unregister_cache(struct cache_definition *cache)
+{
+ down(&other_caches_sem);
+ list_del(&cache->link);
+ up(&other_caches_sem);
+}
+
+static void shrink_other_caches(unsigned int priority, int gfp_mask)
+{
+ struct list_head *p;
+
+ if (down_trylock(&other_caches_sem))
+ return;
+
+ list_for_each_prev(p, &other_caches) {
+ struct cache_definition *cache =
+ list_entry(p, struct cache_definition, link);
+
+ cache->shrink(priority, gfp_mask);
+ }
+ up(&other_caches_sem);
+}
+
+/*
* The swap-out function returns 1 if it successfully
* scanned all the pages it was asked to (`count').
* It returns zero if it couldn't do anything,
@@ -544,6 +588,7 @@ page_mapped:
#ifdef CONFIG_QUOTA
shrink_dqcache_memory(vm_vfs_scan_ratio, gfp_mask);
#endif
+ shrink_other_caches(vm_vfs_scan_ratio, gfp_mask);
if (!*failed_swapout)
*failed_swapout = !swap_out(classzone);