diff -Naur linux-2.4.22-ppc-dev.orig/Documentation/Configure.help linux-2.4.22-ppc-dev/Documentation/Configure.help --- linux-2.4.22-ppc-dev.orig/Documentation/Configure.help 2009-08-07 06:34:57.000000000 +0200 +++ linux-2.4.22-ppc-dev/Documentation/Configure.help 2009-08-07 06:36:08.000000000 +0200 @@ -13047,6 +13047,20 @@ The module will be called isp16.o. If you want to compile it as a module, say M here and read . +Posix Access Control Lists +CONFIG_FS_POSIX_ACL + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you plan to use Access Control Lists, you may also need the + getfacl and setfacl utilities, along with some additional patches + from the website. + + If you don't know what Access Control Lists are, say N. + iSeries Virtual I/O CD Support CONFIG_VIOCD If you are running Linux on an IBM iSeries system and you want to @@ -15650,6 +15664,43 @@ 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). + + You need this for POSIX ACL support on ext2. + + 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. + +Ext2 POSIX Access Control Lists +CONFIG_EXT2_FS_POSIX_ACL + POSIX Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. This option enables + POSIX ACLs on ext2. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If unsure, say N. + Ext3 journalling file system support (EXPERIMENTAL) CONFIG_EXT3_FS This is the journalling version of the Second extended file system @@ -15682,6 +15733,43 @@ 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). + + You need this for POSIX ACL support on ext3. + + 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. + +Ext3 POSIX Access Control Lists +CONFIG_EXT3_FS_POSIX_ACL + POSIX Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. This option enables + POSIX ACLs on ext3. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + 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 @@ -16176,6 +16264,15 @@ If unsure, say N. +Solaris ACL RPCs +CONFIG_NFS_ACL + Allows manipulating POSIX Access Control Lists on NFS file systems + on NFS servers that implement the GETACL and SETACL extensions also + found in Solaris NFSv3. On Linux NFS servers the CONFIG_NFSD_ACL + option implements these extensions. + + In doubt say N. + Root file system on NFS CONFIG_ROOT_NFS If you want your Linux box to mount its whole root file system (the @@ -16220,6 +16317,15 @@ If you would like to include the NFSv3 server as well as the NFSv2 server, say Y here. If unsure, say Y. +Solaris ACL RPCs +CONFIG_NFSD_ACL + Allows manipulating POSIX Access Control Lists on exported file + systems. The client(s) must implement the GETACL and SETACL + extensions of Solaris NFSv3 in order to use these extensions. The + CONFIG_NFS_ACL option enables this. + + In doubt say N. + Provide NFS over TCP server support EXPERIMENTAL CONFIG_NFSD_TCP Enable NFS service over TCP connections. This the officially diff -Naur linux-2.4.22-ppc-dev.orig/Documentation/filesystems/Locking linux-2.4.22-ppc-dev/Documentation/filesystems/Locking --- linux-2.4.22-ppc-dev.orig/Documentation/filesystems/Locking 2009-08-07 06:24:46.000000000 +0200 +++ linux-2.4.22-ppc-dev/Documentation/filesystems/Locking 2009-08-07 06:36:08.000000000 +0200 @@ -69,8 +69,8 @@ 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. diff -Naur linux-2.4.22-ppc-dev.orig/arch/alpha/defconfig linux-2.4.22-ppc-dev/arch/alpha/defconfig --- linux-2.4.22-ppc-dev.orig/arch/alpha/defconfig 2009-08-07 06:24:18.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/alpha/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set CONFIG_ALPHA=y # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set diff -Naur linux-2.4.22-ppc-dev.orig/arch/alpha/kernel/entry.S linux-2.4.22-ppc-dev/arch/alpha/kernel/entry.S --- linux-2.4.22-ppc-dev.orig/arch/alpha/kernel/entry.S 2009-08-07 06:24:18.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/alpha/kernel/entry.S 2009-08-07 06:36:09.000000000 +0200 @@ -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 @@ .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) diff -Naur linux-2.4.22-ppc-dev.orig/arch/arm/defconfig linux-2.4.22-ppc-dev/arch/arm/defconfig --- linux-2.4.22-ppc-dev.orig/arch/arm/defconfig 2009-08-07 06:24:31.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/arm/defconfig 2009-08-07 06:36:08.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set CONFIG_ARM=y # CONFIG_EISA is not set # CONFIG_SBUS is not set diff -Naur linux-2.4.22-ppc-dev.orig/arch/arm/kernel/calls.S linux-2.4.22-ppc-dev/arch/arm/kernel/calls.S --- linux-2.4.22-ppc-dev.orig/arch/arm/kernel/calls.S 2009-08-07 06:24:31.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/arm/kernel/calls.S 2009-08-07 10:22:38.000000000 +0200 @@ -240,18 +240,18 @@ .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 */ diff -Naur linux-2.4.22-ppc-dev.orig/arch/i386/defconfig linux-2.4.22-ppc-dev/arch/i386/defconfig --- linux-2.4.22-ppc-dev.orig/arch/i386/defconfig 2009-08-07 06:24:17.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/i386/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set CONFIG_X86=y CONFIG_ISA=y # CONFIG_SBUS is not set diff -Naur linux-2.4.22-ppc-dev.orig/arch/ia64/defconfig linux-2.4.22-ppc-dev/arch/ia64/defconfig --- linux-2.4.22-ppc-dev.orig/arch/ia64/defconfig 2009-08-07 06:24:36.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ia64/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set # # Code maturity level options diff -Naur linux-2.4.22-ppc-dev.orig/arch/m68k/defconfig linux-2.4.22-ppc-dev/arch/m68k/defconfig --- linux-2.4.22-ppc-dev.orig/arch/m68k/defconfig 2009-08-07 06:24:27.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/m68k/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set CONFIG_UID16=y # diff -Naur linux-2.4.22-ppc-dev.orig/arch/mips/defconfig linux-2.4.22-ppc-dev/arch/mips/defconfig --- linux-2.4.22-ppc-dev.orig/arch/mips/defconfig 2009-08-07 06:24:21.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/mips/defconfig 2009-08-07 06:36:08.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set CONFIG_MIPS=y CONFIG_MIPS32=y # CONFIG_MIPS64 is not set diff -Naur linux-2.4.22-ppc-dev.orig/arch/mips64/defconfig linux-2.4.22-ppc-dev/arch/mips64/defconfig --- linux-2.4.22-ppc-dev.orig/arch/mips64/defconfig 2009-08-07 06:24:38.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/mips64/defconfig 2009-08-07 06:36:08.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set CONFIG_MIPS=y # CONFIG_MIPS32 is not set CONFIG_MIPS64=y diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc/defconfig linux-2.4.22-ppc-dev/arch/ppc/defconfig --- linux-2.4.22-ppc-dev.orig/arch/ppc/defconfig 2009-08-07 06:24:25.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc64/defconfig linux-2.4.22-ppc-dev/arch/ppc64/defconfig --- linux-2.4.22-ppc-dev.orig/arch/ppc64/defconfig 2009-08-07 06:34:34.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc64/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y diff -Naur linux-2.4.22-ppc-dev.orig/arch/ppc64/kernel/misc.S linux-2.4.22-ppc-dev/arch/ppc64/kernel/misc.S --- linux-2.4.22-ppc-dev.orig/arch/ppc64/kernel/misc.S 2009-08-07 06:24:16.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/ppc64/kernel/misc.S 2009-08-07 10:19:01.000000000 +0200 @@ -820,7 +820,20 @@ .llong .sys_futex #endif .llong .sys_perfmonctl /* Put this here for now ... */ - .rept NR_syscalls-222 + .llong .sys_ni_syscall + .llong .sys_setxattr + .llong .sys_lsetxattr /* 225 */ + .llong .sys_fsetxattr + .llong .sys_getxattr + .llong .sys_lgetxattr + .llong .sys_fgetxattr + .llong .sys_listxattr /* 230 */ + .llong .sys_llistxattr + .llong .sys_flistxattr + .llong .sys_removexattr + .llong .sys_lremovexattr + .llong .sys_fremovexattr /* 235 */ + .rept NR_syscalls-236 .llong .sys_ni_syscall .endr #endif @@ -1051,6 +1064,19 @@ .llong .sys_futex #endif .llong .sys_perfmonctl /* Put this here for now ... */ - .rept NR_syscalls-222 + .llong .sys_ni_syscall + .llong .sys_setxattr + .llong .sys_lsetxattr /* 225 */ + .llong .sys_fsetxattr + .llong .sys_getxattr + .llong .sys_lgetxattr + .llong .sys_fgetxattr + .llong .sys_listxattr /* 230 */ + .llong .sys_llistxattr + .llong .sys_flistxattr + .llong .sys_removexattr + .llong .sys_lremovexattr + .llong .sys_fremovexattr /* 235 */ + .rept NR_syscalls-236 .llong .sys_ni_syscall .endr diff -Naur linux-2.4.22-ppc-dev.orig/arch/s390/defconfig linux-2.4.22-ppc-dev/arch/s390/defconfig --- linux-2.4.22-ppc-dev.orig/arch/s390/defconfig 2009-08-07 06:24:40.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/s390/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set # CONFIG_ISA is not set # CONFIG_EISA is not set # CONFIG_MCA is not set diff -Naur linux-2.4.22-ppc-dev.orig/arch/s390/kernel/entry.S linux-2.4.22-ppc-dev/arch/s390/kernel/entry.S --- linux-2.4.22-ppc-dev.orig/arch/s390/kernel/entry.S 2009-08-07 06:24:40.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/s390/kernel/entry.S 2009-08-07 06:36:09.000000000 +0200 @@ -558,18 +558,18 @@ .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 diff -Naur linux-2.4.22-ppc-dev.orig/arch/s390x/defconfig linux-2.4.22-ppc-dev/arch/s390x/defconfig --- linux-2.4.22-ppc-dev.orig/arch/s390x/defconfig 2009-08-07 06:24:43.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/s390x/defconfig 2009-08-07 06:36:08.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set # CONFIG_ISA is not set # CONFIG_EISA is not set # CONFIG_MCA is not set diff -Naur linux-2.4.22-ppc-dev.orig/arch/s390x/kernel/entry.S linux-2.4.22-ppc-dev/arch/s390x/kernel/entry.S --- linux-2.4.22-ppc-dev.orig/arch/s390x/kernel/entry.S 2009-08-07 06:24:43.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/s390x/kernel/entry.S 2009-08-07 06:36:08.000000000 +0200 @@ -591,18 +591,18 @@ .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 diff -Naur linux-2.4.22-ppc-dev.orig/arch/s390x/kernel/wrapper32.S linux-2.4.22-ppc-dev/arch/s390x/kernel/wrapper32.S --- linux-2.4.22-ppc-dev.orig/arch/s390x/kernel/wrapper32.S 2009-08-07 06:24:43.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/s390x/kernel/wrapper32.S 2009-08-07 10:24:30.000000000 +0200 @@ -1107,3 +1107,95 @@ 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 + + diff -Naur linux-2.4.22-ppc-dev.orig/arch/sparc/defconfig linux-2.4.22-ppc-dev/arch/sparc/defconfig --- linux-2.4.22-ppc-dev.orig/arch/sparc/defconfig 2009-08-07 06:24:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/sparc/defconfig 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,16 @@ # # Automatically generated make config: don't edit # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set CONFIG_UID16=y CONFIG_HIGHMEM=y diff -Naur linux-2.4.22-ppc-dev.orig/arch/sparc64/defconfig linux-2.4.22-ppc-dev/arch/sparc64/defconfig --- linux-2.4.22-ppc-dev.orig/arch/sparc64/defconfig 2009-08-07 06:24:30.000000000 +0200 +++ linux-2.4.22-ppc-dev/arch/sparc64/defconfig 2009-08-07 06:36:08.000000000 +0200 @@ -1,6 +1,16 @@ # +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_POSIX_ACL is not set # Automatically generated make config: don't edit # +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT3_FS_XATTR_SHARING is not set +# CONFIG_EXT3_FS_XATTR_USER is not set +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XATTR_SHARING is not set +# CONFIG_EXT2_FS_XATTR_USER is not set +# CONFIG_FS_MBCACHE is not set # # Code maturity level options diff -Naur linux-2.4.22-ppc-dev.orig/fs/Config.in linux-2.4.22-ppc-dev/fs/Config.in --- linux-2.4.22-ppc-dev.orig/fs/Config.in 2009-08-07 06:34:34.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/Config.in 2009-08-07 10:09:06.000000000 +0200 @@ -31,6 +31,15 @@ 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 +dep_bool ' Ext3 POSIX Access Control Lists' \ + CONFIG_EXT3_FS_POSIX_ACL $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 @@ -90,6 +99,15 @@ 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 +dep_bool ' Ext2 POSIX Access Control Lists' \ + CONFIG_EXT2_FS_POSIX_ACL $CONFIG_EXT2_FS_XATTR tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS @@ -108,11 +126,13 @@ dep_tristate 'InterMezzo file system support (replicating fs) (EXPERIMENTAL)' CONFIG_INTERMEZZO_FS $CONFIG_INET $CONFIG_EXPERIMENTAL dep_tristate 'NFS file system support' CONFIG_NFS_FS $CONFIG_INET dep_mbool ' Provide NFSv3 client support' CONFIG_NFS_V3 $CONFIG_NFS_FS + dep_mbool ' Solaris ACL RPCs' CONFIG_NFS_ACL $CONFIG_NFS_V3 dep_mbool ' Allow direct I/O on NFS files (EXPERIMENTAL)' CONFIG_NFS_DIRECTIO $CONFIG_NFS_FS $CONFIG_EXPERIMENTAL dep_bool ' Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET dep_mbool ' Provide NFSv3 server support' CONFIG_NFSD_V3 $CONFIG_NFSD + dep_mbool ' Solaris ACL RPCs' CONFIG_NFSD_ACL $CONFIG_NFSD_V3 dep_mbool ' Provide NFS server over TCP support (EXPERIMENTAL)' CONFIG_NFSD_TCP $CONFIG_NFSD $CONFIG_EXPERIMENTAL if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then @@ -162,6 +182,19 @@ 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 + +# POSIX ACL helper functions + +if [ "$CONFIG_EXT2_FS_POSIX_ACL" = "y" -o \ + "$CONFIG_EXT3_FS_POSIX_ACL" = "y" ]; then + define_bool CONFIG_FS_POSIX_ACL y +else + bool "POSIX ACL helper functions" CONFIG_FS_POSIX_ACL +fi + mainmenu_option next_comment comment 'Partition Types' source fs/partitions/Config.in diff -Naur linux-2.4.22-ppc-dev.orig/fs/Makefile linux-2.4.22-ppc-dev/fs/Makefile --- linux-2.4.22-ppc-dev.orig/fs/Makefile 2009-08-07 06:34:34.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/Makefile 2009-08-07 06:36:09.000000000 +0200 @@ -78,6 +78,13 @@ obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o +export-objs += mbcache.o posix_acl.o xattr_acl.o solaris_acl.o +obj-$(CONFIG_FS_MBCACHE) += mbcache.o +obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o + +obj-$(CONFIG_NFS_ACL) += solaris_acl.o +obj-$(CONFIG_NFSD_ACL) += solaris_acl.o + # persistent filesystems obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/Makefile linux-2.4.22-ppc-dev/fs/ext2/Makefile --- linux-2.4.22-ppc-dev.orig/fs/ext2/Makefile 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext2/Makefile 2009-08-07 06:36:08.000000000 +0200 @@ -13,4 +13,10 @@ 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 +obj-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o + include $(TOPDIR)/Rules.make diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/acl.c linux-2.4.22-ppc-dev/fs/ext2/acl.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/acl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext2/acl.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,575 @@ +/* + * linux/fs/ext2/acl.c + * + * Copyright (C) 2001-2003 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Convert from filesystem to in-memory representation. + */ +static struct posix_acl * +ext2_acl_from_disk(const void *value, size_t size) +{ + const char *end = (char *)value + size; + size_t n, count; + struct posix_acl *acl; + + if (!value) + return NULL; + if (size < sizeof(ext2_acl_header)) + return ERR_PTR(-EINVAL); + if (((ext2_acl_header *)value)->a_version != + cpu_to_le32(EXT2_ACL_VERSION)) + return ERR_PTR(-EINVAL); + value = (char *)value + sizeof(ext2_acl_header); + count = ext2_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + for (n=0; n < count; n++) { + ext2_acl_entry *entry = + (ext2_acl_entry *)value; + if ((char *)value + sizeof(ext2_acl_entry_short) > end) + goto fail; + acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); + acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + + sizeof(ext2_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(ext2_acl_entry); + if ((char *)value > end) + goto fail; + acl->a_entries[n].e_id = + le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +/* + * Convert from in-memory to filesystem representation. + */ +static void * +ext2_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + ext2_acl_header *ext_acl; + char *e; + size_t n; + + *size = ext2_acl_size(acl->a_count); + ext_acl = (ext2_acl_header *)kmalloc(sizeof(ext2_acl_header) + + acl->a_count * sizeof(ext2_acl_entry), GFP_KERNEL); + if (!ext_acl) + return ERR_PTR(-ENOMEM); + ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION); + e = (char *)ext_acl + sizeof(ext2_acl_header); + for (n=0; n < acl->a_count; n++) { + ext2_acl_entry *entry = (ext2_acl_entry *)e; + entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = + cpu_to_le32(acl->a_entries[n].e_id); + e += sizeof(ext2_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(ext2_acl_entry_short); + break; + + default: + goto fail; + } + } + return (char *)ext_acl; + +fail: + kfree(ext_acl); + return ERR_PTR(-EINVAL); +} + +/* + * inode->i_sem: don't care + * BKL: held + */ +static struct posix_acl * +ext2_get_acl(struct inode *inode, int type) +{ + const size_t max_size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES); + struct ext2_inode_info *ei = EXT2_I(inode); + int name_index; + char *value; + struct posix_acl *acl; + int retval; + + if (!IS_POSIXACL(inode)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT2_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_acl); + name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT2_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_default_acl); + name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; + break; + + default: + return ERR_PTR(-EINVAL); + } + value = kmalloc(max_size, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + + retval = ext2_xattr_get(inode, name_index, "", value, max_size); + acl = ERR_PTR(retval); + if (retval >= 0) + acl = ext2_acl_from_disk(value, retval); + else if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + kfree(value); + + if (!IS_ERR(acl)) { + switch(type) { + case ACL_TYPE_ACCESS: + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return acl; +} + +/* + * inode->i_sem: down, or inode is just being initialized + * BKL: held + */ +static int +ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + struct ext2_inode_info *ei = EXT2_I(inode); + int name_index; + void *value = NULL; + size_t size; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (!IS_POSIXACL(inode)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { + mode_t mode = inode->i_mode; + error = posix_acl_equiv_mode(acl, &mode); + if (error < 0) + return error; + else { + inode->i_mode = mode; + mark_inode_dirty(inode); + if (error == 0) + acl = NULL; + } + } + break; + + case ACL_TYPE_DEFAULT: + name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + + default: + return -EINVAL; + } + if (acl) { + if (acl->a_count > EXT2_ACL_MAX_ENTRIES) + return -EINVAL; + value = ext2_acl_to_disk(acl, &size); + if (IS_ERR(value)) + return (int)PTR_ERR(value); + } + + error = ext2_xattr_set(inode, name_index, "", value, size, 0); + + if (value) + kfree(value); + if (!error) { + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT2_ACL_NOT_CACHED) + posix_acl_release(ei->i_acl); + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT2_ACL_NOT_CACHED) + posix_acl_release(ei->i_default_acl); + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return error; +} + +/* + * Inode operation permission(). + * + * inode->i_sem: don't care + * BKL: held + */ +int +ext2_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + + /* Nobody gets write access to a read-only fs */ + if ((mask & MAY_WRITE) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + /* Nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + if (current->fsuid == inode->i_uid) { + mode >>= 6; + } else if (IS_POSIXACL(inode)) { + struct ext2_inode_info *ei = EXT2_I(inode); + + /* The access ACL cannot grant access if the group class + permission bits don't contain all requested permissions. */ + if (((mode >> 3) & mask & S_IRWXO) != mask) + goto check_groups; + if (ei->i_acl == EXT2_ACL_NOT_CACHED) { + struct posix_acl *acl = + ext2_get_acl(inode, ACL_TYPE_ACCESS); + + if (IS_ERR(acl)) + return PTR_ERR(acl); + posix_acl_release(acl); + if (ei->i_acl == EXT2_ACL_NOT_CACHED) + return -EIO; + } + if (ei->i_acl) { + int error = posix_acl_permission(inode, ei->i_acl,mask); + if (error == -EACCES) + goto check_capabilities; + return error; + } else + goto check_groups; + } else { +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + } + if ((mode & mask & S_IRWXO) == mask) + return 0; + +check_capabilities: + /* Allowed to override Discretionary Access Control? */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ + if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || + (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) + return 0; + return -EACCES; +} + +/* + * Initialize the ACLs of a new inode. Called from ext2_new_inode. + * + * dir->i_sem: don't care + * inode->i_sem: up (access to inode is still exclusive) + * BKL: held + */ +int +ext2_init_acl(struct inode *inode, struct inode *dir) +{ + struct posix_acl *acl = NULL; + int error = 0; + + if (!S_ISLNK(inode->i_mode)) { + if (IS_POSIXACL(dir)) { + acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + } + if (!acl) { + inode->i_mode &= ~current->fs->umask; + mark_inode_dirty(inode); + } + } + if (IS_POSIXACL(inode) && acl) { + struct posix_acl *clone; + mode_t mode; + + if (S_ISDIR(inode->i_mode)) { + error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (error) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + error = -ENOMEM; + if (!clone) + goto cleanup; + mode = inode->i_mode; + error = posix_acl_create_masq(clone, &mode); + if (error >= 0) { + inode->i_mode = mode; + mark_inode_dirty(inode); + if (error > 0) { + /* This is an extended ACL */ + error = ext2_set_acl(inode, + ACL_TYPE_ACCESS, clone); + } + } + posix_acl_release(clone); + } +cleanup: + posix_acl_release(acl); + return error; +} + +/* + * Does chmod for an inode that may have an Access Control List. The + * inode->i_mode field must be updated to the desired value by the caller + * before calling this function. + * Returns 0 on success, or a negative error number. + * + * We change the ACL rather than storing some ACL entries in the file + * mode permission bits (which would be more efficient), because that + * would break once additional permissions (like ACL_APPEND, ACL_DELETE + * for directories) are added. There are no more bits available in the + * file mode. + * + * inode->i_sem: down + * BKL: held + */ +int +ext2_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int error; + + if (!IS_POSIXACL(inode)) + return 0; + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + error = posix_acl_chmod_masq(clone, inode->i_mode); + if (!error) + error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return error; +} + +/* + * Extended attribut handlers + */ +static size_t +ext2_xattr_list_acl_access(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_ACCESS); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_ACCESS, size); + return size; +} + +static size_t +ext2_xattr_list_acl_default(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_DEFAULT, size); + return size; +} + +static int +ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + return -EOPNOTSUPP; + + acl = ext2_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl == NULL) + return -ENODATA; + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return error; +} + +static int +ext2_xattr_get_acl_access(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int +ext2_xattr_get_acl_default(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int +ext2_xattr_set_acl(struct inode *inode, int type, const void *value, + size_t size) +{ + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + return -EOPNOTSUPP; + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + else if (acl) { + error = posix_acl_valid(acl); + if (error) + goto release_and_out; + } + } else + acl = NULL; + + error = ext2_set_acl(inode, type, acl); + +release_and_out: + posix_acl_release(acl); + return error; +} + +static int +ext2_xattr_set_acl_access(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); +} + +static int +ext2_xattr_set_acl_default(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); +} + +struct ext2_xattr_handler ext2_xattr_acl_access_handler = { + prefix: XATTR_NAME_ACL_ACCESS, + list: ext2_xattr_list_acl_access, + get: ext2_xattr_get_acl_access, + set: ext2_xattr_set_acl_access, +}; + +struct ext2_xattr_handler ext2_xattr_acl_default_handler = { + prefix: XATTR_NAME_ACL_DEFAULT, + list: ext2_xattr_list_acl_default, + get: ext2_xattr_get_acl_default, + set: ext2_xattr_set_acl_default, +}; + +void +exit_ext2_acl(void) +{ + ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext2_xattr_acl_access_handler); + ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext2_xattr_acl_default_handler); +} + +int __init +init_ext2_acl(void) +{ + int error; + + error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext2_xattr_acl_access_handler); + if (error) + goto fail; + error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext2_xattr_acl_default_handler); + if (error) + goto fail; + return 0; + +fail: + exit_ext2_acl(); + return error; +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/file.c linux-2.4.22-ppc-dev/fs/ext2/file.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/file.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext2/file.c 2009-08-07 06:36:09.000000000 +0200 @@ -20,6 +20,8 @@ #include #include +#include +#include #include /* @@ -51,4 +53,10 @@ struct inode_operations ext2_file_inode_operations = { truncate: ext2_truncate, + setxattr: ext2_setxattr, + getxattr: ext2_getxattr, + listxattr: ext2_listxattr, + removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, }; diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/ialloc.c linux-2.4.22-ppc-dev/fs/ext2/ialloc.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/ialloc.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext2/ialloc.c 2009-08-07 06:36:08.000000000 +0200 @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include @@ -167,6 +169,7 @@ */ if (!is_bad_inode(inode)) { /* Quota is already initialized in iput() */ + ext2_xattr_delete_inode(inode); DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); } @@ -311,7 +314,7 @@ return group; } -struct inode * ext2_new_inode (const struct inode * dir, int mode) +struct inode * ext2_new_inode (struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; @@ -395,17 +398,31 @@ 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); - inode->i_flags |= S_NOQUOTA; - inode->i_nlink = 0; - iput(inode); - return ERR_PTR(-EDQUOT); + err = -EDQUOT; + goto fail3; } + err = ext2_init_acl(inode, dir); + if (err) { + DQUOT_FREE_INODE(inode); + goto fail3; + } + ext2_debug ("allocating inode %lu\n", inode->i_ino); return inode; +fail3: + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; + iput(inode); + return ERR_PTR(err); + fail2: desc = ext2_get_group_desc (sb, group, &bh2); desc->bg_free_inodes_count = diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/inode.c linux-2.4.22-ppc-dev/fs/ext2/inode.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/inode.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext2/inode.c 2009-08-07 06:36:09.000000000 +0200 @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,18 @@ static int ext2_update_inode(struct inode * inode, int do_sync); /* + * Test whether an inode is a fast symlink. + */ +static inline int ext2_inode_is_fast_symlink(struct inode *inode) +{ + int ea_blocks = inode->u.ext2_i.i_file_acl ? + (inode->i_sb->s_blocksize >> 9) : 0; + + return (S_ISLNK(inode->i_mode) && + inode->i_blocks - ea_blocks == 0); +} + +/* * Called at each iput() */ void ext2_put_inode (struct inode * inode) @@ -53,9 +66,7 @@ { 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); @@ -801,6 +812,8 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; + if (ext2_inode_is_fast_symlink(inode)) + return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; @@ -903,8 +916,7 @@ 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", @@ -989,10 +1001,7 @@ 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; @@ -1001,18 +1010,32 @@ inode->i_fop = &ext2_dir_operations; inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) + 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 +#ifdef CONFIG_EXT2_FS_POSIX_ACL + if (inode->u.ext2_i.i_file_acl) { + /* The filesystem is mounted with ACL support, and there + are extended attributes for this inode. However we do + not yet know whether there are actually any ACLs. */ + inode->u.ext2_i.i_acl = EXT2_ACL_NOT_CACHED; + inode->u.ext2_i.i_default_acl = EXT2_ACL_NOT_CACHED; + } +#endif return; bad_inode: @@ -1157,3 +1180,23 @@ { return ext2_update_inode (inode, 1); } + +int ext2_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, iattr); + if (error) + return error; + inode_setattr(inode, iattr); + if (iattr->ia_valid & ATTR_MODE) { + if (!(iattr->ia_valid & ATTR_SIZE)) + down(&inode->i_sem); + error = ext2_acl_chmod(inode); + if (!(iattr->ia_valid & ATTR_SIZE)) + up(&inode->i_sem); + } + return error; +} + diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/namei.c linux-2.4.22-ppc-dev/fs/ext2/namei.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/namei.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext2/namei.c 2009-08-07 06:36:09.000000000 +0200 @@ -31,6 +31,8 @@ #include #include +#include +#include #include /* @@ -111,7 +113,10 @@ struct inode * inode = ext2_new_inode (dir, mode); int err = PTR_ERR(inode); if (!IS_ERR(inode)) { - init_special_inode(inode, mode, rdev); + init_special_inode(inode, inode->i_mode, rdev); +#ifdef CONFIG_EXT2_FS_POSIX_ACL + inode->i_op = &ext2_special_inode_operations; +#endif mark_inode_dirty(inode); err = ext2_add_nondir(dentry, inode); } @@ -136,7 +141,7 @@ 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 +350,19 @@ rmdir: ext2_rmdir, mknod: ext2_mknod, rename: ext2_rename, + setxattr: ext2_setxattr, + getxattr: ext2_getxattr, + listxattr: ext2_listxattr, + removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, +}; + +struct inode_operations ext2_special_inode_operations = { + setxattr: ext2_setxattr, + getxattr: ext2_getxattr, + listxattr: ext2_listxattr, + removexattr: ext2_removexattr, + setattr: ext2_setattr, + permission: ext2_permission, }; diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/super.c linux-2.4.22-ppc-dev/fs/ext2/super.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/super.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext2/super.c 2009-08-07 06:36:09.000000000 +0200 @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -125,6 +127,7 @@ 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; @@ -147,6 +150,26 @@ return; } +#ifdef CONFIG_EXT2_FS_POSIX_ACL + +static void ext2_clear_inode(struct inode *inode) +{ + if (inode->u.ext2_i.i_acl && + inode->u.ext2_i.i_acl != EXT2_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext2_i.i_acl); + inode->u.ext2_i.i_acl = EXT2_ACL_NOT_CACHED; + } + if (inode->u.ext2_i.i_default_acl && + inode->u.ext2_i.i_default_acl != EXT2_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext2_i.i_default_acl); + inode->u.ext2_i.i_default_acl = EXT2_ACL_NOT_CACHED; + } +} + +#else +# define ext2_clear_inode NULL +#endif + static struct super_operations ext2_sops = { read_inode: ext2_read_inode, write_inode: ext2_write_inode, @@ -156,6 +179,7 @@ write_super: ext2_write_super, statfs: ext2_statfs, remount_fs: ext2_remount, + clear_inode: ext2_clear_inode, }; /* @@ -163,7 +187,8 @@ */ static int parse_options (char * options, unsigned long * sb_block, unsigned short *resuid, unsigned short * resgid, - unsigned long * mount_options) + unsigned long * mount_options, + unsigned long * mount_flags) { char * this_char; char * value; @@ -175,6 +200,20 @@ 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 +#ifdef CONFIG_EXT2_FS_POSIX_ACL + if (!strcmp(this_char, "acl")) + *mount_flags |= MS_POSIXACL; + else if (!strcmp(this_char, "noacl")) + *mount_flags &= ~MS_POSIXACL; + else +#endif if (!strcmp (this_char, "bsddf")) clear_opt (*mount_options, MINIX_DF); else if (!strcmp (this_char, "nouid32")) { @@ -424,8 +463,14 @@ 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 +#ifdef CONFIG_EXT2_FS_POSIX_ACL + /* sb->s_flags |= MS_POSIXACL; */ +#endif if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, - &sb->u.ext2_sb.s_mount_opt)) { + &sb->u.ext2_sb.s_mount_opt, &sb->s_flags)) { return NULL; } @@ -712,17 +757,19 @@ struct ext2_super_block * es; unsigned short resuid = sb->u.ext2_sb.s_resuid; unsigned short resgid = sb->u.ext2_sb.s_resgid; - unsigned long new_mount_opt; + unsigned long new_mount_opt, new_mount_flags; unsigned long tmp; /* * Allow the "check" option to be passed as a remount option. */ new_mount_opt = sb->u.ext2_sb.s_mount_opt; + new_mount_flags = sb->s_flags; if (!parse_options (data, &tmp, &resuid, &resgid, - &new_mount_opt)) + &new_mount_opt, &new_mount_flags)) return -EINVAL; + sb->s_flags = new_mount_flags; sb->u.ext2_sb.s_mount_opt = new_mount_opt; sb->u.ext2_sb.s_resuid = resuid; sb->u.ext2_sb.s_resgid = resgid; @@ -813,12 +860,39 @@ 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) + goto fail; + error = init_ext2_xattr_trusted(); + if (error) + goto fail2; + error = init_ext2_acl(); + if (error) + goto fail3; + error = register_filesystem(&ext2_fs_type); + if (!error) + return 0; + + exit_ext2_acl(); +fail3: + exit_ext2_xattr_trusted(); +fail2: + exit_ext2_xattr_user(); +fail: + exit_ext2_xattr(); + return error; } static void __exit exit_ext2_fs(void) { unregister_filesystem(&ext2_fs_type); + exit_ext2_acl(); + exit_ext2_xattr_trusted(); + exit_ext2_xattr_user(); + exit_ext2_xattr(); } EXPORT_NO_SYMBOLS; diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/symlink.c linux-2.4.22-ppc-dev/fs/ext2/symlink.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/symlink.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext2/symlink.c 2009-08-07 06:36:08.000000000 +0200 @@ -19,6 +19,7 @@ #include #include +#include static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen) { @@ -32,7 +33,20 @@ 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, }; diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/xattr.c linux-2.4.22-ppc-dev/fs/ext2/xattr.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/xattr.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext2/xattr.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,1165 @@ +/* + * 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 *); +static void ext2_xattr_cache_remove(struct buffer_head *); +static void ext2_xattr_rehash(struct ext2_xattr_header *, + struct ext2_xattr_entry *); + +static struct mb_cache *ext2_xattr_cache; + +#else +# define ext2_xattr_cache_insert(bh) 0 +# define ext2_xattr_cache_find(inode, header) NULL +# define ext2_xattr_cache_remove(bh) while(0) {} +# 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, + const void *value, size_t size, int flags) +{ + struct ext2_xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + if (size == 0) + value = ""; /* empty EA, do not remove */ + handler = ext2_xattr_resolve_name(&name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, value, size, flags); +} + +/* + * Inode operation removexattr() + * + * dentry->d_inode->i_sem: down + * 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); +} + +/* + * 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) { + /* assert(header == HDR(bh)); */ + lock_buffer(bh); + if (header->h_refcount == cpu_to_le32(1)) { + ea_bdebug(bh, "modifying in-place"); + ext2_xattr_cache_remove(bh); + /* keep the buffer locked while modifying it. */ + } else { + int offset; + + unlock_buffer(bh); + ea_bdebug(bh, "cloning"); + header = kmalloc(bh->b_size, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memcpy(header, HDR(bh), bh->b_size); + header->h_refcount = cpu_to_le32(1); + offset = (char *)header - bh->b_data; + here = ENTRY((char *)here + offset); + last = ENTRY((char *)last + offset); + } + } else { + /* Allocate a buffer where we construct the new block. */ + header = kmalloc(sb->s_blocksize, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memset(header, 0, sb->s_blocksize); + end = (char *)header + sb->s_blocksize; + header->h_magic = cpu_to_le32(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))) { + /* This block is now empty. */ + if (bh && header == HDR(bh)) + unlock_buffer(bh); /* we were modifying in-place. */ + error = ext2_xattr_set2(inode, bh, NULL); + } else { + ext2_xattr_rehash(header, here); + if (bh && header == HDR(bh)) + unlock_buffer(bh); /* we were modifying in-place. */ + error = ext2_xattr_set2(inode, bh, 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; + int error; + + if (header) { + new_bh = ext2_xattr_cache_find(inode, header); + if (new_bh) { + /* + * We found an identical block in the cache. The + * block returned is locked. The old block will + * be released after updating the inode. + */ + ea_bdebug(new_bh, "%s block %ld", + (old_bh == new_bh) ? "keeping" : "reusing", + new_bh->b_blocknr); + + error = -EDQUOT; + /* How can we enforce the allocation? */ + if (DQUOT_ALLOC_BLOCK(inode, 1)) { + unlock_buffer(new_bh); + goto cleanup; + } + + HDR(new_bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(new_bh)->h_refcount) + 1); + ea_bdebug(new_bh, "refcount now=%d", + le32_to_cpu(HDR(new_bh)->h_refcount)); + unlock_buffer(new_bh); + } else if (old_bh && header == HDR(old_bh)) { + /* Keep this block. No need to lock the block as we + * don't need to change the reference count. */ + 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. + */ + lock_buffer(old_bh); + if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) { + /* Free the old block. */ + ea_bdebug(old_bh, "freeing"); + ext2_free_blocks(inode, old_bh->b_blocknr, 1); + mark_buffer_clean(old_bh); + } else { + /* Decrement the refcount only. */ + HDR(old_bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(old_bh)->h_refcount) - 1); + DQUOT_FREE_BLOCK(inode, 1); + mark_buffer_dirty(old_bh); + ea_bdebug(old_bh, "refcount now=%d", + le32_to_cpu(HDR(old_bh)->h_refcount)); + } + unlock_buffer(old_bh); + } + +cleanup: + 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; + + down_write(&EXT2_I(inode)->xattr_sem); + 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; + } + lock_buffer(bh); + if (HDR(bh)->h_refcount == cpu_to_le32(1)) { + ext2_xattr_cache_remove(bh); + ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); + mark_buffer_clean(bh); + } else { + HDR(bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(bh)->h_refcount) - 1); + mark_buffer_dirty(bh); + if (IS_SYNC(inode)) { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + } + DQUOT_FREE_BLOCK(inode, 1); + } + ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount)); + unlock_buffer(bh); + EXT2_I(inode)->i_file_acl = 0; + +cleanup: + brelse(bh); + up_write(&EXT2_I(inode)->xattr_sem); +} + +/* + * 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_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) +{ + __u32 hash = le32_to_cpu(header->h_hash); + struct mb_cache_entry *ce; + + if (!header->h_hash) + return NULL; /* never share */ + ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); + ce = mb_cache_entry_find_first(ext2_xattr_cache, 0, inode->i_dev, hash); + while (ce) { + struct buffer_head *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 { + lock_buffer(bh); + if (le32_to_cpu(HDR(bh)->h_refcount) > + EXT2_XATTR_REFCOUNT_MAX) { + ea_idebug(inode, "block %ld refcount %d>%d", + ce->e_block, + le32_to_cpu(HDR(bh)->h_refcount), + EXT2_XATTR_REFCOUNT_MAX); + } else if (!ext2_xattr_cmp(header, HDR(bh))) { + ea_bdebug(bh, "b_count=%d", + atomic_read(&(bh->b_count))); + mb_cache_entry_release(ce); + /* buffer will be unlocked by caller */ + return bh; + } + unlock_buffer(bh); + brelse(bh); + } + ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash); + } + return NULL; +} + +/* + * ext2_xattr_cache_remove() + * + * Remove the cache entry of a block from the cache. Called when a + * block becomes invalid. + */ +static void +ext2_xattr_cache_remove(struct buffer_head *bh) +{ + struct mb_cache_entry *ce; + + ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_dev, bh->b_blocknr); + if (ce) { + ea_bdebug(bh, "removing (%d cache entries remaining)", + atomic_read(&ext2_xattr_cache->c_entry_count)-1); + mb_cache_entry_free(ce); + } else + ea_bdebug(bh, "no cache entry"); +} + +#define NAME_HASH_SHIFT 5 +#define VALUE_HASH_SHIFT 16 + +/* + * 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_index), 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; +} + +void +exit_ext2_xattr(void) +{ +} + +#endif /* CONFIG_EXT2_FS_XATTR_SHARING */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/xattr_trusted.c linux-2.4.22-ppc-dev/fs/ext2/xattr_trusted.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/xattr_trusted.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext2/xattr_trusted.c 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,76 @@ +/* + * 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); +} + +void +exit_ext2_xattr_trusted(void) +{ + ext2_xattr_unregister(EXT2_XATTR_INDEX_TRUSTED, + &ext2_xattr_trusted_handler); +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext2/xattr_user.c linux-2.4.22-ppc-dev/fs/ext2/xattr_user.c --- linux-2.4.22-ppc-dev.orig/fs/ext2/xattr_user.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext2/xattr_user.c 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,95 @@ +/* + * 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); +} + +void +exit_ext2_xattr_user(void) +{ + ext2_xattr_unregister(EXT2_XATTR_INDEX_USER, + &ext2_xattr_user_handler); +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/Makefile linux-2.4.22-ppc-dev/fs/ext3/Makefile --- linux-2.4.22-ppc-dev.orig/fs/ext3/Makefile 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext3/Makefile 2009-08-07 06:36:08.000000000 +0200 @@ -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,10 @@ 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 +obj-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o + include $(TOPDIR)/Rules.make diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/acl.c linux-2.4.22-ppc-dev/fs/ext3/acl.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/acl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext3/acl.c 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,585 @@ +/* + * linux/fs/ext3/acl.c + * + * Copyright (C) 2001-2003 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Convert from filesystem to in-memory representation. + */ +static struct posix_acl * +ext3_acl_from_disk(const void *value, size_t size) +{ + const char *end = (char *)value + size; + size_t n, count; + struct posix_acl *acl; + + if (!value) + return NULL; + if (size < sizeof(ext3_acl_header)) + return ERR_PTR(-EINVAL); + if (((ext3_acl_header *)value)->a_version != + cpu_to_le32(EXT3_ACL_VERSION)) + return ERR_PTR(-EINVAL); + value = (char *)value + sizeof(ext3_acl_header); + count = ext3_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + for (n=0; n < count; n++) { + ext3_acl_entry *entry = + (ext3_acl_entry *)value; + if ((char *)value + sizeof(ext3_acl_entry_short) > end) + goto fail; + acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); + acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + + sizeof(ext3_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(ext3_acl_entry); + if ((char *)value > end) + goto fail; + acl->a_entries[n].e_id = + le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +/* + * Convert from in-memory to filesystem representation. + */ +static void * +ext3_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + ext3_acl_header *ext_acl; + char *e; + size_t n; + + *size = ext3_acl_size(acl->a_count); + ext_acl = (ext3_acl_header *)kmalloc(sizeof(ext3_acl_header) + + acl->a_count * sizeof(ext3_acl_entry), GFP_KERNEL); + if (!ext_acl) + return ERR_PTR(-ENOMEM); + ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION); + e = (char *)ext_acl + sizeof(ext3_acl_header); + for (n=0; n < acl->a_count; n++) { + ext3_acl_entry *entry = (ext3_acl_entry *)e; + entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = + cpu_to_le32(acl->a_entries[n].e_id); + e += sizeof(ext3_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(ext3_acl_entry_short); + break; + + default: + goto fail; + } + } + return (char *)ext_acl; + +fail: + kfree(ext_acl); + return ERR_PTR(-EINVAL); +} + +/* + * Inode operation get_posix_acl(). + * + * inode->i_sem: don't care + * BKL: held + */ +struct posix_acl * +ext3_get_acl(struct inode *inode, int type) +{ + const size_t max_size = ext3_acl_size(EXT3_ACL_MAX_ENTRIES); + struct ext3_inode_info *ei = EXT3_I(inode); + int name_index; + char *value; + struct posix_acl *acl; + int retval; + + if (!IS_POSIXACL(inode)) + return 0; + + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT3_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_acl); + name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT3_ACL_NOT_CACHED) + return posix_acl_dup(ei->i_default_acl); + name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; + break; + + default: + return ERR_PTR(-EINVAL); + } + value = kmalloc(max_size, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + + retval = ext3_xattr_get(inode, name_index, "", value, max_size); + acl = ERR_PTR(retval); + if (retval > 0) + acl = ext3_acl_from_disk(value, retval); + else if (retval == -ENODATA || retval == -ENOSYS) + acl = NULL; + kfree(value); + + if (!IS_ERR(acl)) { + switch(type) { + case ACL_TYPE_ACCESS: + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return acl; +} + +/* + * inode->i_sem: down, or inode is just being initialized + * BKL: held + */ +static int +ext3_do_set_acl(handle_t *handle, struct inode *inode, int type, + struct posix_acl *acl) +{ + struct ext3_inode_info *ei = EXT3_I(inode); + int name_index; + void *value = NULL; + size_t size; + int error; + + if (S_ISLNK(inode->i_mode)) + return -ENODATA; + + switch(type) { + case ACL_TYPE_ACCESS: + name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { + mode_t mode = inode->i_mode; + error = posix_acl_equiv_mode(acl, &mode); + if (error < 0) + return error; + else { + inode->i_mode = mode; + ext3_mark_inode_dirty(handle, inode); + if (error == 0) + acl = NULL; + } + } + break; + + case ACL_TYPE_DEFAULT: + name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + + default: + return -EINVAL; + } + if (acl) { + if (acl->a_count > EXT3_ACL_MAX_ENTRIES) + return -EINVAL; + value = ext3_acl_to_disk(acl, &size); + if (IS_ERR(value)) + return (int)PTR_ERR(value); + } + + error = ext3_xattr_set_handle(handle, inode, name_index, "", + value, size, 0); + + if (value) + kfree(value); + if (!error) { + switch(type) { + case ACL_TYPE_ACCESS: + if (ei->i_acl != EXT3_ACL_NOT_CACHED) + posix_acl_release(ei->i_acl); + ei->i_acl = posix_acl_dup(acl); + break; + + case ACL_TYPE_DEFAULT: + if (ei->i_default_acl != EXT3_ACL_NOT_CACHED) + posix_acl_release(ei->i_default_acl); + ei->i_default_acl = posix_acl_dup(acl); + break; + } + } + return error; +} + +/* + * Inode operation permission(). + * + * inode->i_sem: don't care + * BKL: held + */ +int +ext3_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + + /* Nobody gets write access to a read-only fs */ + if ((mask & MAY_WRITE) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + /* Nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + if (current->fsuid == inode->i_uid) { + mode >>= 6; + } else if (IS_POSIXACL(inode)) { + struct ext3_inode_info *ei = EXT3_I(inode); + + /* The access ACL cannot grant access if the group class + permission bits don't contain all requested permissions. */ + if (((mode >> 3) & mask & S_IRWXO) != mask) + goto check_groups; + if (ei->i_acl == EXT3_ACL_NOT_CACHED) { + struct posix_acl *acl = + ext3_get_acl(inode, ACL_TYPE_ACCESS); + + if (IS_ERR(acl)) + return PTR_ERR(acl); + posix_acl_release(acl); + if (ei->i_acl == EXT3_ACL_NOT_CACHED) + return -EIO; + } + if (ei->i_acl) { + int error = posix_acl_permission(inode, ei->i_acl,mask); + if (error == -EACCES) + goto check_capabilities; + return error; + } else + goto check_groups; + } else { +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + } + if ((mode & mask & S_IRWXO) == mask) + return 0; + +check_capabilities: + /* Allowed to override Discretionary Access Control? */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ + if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || + (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) + return 0; + return -EACCES; +} + +/* + * Initialize the ACLs of a new inode. Called from ext3_new_inode. + * + * dir->i_sem: don't care + * inode->i_sem: up (access to inode is still exclusive) + * BKL: held + */ +int +ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + struct posix_acl *acl = NULL; + int error = 0; + + if (!S_ISLNK(inode->i_mode)) { + if (IS_POSIXACL(dir)) { + acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + } + if (!acl) { + inode->i_mode &= ~current->fs->umask; + ext3_mark_inode_dirty(handle, inode); + } + } + if (IS_POSIXACL(inode) && acl) { + struct posix_acl *clone; + mode_t mode; + + if (S_ISDIR(inode->i_mode)) { + error = ext3_do_set_acl(handle, inode, + ACL_TYPE_DEFAULT, acl); + if (error) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + error = -ENOMEM; + if (!clone) + goto cleanup; + + mode = inode->i_mode; + error = posix_acl_create_masq(clone, &mode); + if (error >= 0) { + inode->i_mode = mode; + ext3_mark_inode_dirty(handle, inode); + if (error > 0) { + /* This is an extended ACL */ + error = ext3_do_set_acl(handle, inode, + ACL_TYPE_ACCESS, clone); + } + } + posix_acl_release(clone); + } +cleanup: + posix_acl_release(acl); + return error; +} + +/* + * Does chmod for an inode that may have an Access Control List. The + * inode->i_mode field must be updated to the desired value by the caller + * before calling this function. + * Returns 0 on success, or a negative error number. + * + * We change the ACL rather than storing some ACL entries in the file + * mode permission bits (which would be more efficient), because that + * would break once additional permissions (like ACL_APPEND, ACL_DELETE + * for directories) are added. There are no more bits available in the + * file mode. + * + * inode->i_sem: down + * BKL: held + */ +int +ext3_acl_chmod(handle_t *handle, struct inode *inode) +{ + struct posix_acl *acl, *clone; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (!IS_POSIXACL(inode)) + return 0; + acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + error = posix_acl_chmod_masq(clone, inode->i_mode); + if (!error) + error = ext3_do_set_acl(handle, inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return error; +} + +/* + * Extended attribut handlers + */ +static size_t +ext3_xattr_list_acl_access(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_ACCESS); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_ACCESS, size); + return size; +} + +static size_t +ext3_xattr_list_acl_default(char *list, struct inode *inode, + const char *name, int name_len) +{ + const size_t size = sizeof(XATTR_NAME_ACL_DEFAULT); + + if (!IS_POSIXACL(inode)) + return 0; + if (list) + memcpy(list, XATTR_NAME_ACL_DEFAULT, size); + return size; +} + +static int +ext3_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + return -EOPNOTSUPP; + + acl = ext3_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl == NULL) + return -ENODATA; + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return error; +} + +static int +ext3_xattr_get_acl_access(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int +ext3_xattr_get_acl_default(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int +ext3_xattr_set_acl(struct inode *inode, int type, const void *value, + size_t size) +{ + handle_t *handle; + struct posix_acl *acl; + int error; + + if (!IS_POSIXACL(inode)) + return -EOPNOTSUPP; + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + else if (acl) { + error = posix_acl_valid(acl); + if (error) + goto release_and_out; + } + } else + acl = NULL; + + handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + error = ext3_do_set_acl(handle, inode, type, acl); + ext3_journal_stop(handle, inode); + +release_and_out: + posix_acl_release(acl); + return error; +} + +static int +ext3_xattr_set_acl_access(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); +} + +static int +ext3_xattr_set_acl_default(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext3_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); +} + +struct ext3_xattr_handler ext3_xattr_acl_access_handler = { + prefix: XATTR_NAME_ACL_ACCESS, + list: ext3_xattr_list_acl_access, + get: ext3_xattr_get_acl_access, + set: ext3_xattr_set_acl_access, +}; + +struct ext3_xattr_handler ext3_xattr_acl_default_handler = { + prefix: XATTR_NAME_ACL_DEFAULT, + list: ext3_xattr_list_acl_default, + get: ext3_xattr_get_acl_default, + set: ext3_xattr_set_acl_default, +}; + +void +exit_ext3_acl(void) +{ + ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext3_xattr_acl_access_handler); + ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext3_xattr_acl_default_handler); +} + +int __init +init_ext3_acl(void) +{ + int error; + + error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS, + &ext3_xattr_acl_access_handler); + if (error) + goto fail; + error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT, + &ext3_xattr_acl_default_handler); + if (error) + goto fail; + return 0; + +fail: + exit_ext3_acl(); + return error; +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/file.c linux-2.4.22-ppc-dev/fs/ext3/file.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/file.c 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext3/file.c 2009-08-07 06:36:09.000000000 +0200 @@ -21,8 +21,11 @@ #include #include #include +#include #include #include +#include +#include #include #include @@ -124,5 +127,10 @@ 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 */ + permission: ext3_permission, /* BKL held */ }; diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/ialloc.c linux-2.4.22-ppc-dev/fs/ext3/ialloc.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/ialloc.c 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext3/ialloc.c 2009-08-07 06:36:08.000000000 +0200 @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -216,6 +218,7 @@ * 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); @@ -296,8 +299,7 @@ * For other inodes, search forward from the parent directory's block * group to find a free inode. */ -struct inode * ext3_new_inode (handle_t *handle, - const struct inode * dir, int mode) +struct inode * ext3_new_inode (handle_t *handle, struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; @@ -509,14 +511,21 @@ inode->u.ext3_i.i_state = EXT3_STATE_NEW; err = ext3_mark_inode_dirty(handle, inode); if (err) goto fail; + +#ifdef CONFIG_EXT2_FS_XATTR + init_rwsem(&inode->u.ext3_i.xattr_sem); +#endif unlock_super (sb); if(DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); - inode->i_flags |= S_NOQUOTA; - inode->i_nlink = 0; - iput(inode); - return ERR_PTR(-EDQUOT); + err = -EDQUOT; + goto fail2; + } + err = ext3_init_acl(handle, inode, dir); + if (err) { + DQUOT_FREE_INODE(inode); + goto fail2; } ext3_debug ("allocating inode %lu\n", inode->i_ino); return inode; @@ -527,6 +536,12 @@ unlock_super(sb); iput(inode); return ERR_PTR(err); + +fail2: + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; + iput(inode); + return ERR_PTR(err); } /* Verify that we are loading a valid orphan from disk */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/inode.c linux-2.4.22-ppc-dev/fs/ext3/inode.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/inode.c 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext3/inode.c 2009-08-07 10:03:09.000000000 +0200 @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +41,18 @@ */ #undef SEARCH_FROM_ZERO +/* + * Test whether an inode is a fast symlink. + */ +static inline int ext3_inode_is_fast_symlink(struct inode *inode) +{ + int ea_blocks = inode->u.ext3_i.i_file_acl ? + (inode->i_sb->s_blocksize >> 9) : 0; + + return (S_ISLNK(inode->i_mode) && + inode->i_blocks - ea_blocks == 0); +} + /* The ext3 forget function must perform a revoke if we are freeing data * which has been journaled. Metadata (eg. indirect blocks) must be * revoked in all cases. @@ -48,7 +62,7 @@ * 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) { @@ -179,9 +193,7 @@ { 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(); @@ -1870,6 +1882,8 @@ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; + if (ext3_inode_is_fast_symlink(inode)) + return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; @@ -2017,8 +2031,6 @@ 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( @@ -2159,10 +2171,7 @@ 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; @@ -2170,17 +2179,32 @@ inode->i_op = &ext3_dir_inode_operations; inode->i_fop = &ext3_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) + 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 +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (inode->u.ext3_i.i_file_acl) { + /* The filesystem is mounted with ACL support, and there + are extended attributes for this inode. However we do + not yet know whether there are actually any ACLs. */ + inode->u.ext3_i.i_acl = EXT3_ACL_NOT_CACHED; + inode->u.ext3_i.i_default_acl = EXT3_ACL_NOT_CACHED; + } +#endif + return; bad_inode: @@ -2369,10 +2393,6 @@ * be freed, so we have a strong guarantee that no future commit will * leave these blocks visible to the user.) * - * This is only needed for regular files. rmdir() has its own path, and - * we can never truncate a direcory except on final unlink (at which - * point i_nlink is zero so recovery is easy.) - * * Called with the BKL. */ @@ -2393,7 +2413,8 @@ return error; } - if (attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { + if (S_ISREG(inode->i_mode) && + attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { handle_t *handle; handle = ext3_journal_start(inode, 3); @@ -2415,9 +2436,27 @@ /* If inode_setattr's call to ext3_truncate failed to get a * transaction handle at all, we need to clean up the in-core * orphan list manually. */ - if (inode->i_nlink) + if (S_ISREG(inode->i_mode) && inode->i_nlink) ext3_orphan_del(NULL, inode); +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (!rc && (ia_valid & ATTR_MODE) && IS_POSIXACL(inode)) { + handle_t *handle; + + if (!(ia_valid & ATTR_SIZE)) + down(&inode->i_sem); + handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS); + if (IS_ERR(handle)) + error = PTR_ERR(handle); + else { + rc = ext3_acl_chmod(handle, inode); + ext3_journal_stop(handle, inode); + } + if (!(ia_valid & ATTR_SIZE)) + up(&inode->i_sem); + } +#endif + err_out: ext3_std_error(inode->i_sb, error); if (!error) diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/namei.c linux-2.4.22-ppc-dev/fs/ext3/namei.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/namei.c 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext3/namei.c 2009-08-07 06:36:09.000000000 +0200 @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -490,7 +492,10 @@ inode = ext3_new_inode (handle, dir, mode); err = PTR_ERR(inode); if (!IS_ERR(inode)) { - init_special_inode(inode, mode, rdev); + init_special_inode(inode, inode->i_mode, rdev); +#ifdef CONFIG_EXT3_FS_XATTR + inode->i_op = &ext3_special_inode_operations; +#endif err = ext3_add_nondir(handle, dentry, inode); } ext3_journal_stop(handle, dir); @@ -515,7 +520,7 @@ 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 +528,6 @@ 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 +554,6 @@ 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 +919,7 @@ 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 +1122,20 @@ rmdir: ext3_rmdir, /* BKL held */ mknod: ext3_mknod, /* BKL held */ rename: ext3_rename, /* 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 */ + permission: ext3_permission, /* BKL held */ +}; + +struct inode_operations ext3_special_inode_operations = { + 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 */ + permission: ext3_permission, /* BKL held */ }; + diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/super.c linux-2.4.22-ppc-dev/fs/ext3/super.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/super.c 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext3/super.c 2009-08-07 09:59:28.000000000 +0200 @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -406,6 +408,7 @@ 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); @@ -451,6 +454,26 @@ static struct dquot_operations ext3_qops; static int (*old_sync_dquot)(struct dquot *dquot); +#ifdef CONFIG_EXT3_FS_POSIX_ACL + +static void ext3_clear_inode(struct inode *inode) +{ + if (inode->u.ext3_i.i_acl && + inode->u.ext3_i.i_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext3_i.i_acl); + inode->u.ext3_i.i_acl = EXT3_ACL_NOT_CACHED; + } + if (inode->u.ext3_i.i_default_acl && + inode->u.ext3_i.i_default_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(inode->u.ext3_i.i_default_acl); + inode->u.ext3_i.i_default_acl = EXT3_ACL_NOT_CACHED; + } +} + +#else +# define ext3_clear_inode NULL +#endif + static struct super_operations ext3_sops = { read_inode: ext3_read_inode, /* BKL held */ write_inode: ext3_write_inode, /* BKL not held. Don't need */ @@ -464,6 +487,7 @@ unlockfs: ext3_unlockfs, /* BKL not held. We take it */ statfs: ext3_statfs, /* BKL held */ remount_fs: ext3_remount, /* BKL held */ + clear_inode: ext3_clear_inode, /* BKL not needed. */ }; static int want_value(char *value, char *option) @@ -501,10 +525,12 @@ */ static int parse_options (char * options, unsigned long * sb_block, struct ext3_sb_info *sbi, + unsigned long *mount_flags, unsigned long * inum, 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; @@ -517,6 +543,20 @@ 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 +#ifdef CONFIG_EXT3_FS_POSIX_ACL + if (!strcmp(this_char, "acl")) + *mount_flags |= MS_POSIXACL; + else if (!strcmp(this_char, "noacl")) + *mount_flags &= ~MS_POSIXACL; + else +#endif if (!strcmp (this_char, "bsddf")) clear_opt (*mount_options, MINIX_DF); else if (!strcmp (this_char, "nouid32")) { @@ -931,7 +971,17 @@ sbi->s_mount_opt = 0; sbi->s_resuid = EXT3_DEF_RESUID; sbi->s_resgid = EXT3_DEF_RESGID; - if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0)) { + + /* Default extended attribute flags */ +#ifdef CONFIG_EXT3_FS_XATTR_USER + /* set_opt(sbi->s_mount_opt, XATTR_USER); */ +#endif +#ifdef CONFIG_EXT3_FS_POSIX_ACL + /* sb->s_flags |= MS_POSIXACL; */ +#endif + + if (!parse_options ((char *) data, &sb_block, sbi, &sb->s_flags, + &journal_inum, 0)) { sb->s_dev = 0; goto out_fail; } @@ -1649,19 +1699,21 @@ { struct ext3_super_block * es; struct ext3_sb_info *sbi = EXT3_SB(sb); - unsigned long tmp; + unsigned long mount_flags = sb->s_flags, tmp; clear_ro_after(sb); /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options(data, &tmp, sbi, &tmp, 1)) + if (!parse_options(data, &tmp, sbi, &mount_flags, &tmp, 1)) return -EINVAL; if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) ext3_abort(sb, __FUNCTION__, "Abort forced by user"); + sb->s_flags = mount_flags; + es = sbi->s_es; ext3_init_journal_params(sbi, sbi->s_journal); @@ -1821,11 +1873,38 @@ old_sync_dquot = ext3_qops.sync_dquot; ext3_qops.sync_dquot = ext3_sync_dquot; #endif - return register_filesystem(&ext3_fs_type); + int error = init_ext3_xattr(); + if (error) + return error; + error = init_ext3_xattr_user(); + if (error) + goto fail; + error = init_ext3_xattr_trusted(); + if (error) + goto fail2; + error = init_ext3_acl(); + if (error) + goto fail3; + error = register_filesystem_lifo(&ext3_fs_type); + if (!error) + return 0; + + exit_ext3_acl(); +fail3: + exit_ext3_xattr_trusted(); +fail2: + exit_ext3_xattr_user(); +fail: + exit_ext3_xattr(); + return error; } static void __exit exit_ext3_fs(void) { + exit_ext3_acl(); + exit_ext3_xattr_trusted(); + exit_ext3_xattr_user(); + exit_ext3_xattr(); unregister_filesystem(&ext3_fs_type); } diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/symlink.c linux-2.4.22-ppc-dev/fs/ext3/symlink.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/symlink.c 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/ext3/symlink.c 2009-08-07 06:36:09.000000000 +0200 @@ -20,6 +20,7 @@ #include #include #include +#include static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen) { @@ -33,7 +34,20 @@ 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 */ }; diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/xattr.c linux-2.4.22-ppc-dev/fs/ext3/xattr.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/xattr.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext3/xattr.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,1221 @@ +/* + * 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(handle_t *, struct inode *, + struct ext3_xattr_header *); +static void ext3_xattr_cache_remove(struct buffer_head *); +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(handle, inode, header) NULL +# define ext3_xattr_cache_remove(bh) while(0) {} +# 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, + const 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); +} + +/* + * 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) { + /* assert(header == HDR(bh)); */ + if (header->h_refcount != cpu_to_le32(1)) + goto skip_get_write_access; + /* ext3_journal_get_write_access() requires an unlocked bh, + which complicates things here. */ + error = ext3_journal_get_write_access(handle, bh); + if (error) + goto cleanup; + lock_buffer(bh); + if (header->h_refcount == cpu_to_le32(1)) { + ea_bdebug(bh, "modifying in-place"); + ext3_xattr_cache_remove(bh); + /* keep the buffer locked while modifying it. */ + } else { + int offset; + + unlock_buffer(bh); + journal_release_buffer(handle, bh); + skip_get_write_access: + ea_bdebug(bh, "cloning"); + header = kmalloc(bh->b_size, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memcpy(header, HDR(bh), bh->b_size); + header->h_refcount = cpu_to_le32(1); + offset = (char *)header - bh->b_data; + here = ENTRY((char *)here + offset); + last = ENTRY((char *)last + offset); + } + } else { + /* Allocate a buffer where we construct the new block. */ + header = kmalloc(sb->s_blocksize, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + memset(header, 0, sb->s_blocksize); + end = (char *)header + sb->s_blocksize; + header->h_magic = cpu_to_le32(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))) { + /* This block is now empty. */ + if (bh && header == HDR(bh)) + unlock_buffer(bh); /* we were modifying in-place. */ + error = ext3_xattr_set_handle2(handle, inode, bh, NULL); + } else { + ext3_xattr_rehash(header, here); + if (bh && header == HDR(bh)) + unlock_buffer(bh); /* we were modifying in-place. */ + error = ext3_xattr_set_handle2(handle, inode, bh, 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; + int error; + + if (header) { + new_bh = ext3_xattr_cache_find(handle, inode, header); + if (new_bh) { + /* We found an identical block in the cache. The + * block returned is locked. The old block will + * be released after updating the inode. + */ + ea_bdebug(new_bh, "%s block %ld", + (old_bh == new_bh) ? "keeping" : "reusing", + new_bh->b_blocknr); + + error = -EDQUOT; + /* How can we enforce the allocation? */ + if (DQUOT_ALLOC_BLOCK(inode, 1)) { + unlock_buffer(new_bh); + journal_release_buffer(handle, new_bh); + goto cleanup; + } + HDR(new_bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(new_bh)->h_refcount) + 1); + ea_bdebug(new_bh, "refcount now=%d", + le32_to_cpu(HDR(new_bh)->h_refcount)); + unlock_buffer(new_bh); + } else if (old_bh && header == HDR(old_bh)) { + /* Keep this block. No need to lock the block as we + * don't need to change the reference count. */ + 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. + */ + + error = ext3_journal_get_write_access(handle, old_bh); + if (error) + goto cleanup; + lock_buffer(old_bh); + if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) { + /* Free the old block. */ + ea_bdebug(old_bh, "freeing"); + ext3_free_blocks(handle, inode, old_bh->b_blocknr, 1); + + /* ext3_forget() calls bforget() for us, but we + let our caller release old_bh, so we need to + duplicate the handle before. */ + get_bh(old_bh); + ext3_forget(handle, 1, inode, old_bh,old_bh->b_blocknr); + } else { + /* Decrement the refcount only. */ + HDR(old_bh)->h_refcount = cpu_to_le32( + le32_to_cpu(HDR(old_bh)->h_refcount) - 1); + DQUOT_FREE_BLOCK(inode, 1); + ext3_journal_dirty_metadata(handle, old_bh); + ea_bdebug(old_bh, "refcount now=%d", + le32_to_cpu(HDR(old_bh)->h_refcount)); + } + unlock_buffer(old_bh); + } + +cleanup: + 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; + + down_write(&EXT3_I(inode)->xattr_sem); + 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; + } + ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count))); + if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) || + HDR(bh)->h_blocks != cpu_to_le32(1)) { + ext3_error(inode->i_sb, "ext3_xattr_delete_inode", + "inode %ld: bad block %d", inode->i_ino, + EXT3_I(inode)->i_file_acl); + goto cleanup; + } + if (ext3_journal_get_write_access(handle, bh) != 0) + goto cleanup; + lock_buffer(bh); + if (HDR(bh)->h_refcount == cpu_to_le32(1)) { + ext3_xattr_cache_remove(bh); + ext3_free_blocks(handle, inode, EXT3_I(inode)->i_file_acl, 1); + + /* ext3_forget() calls bforget() for us, but we release + old_bh blow, so we need to duplicate the handle before. */ + get_bh(bh); + ext3_forget(handle, 1, inode, bh, EXT3_I(inode)->i_file_acl); + } else { + 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); + } + ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount)); + unlock_buffer(bh); + EXT3_I(inode)->i_file_acl = 0; + +cleanup: + brelse(bh); + up_write(&EXT3_I(inode)->xattr_sem); +} + +/* + * 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_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(handle_t *handle, struct inode *inode, + struct ext3_xattr_header *header) +{ + __u32 hash = le32_to_cpu(header->h_hash); + struct mb_cache_entry *ce; + + if (!header->h_hash) + return NULL; /* never share */ + ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); + ce = mb_cache_entry_find_first(ext3_xattr_cache, 0, inode->i_dev, hash); + while (ce) { + struct buffer_head *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 { + /* ext3_journal_get_write_access() requires an unlocked + bh, which complicates things here. */ + if (ext3_journal_get_write_access(handle, bh) != 0) + return NULL; + lock_buffer(bh); + if (le32_to_cpu(HDR(bh)->h_refcount) > + EXT3_XATTR_REFCOUNT_MAX) { + ea_idebug(inode, "block %ld refcount %d>%d", + ce->e_block, + le32_to_cpu(HDR(bh)->h_refcount), + EXT3_XATTR_REFCOUNT_MAX); + } else if (!ext3_xattr_cmp(header, HDR(bh))) { + ea_bdebug(bh, "b_count=%d", + atomic_read(&(bh->b_count))); + mb_cache_entry_release(ce); + /* buffer will be unlocked by caller */ + return bh; + } + unlock_buffer(bh); + journal_release_buffer(handle, bh); + brelse(bh); + } + ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash); + } + return NULL; +} + +/* + * ext3_xattr_cache_remove() + * + * Remove the cache entry of a block from the cache. Called when a + * block becomes invalid. + */ +static void +ext3_xattr_cache_remove(struct buffer_head *bh) +{ + struct mb_cache_entry *ce; + + ce = mb_cache_entry_get(ext3_xattr_cache, bh->b_dev, bh->b_blocknr); + if (ce) { + ea_bdebug(bh, "removing (%d cache entries remaining)", + atomic_read(&ext3_xattr_cache->c_entry_count)-1); + mb_cache_entry_free(ce); + } else + ea_bdebug(bh, "no cache entry"); +} + +#define NAME_HASH_SHIFT 5 +#define VALUE_HASH_SHIFT 16 + +/* + * 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_index), 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 */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/xattr_trusted.c linux-2.4.22-ppc-dev/fs/ext3/xattr_trusted.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/xattr_trusted.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext3/xattr_trusted.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,76 @@ +/* + * 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); +} + +void +exit_ext3_xattr_trusted(void) +{ + ext3_xattr_unregister(EXT3_XATTR_INDEX_TRUSTED, + &ext3_xattr_trusted_handler); +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/ext3/xattr_user.c linux-2.4.22-ppc-dev/fs/ext3/xattr_user.c --- linux-2.4.22-ppc-dev.orig/fs/ext3/xattr_user.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/ext3/xattr_user.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,96 @@ +/* + * 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); +} + +void +exit_ext3_xattr_user(void) +{ + ext3_xattr_unregister(EXT3_XATTR_INDEX_USER, + &ext3_xattr_user_handler); +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/jbd/journal.c linux-2.4.22-ppc-dev/fs/jbd/journal.c --- linux-2.4.22-ppc-dev.orig/fs/jbd/journal.c 2009-08-07 06:23:22.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/jbd/journal.c 2009-08-07 06:36:08.000000000 +0200 @@ -48,9 +48,7 @@ EXPORT_SYMBOL(journal_get_undo_access); EXPORT_SYMBOL(journal_dirty_data); EXPORT_SYMBOL(journal_dirty_metadata); -#if 0 EXPORT_SYMBOL(journal_release_buffer); -#endif EXPORT_SYMBOL(journal_forget); #if 0 EXPORT_SYMBOL(journal_sync_buffer); diff -Naur linux-2.4.22-ppc-dev.orig/fs/jbd/transaction.c linux-2.4.22-ppc-dev/fs/jbd/transaction.c --- linux-2.4.22-ppc-dev.orig/fs/jbd/transaction.c 2009-08-07 06:32:31.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/jbd/transaction.c 2009-08-07 06:36:08.000000000 +0200 @@ -630,6 +630,7 @@ error = 0; spin_lock(&journal_datalist_lock); + handle->h_last_buffer_credits = 0; /* The buffer is already part of this transaction if * b_transaction or b_next_transaction points to it. */ @@ -648,6 +649,7 @@ J_ASSERT_JH(jh, handle->h_buffer_credits > 0); handle->h_buffer_credits--; + handle->h_last_buffer_credits++; goto done_locked; } @@ -723,6 +725,7 @@ J_ASSERT(handle->h_buffer_credits > 0); handle->h_buffer_credits--; + handle->h_last_buffer_credits++; /* Finally, if the buffer is not journaled right now, we need to * make sure it doesn't get written to disk before the caller @@ -1178,41 +1181,30 @@ return 0; } -#if 0 /* * journal_release_buffer: undo a get_write_access without any buffer * updates, if the update decided in the end that it didn't need access. * * journal_get_write_access() can block, so it is quite possible for a * journaling component to decide after the write access is returned - * that global state has changed and the update is no longer required. */ + * that global state has changed and the update is no longer required. + * + * We leave the buffer attached to t_reserved_list because even though this + * handle doesn't want it, some other concurrent handle may want to journal + * this buffer. If that handle is curently in between get_write_access() and + * journal_dirty_metadata() then it expects the buffer to be reserved. If + * we were to rip it off t_reserved_list here, the other handle will explode + * when journal_dirty_metadata is presented with a non-reserved buffer. + * + * If nobody really wants to journal this buffer then it will be thrown + * away at the start of commit. + */ void journal_release_buffer (handle_t *handle, struct buffer_head *bh) { - transaction_t *transaction = handle->h_transaction; - journal_t *journal = transaction->t_journal; - struct journal_head *jh = bh2jh(bh); - - lock_journal(journal); - JBUFFER_TRACE(jh, "entry"); - - /* If the buffer is reserved but not modified by this - * transaction, then it is safe to release it. In all other - * cases, just leave the buffer as it is. */ - - spin_lock(&journal_datalist_lock); - if (jh->b_jlist == BJ_Reserved && jh->b_transaction == transaction && - !buffer_jdirty(jh2bh(jh))) { - JBUFFER_TRACE(jh, "unused: refiling it"); - handle->h_buffer_credits++; - __journal_refile_buffer(jh); - } - spin_unlock(&journal_datalist_lock); - - JBUFFER_TRACE(jh, "exit"); - unlock_journal(journal); + BUFFER_TRACE(bh, "entry"); + handle->h_buffer_credits += handle->h_last_buffer_credits; } -#endif /** * void journal_forget() - bforget() for potentially-journaled buffers. diff -Naur linux-2.4.22-ppc-dev.orig/fs/jfs/jfs_xattr.h linux-2.4.22-ppc-dev/fs/jfs/jfs_xattr.h --- linux-2.4.22-ppc-dev.orig/fs/jfs/jfs_xattr.h 2009-08-07 06:23:18.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/jfs/jfs_xattr.h 2009-08-07 06:36:08.000000000 +0200 @@ -52,8 +52,10 @@ #define END_EALIST(ealist) \ ((struct jfs_ea *) (((char *) (ealist)) + EALIST_SIZE(ealist))) -extern int __jfs_setxattr(struct inode *, const char *, void *, size_t, int); -extern int jfs_setxattr(struct dentry *, const char *, void *, size_t, int); +extern int __jfs_setxattr(struct inode *, const char *, const void *, size_t, + int); +extern int jfs_setxattr(struct dentry *, const char *, const void *, size_t, + int); extern ssize_t __jfs_getxattr(struct inode *, const char *, void *, size_t); extern ssize_t jfs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t jfs_listxattr(struct dentry *, char *, size_t); diff -Naur linux-2.4.22-ppc-dev.orig/fs/jfs/xattr.c linux-2.4.22-ppc-dev/fs/jfs/xattr.c --- linux-2.4.22-ppc-dev.orig/fs/jfs/xattr.c 2009-08-07 06:23:18.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/jfs/xattr.c 2009-08-07 06:36:08.000000000 +0200 @@ -641,7 +641,7 @@ } static int can_set_xattr(struct inode *inode, const char *name, - void *value, size_t value_len) + const void *value, size_t value_len) { if (IS_RDONLY(inode)) return -EROFS; @@ -660,7 +660,7 @@ return permission(inode, MAY_WRITE); } -int __jfs_setxattr(struct inode *inode, const char *name, void *value, +int __jfs_setxattr(struct inode *inode, const char *name, const void *value, size_t value_len, int flags) { struct jfs_ea_list *ealist; @@ -799,7 +799,7 @@ return rc; } -int jfs_setxattr(struct dentry *dentry, const char *name, void *value, +int jfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t value_len, int flags) { if (value == NULL) { /* empty EA, do not remove */ @@ -882,12 +882,18 @@ 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; @@ -931,6 +937,17 @@ 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); diff -Naur linux-2.4.22-ppc-dev.orig/fs/lockd/svc.c linux-2.4.22-ppc-dev/fs/lockd/svc.c --- linux-2.4.22-ppc-dev.orig/fs/lockd/svc.c 2009-08-07 06:23:20.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/lockd/svc.c 2009-08-07 06:36:08.000000000 +0200 @@ -389,6 +389,7 @@ #define NLM_NRVERS (sizeof(nlmsvc_version)/sizeof(nlmsvc_version[0])) struct svc_program nlmsvc_program = { + NULL, /* last registered program */ NLM_PROGRAM, /* program number */ 1, NLM_NRVERS-1, /* version range */ NLM_NRVERS, /* number of entries in nlmsvc_version */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/mbcache.c linux-2.4.22-ppc-dev/fs/mbcache.c --- linux-2.4.22-ppc-dev.orig/fs/mbcache.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/mbcache.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,649 @@ +/* + * 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) + +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_takeout); +EXPORT_SYMBOL(mb_cache_entry_free); +EXPORT_SYMBOL(mb_cache_entry_dup); +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 + + +/* + * 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(atomic_read(&ce->e_used) == 0); + 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) +{ + if (atomic_dec_and_test(&ce->e_used)) { + if (__mb_cache_entry_is_hashed(ce)) + list_add_tail(&ce->e_lru_list, &mb_cache_lru_list); + else { + spin_unlock(&mb_cache_spinlock); + __mb_cache_entry_forget(ce, GFP_KERNEL); + return; + } + } + 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_del(&ce->e_lru_list); + __mb_cache_entry_unhash(ce); + list_add_tail(&ce->e_lru_list, &free_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_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_index)) + 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_del(&ce->e_lru_list); + list_add_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_del(&ce->e_lru_list); + list_add_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; + atomic_set(&ce->e_used, 1); + } + 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(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_takeout() + * + * Take a cache entry out of the cache, making it invalid. The entry can later + * be re-inserted using mb_cache_entry_insert(), or released using + * mb_cache_entry_release(). + */ +void +mb_cache_entry_takeout(struct mb_cache_entry *ce) +{ + spin_lock(&mb_cache_spinlock); + mb_assert(list_empty(&ce->e_lru_list)); + __mb_cache_entry_unhash(ce); + spin_unlock(&mb_cache_spinlock); +} + + +/* + * mb_cache_entry_free() + * + * This is equivalent to the sequence mb_cache_entry_takeout() -- + * mb_cache_entry_release(). + */ +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_dup() + * + * Duplicate a handle to a cache entry (does not duplicate the cache entry + * itself). After the call, both the old and the new handle must be released. + */ +struct mb_cache_entry * +mb_cache_entry_dup(struct mb_cache_entry *ce) +{ + atomic_inc(&ce->e_used); + return 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. + */ +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) { + if (!list_empty(&ce->e_lru_list)) + list_del_init(&ce->e_lru_list); + atomic_inc(&ce->e_used); + 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) { + if (!list_empty(&ce->e_lru_list)) + list_del_init(&ce->e_lru_list); + atomic_inc(&ce->e_used); + 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) + diff -Naur linux-2.4.22-ppc-dev.orig/fs/namei.c linux-2.4.22-ppc-dev/fs/namei.c --- linux-2.4.22-ppc-dev.orig/fs/namei.c 2009-08-07 06:34:59.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/namei.c 2009-08-07 10:08:20.000000000 +0200 @@ -1080,8 +1080,9 @@ goto exit_dput; } - error = vfs_create(dir->d_inode, dentry, - mode & ~current->fs->umask); + if (!IS_POSIXACL(dir->d_inode)) + mode &= ~current->fs->umask; + error = vfs_create(dir->d_inode, dentry, mode); if (!error) gr_handle_create(dentry, nd->mnt); @@ -1345,7 +1346,8 @@ dentry = lookup_create(&nd, 0); error = PTR_ERR(dentry); - mode &= ~current->fs->umask; + if (!IS_POSIXACL(nd.dentry->d_inode)) + mode &= ~current->fs->umask; if (!IS_ERR(dentry)) { if (gr_handle_chroot_mknod(dentry, nd.mnt, mode)) { error = -EPERM; @@ -1435,9 +1437,10 @@ if (!gr_acl_handle_mkdir(dentry, nd.dentry, nd.mnt)) error = -EACCES; + if (!IS_POSIXACL(nd.dentry->d_inode)) + mode &= ~current->fs->umask; if(!error) - error = vfs_mkdir(nd.dentry->d_inode, dentry, - mode & ~current->fs->umask); + error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); if(!error) gr_handle_create(dentry, nd.mnt); diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfs/Makefile linux-2.4.22-ppc-dev/fs/nfs/Makefile --- linux-2.4.22-ppc-dev.orig/fs/nfs/Makefile 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfs/Makefile 2009-08-07 09:25:26.000000000 +0200 @@ -15,6 +15,7 @@ obj-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o obj-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o obj-$(CONFIG_NFS_DIRECTIO) += direct.o +obj-$(CONFIG_NFS_ACL) += xattr.o obj-m := $(O_TARGET) diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfs/dir.c linux-2.4.22-ppc-dev/fs/nfs/dir.c --- linux-2.4.22-ppc-dev.orig/fs/nfs/dir.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfs/dir.c 2009-08-07 06:36:08.000000000 +0200 @@ -68,6 +68,10 @@ permission: nfs_permission, revalidate: nfs_revalidate, setattr: nfs_notify_change, + listxattr: nfs_listxattr, + getxattr: nfs_getxattr, + setxattr: nfs_setxattr, + removexattr: nfs_removexattr, }; typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); @@ -1082,34 +1086,63 @@ int nfs_permission(struct inode *inode, int mask) { - int error = vfs_permission(inode, mask); - - if (!NFS_PROTO(inode)->access) - goto out; - - if (error == -EROFS) - goto out; - - /* - * Trust UNIX mode bits except: - * - * 1) When override capabilities may have been invoked - * 2) When root squashing may be involved - * 3) When ACLs may overturn a negative answer */ - if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH) - && (current->fsuid != 0) && (current->fsgid != 0) - && error != -EACCES) - goto out; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_access_cache *cache = &NFS_I(inode)->cache_access; + struct rpc_cred *cred; + int mode = inode->i_mode; + int error; - error = NFS_PROTO(inode)->access(inode, mask, 0); + if (mask & MAY_WRITE) { + /* + * + * Nobody gets write access to a read-only fs. + * + */ + if (IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; - if (error == -EACCES && NFS_CLIENT(inode)->cl_droppriv && - current->uid != 0 && current->gid != 0 && - (current->fsuid != current->uid || current->fsgid != current->gid)) - error = NFS_PROTO(inode)->access(inode, mask, 1); + /* + * + * Nobody gets write access to an immutable file. + * + */ + if (IS_IMMUTABLE(inode)) + return -EACCES; + } - out: - return error; + if ((server->flags & NFS_MOUNT_NOACL) || !NFS_PROTO(inode)->access) + goto out_notsup; + cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + if (cache->cred == cred + && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) { + if (!cache->err) { + /* Is the mask a subset of an accepted mask? */ + if ((cache->mask & mask) == mask) + goto out_cached; + } else { + /* ...or is it a superset of a rejected mask? */ + if ((cache->mask & mask) == cache->mask) + goto out_cached; + } + } + error = NFS_PROTO(inode)->access(inode, cred, mask); + if (!error || error == -EACCES) { + cache->jiffies = jiffies; + if (cache->cred) + put_rpccred(cache->cred); + cache->cred = cred; + cache->mask = mask; + cache->err = error; + return error; + } + put_rpccred(cred); +out_notsup: + nfs_revalidate_inode(NFS_SERVER(inode), inode); + return vfs_permission(inode, mask); +out_cached: + put_rpccred(cred); + return cache->err; } /* diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfs/file.c linux-2.4.22-ppc-dev/fs/nfs/file.c --- linux-2.4.22-ppc-dev.orig/fs/nfs/file.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfs/file.c 2009-08-07 06:36:09.000000000 +0200 @@ -57,6 +57,10 @@ permission: nfs_permission, revalidate: nfs_revalidate, setattr: nfs_notify_change, + listxattr: nfs_listxattr, + getxattr: nfs_getxattr, + setxattr: nfs_setxattr, + removexattr: nfs_removexattr, }; /* Hack for future NFS swap support */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfs/inode.c linux-2.4.22-ppc-dev/fs/nfs/inode.c --- linux-2.4.22-ppc-dev.orig/fs/nfs/inode.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfs/inode.c 2009-08-07 06:36:09.000000000 +0200 @@ -65,6 +65,24 @@ show_options: nfs_show_options, }; +#ifdef CONFIG_NFS_ACL +static struct rpc_stat nfs_acl_rpcstat = { &nfs_acl_program }; +static struct rpc_version * nfs_acl_version[] = { + NULL, + NULL, + NULL, + &nfs_acl_version3, +}; + +struct rpc_program nfs_acl_program = { + "nfs_acl", + NFS3_ACL_PROGRAM, + sizeof(nfs_acl_version) / sizeof(nfs_acl_version[0]), + nfs_acl_version, + &nfs_acl_rpcstat, +}; +#endif /* CONFIG_NFS_ACL */ + /* * RPC cruft for NFS */ @@ -146,10 +164,14 @@ static void nfs_clear_inode(struct inode *inode) { - struct rpc_cred *cred = NFS_I(inode)->mm_cred; + struct nfs_inode_info *nfsi = NFS_I(inode); + struct rpc_cred *cred = nfsi->mm_cred; if (cred) put_rpccred(cred); + cred = nfsi->cache_access.cred; + if (cred) + put_rpccred(cred); } void @@ -168,6 +190,11 @@ if ((rpc = server->client) != NULL) rpc_shutdown_client(rpc); +#ifdef CONFIG_NFS_ACL + if ((rpc = server->acl_client) != NULL) + rpc_shutdown_client(rpc); +#endif /* CONFIG_NFS_ACL */ + nfs_reqlist_free(server); if (!(server->flags & NFS_MOUNT_NONLM)) @@ -186,6 +213,11 @@ /* -EIO all pending I/O */ if ((rpc = server->client) != NULL) rpc_killall_tasks(rpc); + +#ifdef CONFIG_NFS_ACL + if ((rpc = server->acl_client) != NULL) + rpc_killall_tasks(rpc); +#endif /* CONFIG_NFS_ACL */ } @@ -383,6 +415,26 @@ clnt->cl_chatty = 1; server->client = clnt; +#ifdef CONFIG_NFS_ACL + if (version == 3) { + clnt = rpc_create_client(xprt, server->hostname, + &nfs_acl_program, version, authflavor); + if (clnt == NULL) { + rpc_shutdown_client(server->client); + goto out_no_client; + } + + clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0; + clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0; + clnt->cl_droppriv = (data->flags & NFS_MOUNT_BROKEN_SUID) ?1:0; + clnt->cl_chatty = 1; + server->acl_client = clnt; + + /* Initially assume the nfs_acl program is supported */ + server->flags |= (NFS_SOLARIS_GETACL | NFS_SOLARIS_SETACL); + } +#endif /* CONFIG_NFS_ACL */ + /* Fire up rpciod if not yet running */ if (rpciod_up() != 0) goto out_no_iod; @@ -397,6 +449,9 @@ data->flags &= ~NFS_MOUNT_VER3; rpciod_down(); rpc_shutdown_client(server->client); +#ifdef CONFIG_NFS_ACL + rpc_shutdown_client(server->acl_client); +#endif /* CONFIG_NFS_ACL */ goto nfsv3_try_again; } @@ -493,6 +548,9 @@ printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); out_shutdown: rpc_shutdown_client(server->client); +#ifdef CONFIG_NFS_ACL + rpc_shutdown_client(server->acl_client); +#endif /* CONFIG_NFS_ACL */ goto out_free_host; out_no_client: @@ -568,6 +626,7 @@ { NFS_MOUNT_NOAC, ",noac", "" }, { NFS_MOUNT_NONLM, ",nolock", ",lock" }, { NFS_MOUNT_BROKEN_SUID, ",broken_suid", "" }, + { NFS_MOUNT_NOACL, ",noacl", "" }, { 0, NULL, NULL } }; struct proc_nfs_info *nfs_infop; @@ -623,6 +682,13 @@ nfs_zap_caches(inode); } +static struct inode_operations nfs_special_inode_operations = { + listxattr: nfs_listxattr, + getxattr: nfs_getxattr, + setxattr: nfs_setxattr, + removexattr: nfs_removexattr, +}; + /* * Fill in inode information from the fattr. */ @@ -648,8 +714,10 @@ inode->i_fop = &nfs_dir_operations; } else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; - else + else { + inode->i_op = &nfs_special_inode_operations; init_special_inode(inode, inode->i_mode, fattr->rdev); + } memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)); } nfs_refresh_inode(inode, fattr); @@ -1068,6 +1136,16 @@ NFS_CACHE_ISIZE(inode) = new_size; inode->i_size = new_isize; + if (inode->i_mode != fattr->mode || + inode->i_uid != fattr->uid || + inode->i_gid != fattr->gid) { + struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred; + if (*cred) { + put_rpccred(*cred); + *cred = NULL; + } + } + inode->i_mode = fattr->mode; inode->i_nlink = fattr->nlink; inode->i_uid = fattr->uid; diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfs/nfs3proc.c linux-2.4.22-ppc-dev/fs/nfs/nfs3proc.c --- linux-2.4.22-ppc-dev.orig/fs/nfs/nfs3proc.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfs/nfs3proc.c 2009-08-07 06:36:08.000000000 +0200 @@ -14,6 +14,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_PROC @@ -117,12 +118,13 @@ } static int -nfs3_proc_access(struct inode *inode, int mode, int ruid) +nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) { struct nfs_fattr fattr; struct nfs3_accessargs arg = { NFS_FH(inode), 0 }; struct nfs3_accessres res = { &fattr, 0 }; - int status, flags; + struct rpc_message msg = { NFS3PROC_ACCESS, &arg, &res, cred }; + int status; dprintk("NFS call access\n"); fattr.valid = 0; @@ -140,8 +142,7 @@ if (mode & MAY_EXEC) arg.access |= NFS3_ACCESS_EXECUTE; } - flags = (ruid) ? RPC_CALL_REALUID : 0; - status = rpc_call(NFS_CLIENT(inode), NFS3PROC_ACCESS, &arg, &res, flags); + status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); nfs_refresh_inode(inode, &fattr); dprintk("NFS reply access\n"); @@ -509,6 +510,238 @@ return status; } +#ifdef CONFIG_NFS_ACL +static int +nfs3_proc_checkacls(struct inode *inode) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr; + struct nfs3_getaclargs args; + struct nfs3_getaclres res = { &fattr }; + int status; + + if (!(server->flags & NFS_SOLARIS_GETACL) || + (server->flags & NFS_MOUNT_NOACL)) + return -EOPNOTSUPP; + + args.fh = NFS_FH(inode); + args.mask = NFS3_ACLCNT | NFS3_DFACLCNT; + + dprintk("NFS call getacl\n"); + status = rpc_call(NFS_ACL_CLIENT(inode), NFS3_ACL_PROC_GETACL, + &args, &res, 0); + dprintk("NFS reply getacl: %d\n", status); + + if (status) { + if (status == -ENOSYS) { + dprintk("NFS_ACL GETACL RPC not supported " + "(will not retry)\n"); + server->flags &= ~NFS_SOLARIS_GETACL; + status = -EOPNOTSUPP; + } else if (status == -ENOTSUPP) + status = -EOPNOTSUPP; + goto getout; + } + if ((args.mask & res.mask) != args.mask) { + status = -EIO; + goto getout; + } + + status = nfs_refresh_inode(inode, &fattr); + +getout: + posix_acl_release(res.acl_access); + posix_acl_release(res.acl_default); + + if (!status) { + /* The (count > 4) test will exclude ACL entries from the list + of names even if their ACL_GROUP_ENTRY and ACL_MASK have + different permissions. Getacl still returns these as + four-entry ACLs, instead of minimal (three-entry) ACLs. */ + + if ((args.mask & NFS3_ACLCNT) && res.acl_access_count > 4) + status |= ACL_TYPE_ACCESS; + if ((args.mask & NFS3_DFACLCNT) && res.acl_default_count > 0) + status |= ACL_TYPE_DEFAULT; + } + return status; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +static struct posix_acl * +nfs3_proc_getacl(struct inode *inode, int type) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr; + struct nfs3_getaclargs args; + struct nfs3_getaclres res = { &fattr }; + struct posix_acl *acl = NULL; + int status; + + if (!(server->flags & NFS_SOLARIS_GETACL) || + (server->flags & NFS_MOUNT_NOACL)) + return ERR_PTR(-EOPNOTSUPP); + + args.fh = NFS_FH(inode); + switch(type) { + case ACL_TYPE_ACCESS: + args.mask = NFS3_ACLCNT|NFS3_ACL; + break; + + case ACL_TYPE_DEFAULT: + args.mask = NFS3_DFACLCNT|NFS3_DFACL; + break; + + default: + return ERR_PTR(-EINVAL); + } + + dprintk("NFS call getacl\n"); + status = rpc_call(NFS_ACL_CLIENT(inode), NFS3_ACL_PROC_GETACL, + &args, &res, 0); + dprintk("NFS reply getacl: %d\n", status); + + if (status) { + if (status == -ENOSYS) { + dprintk("NFS_ACL GETACL RPC not supported " + "(will not retry)\n"); + server->flags &= ~NFS_SOLARIS_GETACL; + status = -EOPNOTSUPP; + } else if (status == -ENOTSUPP) + status = -EOPNOTSUPP; + goto getout; + } + if ((args.mask & res.mask) != args.mask) { + status = -EIO; + goto getout; + } + + if (type == ACL_TYPE_ACCESS) { + if (res.acl_access) { + mode_t mode = inode->i_mode; + if (!posix_acl_equiv_mode(res.acl_access, &mode) && + inode->i_mode == mode) { + posix_acl_release(res.acl_access); + res.acl_access = NULL; + } + } + acl = res.acl_access; + res.acl_access = NULL; + } else { + acl = res.acl_default; + res.acl_default = NULL; + } + + status = nfs_refresh_inode(inode, &fattr); + +getout: + posix_acl_release(res.acl_access); + posix_acl_release(res.acl_default); + + if (status) { + posix_acl_release(acl); + acl = ERR_PTR(status); + } + return acl; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +static int +nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr; + struct nfs3_setaclargs args = { }; + int status; + + if (!(server->flags & NFS_SOLARIS_SETACL) || + (server->flags & NFS_MOUNT_NOACL)) + return -EOPNOTSUPP; + + /* We are doing this here, because XDR marshalling can only + return -ENOMEM. */ + if (acl && acl->a_count > NFS3_ACL_MAX_ENTRIES) + return -EINVAL; + args.inode = inode; + args.mask = NFS3_ACL|NFS3_DFACL; + if (S_ISDIR(inode->i_mode)) { + switch(type) { + case ACL_TYPE_ACCESS: + args.acl_access = acl; + args.acl_default = NFS_PROTO(inode)->getacl( + inode, ACL_TYPE_DEFAULT); + status = PTR_ERR(args.acl_default); + if (IS_ERR(args.acl_default)) { + args.acl_default = NULL; + goto cleanup; + } + break; + + case ACL_TYPE_DEFAULT: + args.acl_access = NFS_PROTO(inode)->getacl( + inode, ACL_TYPE_ACCESS); + status = PTR_ERR(args.acl_access); + if (IS_ERR(args.acl_access)) { + args.acl_access = NULL; + goto cleanup; + } + args.acl_default = acl; + break; + + default: + status = -EINVAL; + goto cleanup; + } + } else { + status = -EINVAL; + if (type != ACL_TYPE_ACCESS) + goto cleanup; + args.mask = NFS3_ACL; + args.acl_access = acl; + } + if (args.acl_access == NULL) { + args.acl_access = posix_acl_from_mode(inode->i_mode, + GFP_KERNEL); + status = PTR_ERR(args.acl_access); + if (IS_ERR(args.acl_access)) { + args.acl_access = NULL; + goto cleanup; + } + } + args.mask = NFS3_ACL | (args.acl_default ? NFS3_DFACL : 0); + + dprintk("NFS call setacl\n"); + status = rpc_call(NFS_ACL_CLIENT(inode), NFS3_ACL_PROC_SETACL, + &args, &fattr, 0); + dprintk("NFS reply setacl: %d\n", status); + + if (status) { + if (status == -ENOSYS) { + dprintk("NFS_ACL SETACL RPC not supported" + "(will not retry)\n"); + server->flags &= ~NFS_SOLARIS_SETACL; + status = -EOPNOTSUPP; + } else if (status == -ENOTSUPP) + status = -EOPNOTSUPP; + } else { + /* Force an attribute cache update if the file mode + * has changed. */ + if (inode->i_mode != fattr.mode) + NFS_CACHEINV(inode); + status = nfs_refresh_inode(inode, &fattr); + } + +cleanup: + if (args.acl_access != acl) + posix_acl_release(args.acl_access); + if (args.acl_default != acl) + posix_acl_release(args.acl_default); + return status; +} +#endif /* CONFIG_NFS_ACL */ + extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); struct nfs_rpc_ops nfs_v3_clientops = { @@ -535,4 +768,9 @@ nfs3_proc_mknod, nfs3_proc_statfs, nfs3_decode_dirent, +#ifdef CONFIG_NFS_ACL + nfs3_proc_getacl, + nfs3_proc_setacl, + nfs3_proc_checkacls, +#endif /* CONFIG_NFS_ACL */ }; diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfs/nfs3xdr.c linux-2.4.22-ppc-dev/fs/nfs/nfs3xdr.c --- linux-2.4.22-ppc-dev.orig/fs/nfs/nfs3xdr.c 2009-08-07 06:23:19.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfs/nfs3xdr.c 2009-08-07 06:36:09.000000000 +0200 @@ -21,6 +21,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_XDR @@ -47,6 +48,7 @@ #define NFS3_fsinfo_sz #define NFS3_pathconf_sz #define NFS3_entry_sz NFS3_filename_sz+3 +#define NFS3_acl_sz 1+NFS3_ACL_MAX_ENTRIES*3 #define NFS3_enc_void_sz 0 #define NFS3_sattrargs_sz NFS3_fh_sz+NFS3_sattr_sz+3 @@ -63,6 +65,8 @@ #define NFS3_linkargs_sz NFS3_fh_sz+NFS3_diropargs_sz #define NFS3_readdirargs_sz NFS3_fh_sz+2 #define NFS3_commitargs_sz NFS3_fh_sz+3 +#define NFS3_getaclargs_sz NFS3_fh_sz+1 +#define NFS3_setaclargs_sz NFS3_fh_sz+1+2*(1+NFS3_acl_sz) #define NFS3_dec_void_sz 0 #define NFS3_attrstat_sz 1+NFS3_fattr_sz @@ -80,6 +84,8 @@ #define NFS3_fsinfores_sz 1+NFS3_post_op_attr_sz+12 #define NFS3_pathconfres_sz 1+NFS3_post_op_attr_sz+6 #define NFS3_commitres_sz 1+NFS3_wcc_data_sz+2 +#define NFS3_getaclres_sz 1+NFS3_post_op_attr_sz+1+2*(1+NFS3_acl_sz) +#define NFS3_setaclres_sz 1+NFS3_post_op_attr_sz /* * Map file type to S_IFMT bits @@ -650,6 +656,48 @@ return 0; } +#ifdef CONFIG_NFS_ACL +/* + * Encode GETACL arguments + */ +static int +nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p, + struct nfs3_getaclargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->mask); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + + return 0; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +/* + * Encode SETACL arguments + */ +static int +nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p, + struct nfs3_setaclargs *args) +{ + struct iovec *iov = req->rq_svec; + u32 *end = (u32 *)((u8 *)iov->iov_base + iov->iov_len); + struct posix_acl *acl; + + p = xdr_encode_fhandle(p, NFS_FH(args->inode)); + *p++ = htonl(args->mask); + acl = (args->mask & NFS3_ACL) ? args->acl_access : NULL; + if (!(p = nfs_acl_encode(p, end, args->inode, acl, 1, 0))) + return -ENOMEM; + acl = (args->mask & NFS3_DFACL) ? args->acl_default : NULL; + if (!(p = nfs_acl_encode(p, end, args->inode, acl, 1, NFS3_ACL_DEFAULT))) + return -ENOMEM; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + + return 0; +} +#endif /* CONFIG_NFS_ACL */ + /* * NFS XDR decode functions */ @@ -1002,6 +1050,59 @@ return 0; } +#ifdef CONFIG_NFS_ACL +/* + * Decode GETACL reply + */ +static int +nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p, + struct nfs3_getaclres *res) +{ + struct iovec *iov = req->rq_rvec; + u32 *end = (u32 *)((u8 *)iov->iov_base + iov->iov_len); + int status = ntohl(*p++); + struct posix_acl **acl; + unsigned int *aclcnt; + + if (status != 0) + return -nfs_stat_to_errno(status); + + p = xdr_decode_post_op_attr(p, res->fattr); + + res->mask = ntohl(*p++); + if (res->mask & ~(NFS3_ACL|NFS3_ACLCNT|NFS3_DFACL|NFS3_DFACLCNT)) + return -EINVAL; + + /* res->acl_{access,default} are released in nfs3_proc_getacl. */ + acl = (res->mask & NFS3_ACL) ? &res->acl_access : NULL; + aclcnt = (res->mask & NFS3_ACLCNT) ? &res->acl_access_count : NULL; + if (!(p = nfs_acl_decode(p, end, aclcnt, acl))) + return -EINVAL; + + acl = (res->mask & NFS3_DFACL) ? &res->acl_default : NULL; + aclcnt = (res->mask & NFS3_DFACLCNT) ? &res->acl_default_count : NULL; + if (!(p = nfs_acl_decode(p, end, aclcnt, acl))) + return -EINVAL; + return 0; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +/* + * Decode setacl reply. + */ +static int +nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) +{ + int status = ntohl(*p++); + + if (status) + return -nfs_stat_to_errno(status); + xdr_decode_post_op_attr(p, fattr); + return 0; +} +#endif /* CONFIG_NFS_ACL */ + #ifndef MAX # define MAX(a, b) (((a) > (b))? (a) : (b)) #endif @@ -1045,3 +1146,16 @@ nfs3_procedures }; +#ifdef CONFIG_NFS_ACL +static struct rpc_procinfo nfs3_acl_procedures[] = { + PROC(null, enc_void, dec_void, 0), + PROC(getacl, getaclargs, getaclres, 1), + PROC(setacl, setaclargs, setaclres, 0), +}; + +struct rpc_version nfs_acl_version3 = { + 3, + sizeof(nfs3_acl_procedures)/sizeof(nfs3_acl_procedures[0]), + nfs3_acl_procedures +}; +#endif /* CONFIG_NFS_ACL */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfs/xattr.c linux-2.4.22-ppc-dev/fs/nfs/xattr.c --- linux-2.4.22-ppc-dev.orig/fs/nfs/xattr.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/nfs/xattr.c 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +ssize_t +nfs_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error=0, pos=0, len=0; + + error = -EOPNOTSUPP; + if (NFS_PROTO(inode)->version == 3 && NFS_PROTO(inode)->checkacls) + error = NFS_PROTO(inode)->checkacls(inode); + if (error < 0) + return error; + +# define output(s) do { \ + if (pos + sizeof(s) <= size) { \ + memcpy(buffer + pos, s, sizeof(s)); \ + pos += sizeof(s); \ + } \ + len += sizeof(s); \ + } while(0) + + if (error & ACL_TYPE_ACCESS) + output("system.posix_acl_access"); + if (error & ACL_TYPE_DEFAULT) + output("system.posix_acl_default"); + +# undef output + + if (!buffer || len <= size) + return len; + return -ERANGE; +} + +ssize_t +nfs_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) +{ + struct inode *inode = dentry->d_inode; + struct posix_acl *acl; + int type, error = 0; + + if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) + type = ACL_TYPE_ACCESS; + else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) + type = ACL_TYPE_DEFAULT; + else + return -EOPNOTSUPP; + + acl = ERR_PTR(-EOPNOTSUPP); + if (NFS_PROTO(inode)->version == 3 && NFS_PROTO(inode)->getacl) + acl = NFS_PROTO(inode)->getacl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + else if (acl) { + if (type == ACL_TYPE_ACCESS && acl->a_count == 0) + error = -ENODATA; + else + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + } else + error = -ENODATA; + + return error; +} + +int +nfs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + struct posix_acl *acl; + int type, error; + + if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) + type = ACL_TYPE_ACCESS; + else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) + type = ACL_TYPE_DEFAULT; + else + return -EOPNOTSUPP; + + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + + error = -EOPNOTSUPP; + if (NFS_PROTO(inode)->version == 3 && NFS_PROTO(inode)->setacl) + error = NFS_PROTO(inode)->setacl(inode, type, acl); + posix_acl_release(acl); + + return error; +} + +int +nfs_removexattr(struct dentry *dentry, const char *name) +{ + struct inode *inode = dentry->d_inode; + int error, type; + + if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) + type = ACL_TYPE_ACCESS; + else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) + type = ACL_TYPE_DEFAULT; + else + return -EOPNOTSUPP; + + error = -EOPNOTSUPP; + if (NFS_PROTO(inode)->version == 3 && NFS_PROTO(inode)->setacl) + error = NFS_PROTO(inode)->setacl(inode, type, NULL); + + return error; +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfsd/export.c linux-2.4.22-ppc-dev/fs/nfsd/export.c --- linux-2.4.22-ppc-dev.orig/fs/nfsd/export.c 2009-08-07 06:23:20.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfsd/export.c 2009-08-07 06:36:08.000000000 +0200 @@ -643,6 +643,7 @@ { NFSEXP_CROSSMNT, {"nohide", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, + { NFSEXP_NOACL, {"no_acl", ""}}, #ifdef MSNFS { NFSEXP_MSNFS, {"msnfs", ""}}, #endif diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfsd/nfs3proc.c linux-2.4.22-ppc-dev/fs/nfsd/nfs3proc.c --- linux-2.4.22-ppc-dev.orig/fs/nfsd/nfs3proc.c 2009-08-07 06:23:20.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfsd/nfs3proc.c 2009-08-07 06:36:08.000000000 +0200 @@ -628,6 +628,105 @@ RETURN_STATUS(nfserr); } +#ifdef CONFIG_NFSD_ACL +/* + * Get the Access and/or Default ACL of a file. + */ +static int +nfsd3_proc_getacl(struct svc_rqst * rqstp, struct nfsd3_getaclargs *argp, + struct nfsd3_getaclres *resp) +{ + svc_fh *fh; + struct posix_acl *acl; + int nfserr = 0; + + fh = fh_copy(&resp->fh, &argp->fh); + if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP))) + RETURN_STATUS(nfserr_inval); + + if (argp->mask & ~(NFS3_ACL|NFS3_ACLCNT|NFS3_DFACL|NFS3_DFACLCNT)) + RETURN_STATUS(nfserr_inval); + resp->mask = argp->mask; + + if (resp->mask & (NFS3_ACL|NFS3_ACLCNT)) { + acl = nfsd_get_posix_acl(fh, ACL_TYPE_ACCESS); + if (IS_ERR(acl)) { + int err = PTR_ERR(acl); + + if (err == -ENODATA || err == -EOPNOTSUPP) + acl = NULL; + else { + nfserr = nfserrno(err); + goto fail; + } + } + if (acl == NULL) { + /* Solaris returns the inode's minimum ACL. */ + + struct inode *inode = fh->fh_dentry->d_inode; + acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); + } + resp->acl_access = acl; + } + if (resp->mask & (NFS3_DFACL|NFS3_DFACLCNT)) { + /* Check how Solaris handles requests for the Default ACL + of a non-directory! */ + + acl = nfsd_get_posix_acl(fh, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) { + int err = PTR_ERR(acl); + + if (err == -ENODATA || err == -EOPNOTSUPP) + acl = NULL; + else { + nfserr = nfserrno(err); + goto fail; + } + } + resp->acl_default = acl; + } + + /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */ + RETURN_STATUS(0); + +fail: + posix_acl_release(resp->acl_access); + posix_acl_release(resp->acl_default); + RETURN_STATUS(nfserr); +} +#endif /* CONFIG_NFSD_ACL */ + +#ifdef CONFIG_NFSD_ACL +/* + * Set the Access and/or Default ACL of a file. + */ +static int +nfsd3_proc_setacl(struct svc_rqst * rqstp, struct nfsd3_setaclargs *argp, + struct nfsd3_attrstat *resp) +{ + svc_fh *fh; + int nfserr = 0; + + fh = fh_copy(&resp->fh, &argp->fh); + nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); + + if (!nfserr) { + nfserr = nfserrno( nfsd_set_posix_acl( + fh, ACL_TYPE_ACCESS, argp->acl_access) ); + } + if (!nfserr) { + nfserr = nfserrno( nfsd_set_posix_acl( + fh, ACL_TYPE_DEFAULT, argp->acl_default) ); + } + + /* argp->acl_{access,default} may have been allocated in + nfs3svc_decode_setaclargs. */ + posix_acl_release(argp->acl_access); + posix_acl_release(argp->acl_default); + RETURN_STATUS(nfserr); +} +#endif /* CONFIG_NFSD_ACL */ + /* * NFSv3 Server procedures. @@ -644,6 +743,7 @@ #define nfsd3_fhandleres nfsd3_attrstat #define nfsd3_attrstatres nfsd3_attrstat #define nfsd3_wccstatres nfsd3_attrstat +#define nfsd3_setaclres nfsd3_attrstat #define nfsd3_createres nfsd3_diropres #define nfsd3_voidres nfsd3_voidargs struct nfsd3_voidargs { int dummy; }; @@ -665,6 +765,7 @@ #define AT 21 /* attributes */ #define pAT (1+AT) /* post attributes - conditional */ #define WC (7+pAT) /* WCC attributes */ +#define ACL (1+NFS3_ACL_MAX_ENTRIES*3) /* Access Control List */ struct svc_procedure nfsd_procedures3[22] = { PROC(null, void, void, void, RC_NOCACHE, ST), @@ -690,3 +791,11 @@ PROC(pathconf, fhandle, pathconf, void, RC_NOCACHE, ST+pAT+6), PROC(commit, commit, commit, fhandle, RC_NOCACHE, ST+WC+2), }; + +#ifdef CONFIG_NFSD_ACL +struct svc_procedure nfsd_acl_procedures3[] = { + PROC(null, void, void, void, RC_NOCACHE, ST), + PROC(getacl, getacl, getacl, getacl, RC_NOCACHE, ST+1+2*(1+ACL)), + PROC(setacl, setacl, setacl, fhandle, RC_NOCACHE, ST+pAT) +}; +#endif /* CONFIG_NFSD_ACL */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfsd/nfs3xdr.c linux-2.4.22-ppc-dev/fs/nfsd/nfs3xdr.c --- linux-2.4.22-ppc-dev.orig/fs/nfsd/nfs3xdr.c 2009-08-07 06:23:20.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfsd/nfs3xdr.c 2009-08-07 06:36:08.000000000 +0200 @@ -15,6 +15,8 @@ #include #include +#include + #define NFSDDBG_FACILITY NFSDDBG_XDR #ifdef NFSD_OPTIMIZE_SPACE @@ -157,9 +159,19 @@ encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) { struct inode *inode = fhp->fh_dentry->d_inode; + mode_t mode = inode->i_mode; + + if (IS_POSIXACL(inode) && !EX_ACL(fhp->fh_export)) { + struct posix_acl *acl = nfsd_get_posix_acl(fhp,ACL_TYPE_ACCESS); + if (!IS_ERR(acl) && acl) { + posix_acl_masq_nfs_mode(acl, &mode); + posix_acl_release(acl); + } + } + *p++ = htonl(nfs3_ftypes[(inode->i_mode & S_IFMT) >> 12]); - *p++ = htonl((u32) inode->i_mode); + *p++ = htonl((u32) mode); *p++ = htonl((u32) inode->i_nlink); *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid)); *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid)); @@ -501,6 +513,51 @@ return xdr_argsize_check(rqstp, p); } +#ifdef CONFIG_NFSD_ACL +int +nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_getaclargs *args) +{ + if (!(p = decode_fh(p, &args->fh))) + return 0; + args->mask = ntohl(*p); p++; + + return xdr_argsize_check(rqstp, p); +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFSD_ACL +int +nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_setaclargs *args) +{ + struct svc_buf *buf = &rqstp->rq_argbuf; + u32 *end = buf->base + buf->buflen; + struct posix_acl **acl; + + if (!(p = decode_fh(p, &args->fh))) + return 0; + args->mask = ntohl(*p++); + if (args->mask & ~(NFS3_ACL|NFS3_ACLCNT|NFS3_DFACL|NFS3_DFACLCNT) || + !xdr_argsize_check(rqstp, p)) + return 0; + + /* argp->acl_{access,default} are released in nfsd3_proc_setacl. */ + acl = (args->mask & NFS3_ACL) ? &args->acl_access : NULL; + if (!(p = nfs_acl_decode(p, end, NULL, acl))) + return 0; + + acl = (args->mask & NFS3_DFACL) ? &args->acl_default : NULL; + if (!(p = nfs_acl_decode(p, end, NULL, acl))) { + posix_acl_release(args->acl_access); + args->acl_access = NULL; + return 0; + } + + return 1; +} +#endif /* CONFIG_NFS_ACL */ + /* * XDR encode functions */ @@ -830,6 +887,46 @@ return xdr_ressize_check(rqstp, p); } + +#ifdef CONFIG_NFSD_ACL +/* GETACL */ +int +nfs3svc_encode_getaclres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_getaclres *resp) +{ + struct dentry *dentry = resp->fh.fh_dentry; + struct svc_buf *buf = &rqstp->rq_resbuf; + u32 *end = buf->base + buf->buflen; + + if (resp->status == 0 && dentry && dentry->d_inode) { + struct inode *inode = dentry->d_inode; + + p = encode_post_op_attr(rqstp, p, &resp->fh); + *p++ = htonl(resp->mask); + if (!(p = nfs_acl_encode(p, end, inode, resp->acl_access, + resp->mask & NFS3_ACL, 0))) + return 0; + if (!(p = nfs_acl_encode(p, end, inode, resp->acl_default, + resp->mask & NFS3_DFACL, + NFS3_ACL_DEFAULT))) + return 0; + } + + return xdr_ressize_check(rqstp, p); +} +#endif /* CONFIG_NFS_ACL */ + +/* SETACL */ +int +nfs3svc_encode_setaclres(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_attrstat *resp) +{ + if (resp->status == 0) + p = encode_post_op_attr(rqstp, p, &resp->fh); + + return xdr_ressize_check(rqstp, p); +} + /* * XDR release functions */ @@ -849,3 +946,15 @@ fh_put(&resp->fh2); return 1; } + +#ifdef CONFIG_NFSD_ACL +int +nfs3svc_release_getacl(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_getaclres *resp) +{ + fh_put(&resp->fh); + posix_acl_release(resp->acl_access); + posix_acl_release(resp->acl_default); + return 1; +} +#endif /* CONFIG_NFS_ACL */ diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfsd/nfsproc.c linux-2.4.22-ppc-dev/fs/nfsd/nfsproc.c --- linux-2.4.22-ppc-dev.orig/fs/nfsd/nfsproc.c 2009-08-07 06:23:20.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfsd/nfsproc.c 2009-08-07 06:36:09.000000000 +0200 @@ -595,6 +595,8 @@ #endif { nfserr_stale, -ESTALE }, { nfserr_dropit, -ENOMEM }, + { nfserr_notsupp, -EOPNOTSUPP }, + { nfserr_notsupp, -ENOTSUPP }, { -1, -EIO } }; int i; diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfsd/nfssvc.c linux-2.4.22-ppc-dev/fs/nfsd/nfssvc.c --- linux-2.4.22-ppc-dev.orig/fs/nfsd/nfssvc.c 2009-08-07 06:34:44.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfsd/nfssvc.c 2009-08-07 06:36:09.000000000 +0200 @@ -34,7 +34,10 @@ #include #define NFSDDBG_FACILITY NFSDDBG_SVC -#define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE) +/* The nfs_acl RPCs may transmit two ACLs with 1024 entries each! */ +#define NFSSVC_MAXACLSIZE ((28+2*3*1024)<<2) +#define NFSD_BUFSIZE (1024 + (NFSSVC_MAXACLSIZE > NFSSVC_MAXBLKSIZE ? \ + NFSSVC_MAXACLSIZE : NFSSVC_MAXBLKSIZE )) /* these signals will be delivered to an nfsd thread * when handling a request @@ -155,6 +158,7 @@ nfsd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; + struct fs_struct *fsp; int err; struct nfsd_list me; @@ -165,6 +169,19 @@ sprintf(current->comm, "nfsd"); current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; + /* Make sure umask is 0. + * This is required by the new ACL code which does the umask + * munging below vfs_create() level. + */ + fsp = copy_fs_struct(current->fs); + if (fsp == NULL) { + printk("Unable to start nfsd thread: out of memory\n"); + goto out; + } + exit_fs(current); + current->fs = fsp; + fsp->umask = 0; + nfsdstats.th_cnt++; /* Let svc_process check client's authentication. */ rqstp->rq_auth = 1; @@ -246,6 +263,7 @@ list_del(&me.list); nfsdstats.th_cnt --; +out: /* Release the thread */ svc_exit_thread(rqstp); @@ -315,6 +333,35 @@ return 1; } +#ifdef CONFIG_NFSD_ACL +static struct svc_version nfsd_acl_version3 = { + 3, 3, nfsd_acl_procedures3, nfsd_dispatch +}; + +static struct svc_version * nfsd_acl_version[] = { + NULL, + NULL, + NULL, + &nfsd_acl_version3, +}; + +struct svc_stat nfsd_acl_svcstats = { &nfsd_acl_program, }; + +# define NFSD_ACL_NRVERS (sizeof(nfsd_acl_version)/sizeof(nfsd_acl_version[0])) +struct svc_program nfsd_acl_program = { + NULL, /* last registered program */ + NFS3_ACL_PROGRAM, /* program number */ + 2, NFSD_ACL_NRVERS-1, /* version range */ + NFSD_ACL_NRVERS, /* nr of entries in nfsd_acl_version */ + nfsd_acl_version, /* version table */ + "nfsd", /* program name */ + &nfsd_acl_svcstats, /* version table */ +}; +# define nfsd_acl_program_p &nfsd_acl_program +#else /* CONFIG_NFS_ACL */ +# define nfsd_acl_program_p NULL +#endif /* CONFIG_NFS_ACL */ + static struct svc_version nfsd_version2 = { 2, 18, nfsd_procedures2, nfsd_dispatch }; @@ -334,10 +381,11 @@ #define NFSD_NRVERS (sizeof(nfsd_version)/sizeof(nfsd_version[0])) struct svc_program nfsd_program = { + nfsd_acl_program_p, /* next registered program */ NFS_PROGRAM, /* program number */ 2, NFSD_NRVERS-1, /* version range */ NFSD_NRVERS, /* nr of entries in nfsd_version */ nfsd_version, /* version table */ "nfsd", /* program name */ - &nfsd_svcstats, /* version table */ + &nfsd_svcstats, /* rpc statistics */ }; diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfsd/nfsxdr.c linux-2.4.22-ppc-dev/fs/nfsd/nfsxdr.c --- linux-2.4.22-ppc-dev.orig/fs/nfsd/nfsxdr.c 2009-08-07 06:23:20.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfsd/nfsxdr.c 2009-08-07 06:36:09.000000000 +0200 @@ -135,10 +135,20 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) { struct inode *inode = fhp->fh_dentry->d_inode; + mode_t mode = inode->i_mode; int type = (inode->i_mode & S_IFMT); + + if (IS_POSIXACL(inode) && !EX_ACL(fhp->fh_export)) { + struct posix_acl *acl = nfsd_get_posix_acl(fhp,ACL_TYPE_ACCESS); + + if (!IS_ERR(acl) && acl) { + posix_acl_masq_nfs_mode(acl, &mode); + posix_acl_release(acl); + } + } *p++ = htonl(nfs_ftypes[type >> 12]); - *p++ = htonl((u32) inode->i_mode); + *p++ = htonl((u32) mode); *p++ = htonl((u32) inode->i_nlink); *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid)); *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid)); diff -Naur linux-2.4.22-ppc-dev.orig/fs/nfsd/vfs.c linux-2.4.22-ppc-dev/fs/nfsd/vfs.c --- linux-2.4.22-ppc-dev.orig/fs/nfsd/vfs.c 2009-08-07 06:23:20.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/nfsd/vfs.c 2009-08-07 06:36:08.000000000 +0200 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1590,3 +1591,104 @@ nfsdstats.ra_size = cache_size; return 0; } + +#ifdef CONFIG_FS_POSIX_ACL +struct posix_acl * +nfsd_get_posix_acl(struct svc_fh *fhp, int type) +{ + struct inode *inode = fhp->fh_dentry->d_inode; + char *name; + void *value = NULL; + ssize_t size; + struct posix_acl *acl; + + if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->getxattr) + return ERR_PTR(-EOPNOTSUPP); + switch(type) { + case ACL_TYPE_ACCESS: + name = XATTR_NAME_ACL_ACCESS; + break; + case ACL_TYPE_DEFAULT: + name = XATTR_NAME_ACL_DEFAULT; + break; + default: + return ERR_PTR(-EOPNOTSUPP); + } + + lock_kernel(); /* goes away in 2.5 */ + size = inode->i_op->getxattr(fhp->fh_dentry, name, NULL, 0); + unlock_kernel(); /* goes away in 2.5 */ + + if (size < 0) { + acl = ERR_PTR(size); + goto getout; + } else if (size > 0) { + value = kmalloc(size, GFP_KERNEL); + if (!value) { + acl = ERR_PTR(-ENOMEM); + goto getout; + } + size = inode->i_op->getxattr(fhp->fh_dentry, name, value, size); + if (size < 0) { + acl = ERR_PTR(size); + goto getout; + } + } + acl = posix_acl_from_xattr(value, size); + +getout: + kfree(value); + return acl; +} + +int +nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl) +{ + struct inode *inode = fhp->fh_dentry->d_inode; + char *name; + void *value = NULL; + size_t size; + int error; + + if (!IS_POSIXACL(inode) || !inode->i_op || + !inode->i_op->setxattr || !inode->i_op->removexattr) + return -EOPNOTSUPP; + switch(type) { + case ACL_TYPE_ACCESS: + name = XATTR_NAME_ACL_ACCESS; + break; + case ACL_TYPE_DEFAULT: + name = XATTR_NAME_ACL_DEFAULT; + break; + default: + return -EOPNOTSUPP; + } + + if (acl && acl->a_count) { + size = xattr_acl_size(acl->a_count); + value = kmalloc(size, GFP_KERNEL); + if (!value) + return -ENOMEM; + size = posix_acl_to_xattr(acl, value, size); + if (size < 0) { + error = size; + goto getout; + } + } else + size = 0; + + if (!fhp->fh_locked) + fh_lock(fhp); /* unlocking is done automatically */ + lock_kernel(); /* goes away in 2.5 */ + if (size) + error = inode->i_op->setxattr(fhp->fh_dentry, name, + value, size, 0); + else + error = inode->i_op->removexattr(fhp->fh_dentry, name); + unlock_kernel(); /* goes away in 2.5 */ + +getout: + kfree(value); + return error; +} +#endif diff -Naur linux-2.4.22-ppc-dev.orig/fs/posix_acl.c linux-2.4.22-ppc-dev/fs/posix_acl.c --- linux-2.4.22-ppc-dev.orig/fs/posix_acl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/posix_acl.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,430 @@ +/* + * linux/fs/posix_acl.c + * + * Copyright (C) 2002 by Andreas Gruenbacher + * + * Fixes from William Schumacher incorporated on 15 March 2001. + * (Reported by Charles Bertsch, ). + */ + +/* + * This file contains generic functions for manipulating + * POSIX 1003.1e draft standard 17 ACLs. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Andreas Gruenbacher "); +MODULE_DESCRIPTION("Generic Posix Access Control List (ACL) Manipulation"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10) +MODULE_LICENSE("GPL"); +#endif + +EXPORT_SYMBOL(posix_acl_alloc); +EXPORT_SYMBOL(posix_acl_clone); +EXPORT_SYMBOL(posix_acl_valid); +EXPORT_SYMBOL(posix_acl_equiv_mode); +EXPORT_SYMBOL(posix_acl_from_mode); +EXPORT_SYMBOL(posix_acl_create_masq); +EXPORT_SYMBOL(posix_acl_chmod_masq); +EXPORT_SYMBOL(posix_acl_masq_nfs_mode); +EXPORT_SYMBOL(posix_acl_permission); + +/* + * Allocate a new ACL with the specified number of entries. + */ +struct posix_acl * +posix_acl_alloc(int count, int flags) +{ + const size_t size = sizeof(struct posix_acl) + + count * sizeof(struct posix_acl_entry); + struct posix_acl *acl = kmalloc(size, flags); + if (acl) { + atomic_set(&acl->a_refcount, 1); + acl->a_count = count; + } + return acl; +} + +/* + * Clone an ACL. + */ +struct posix_acl * +posix_acl_clone(const struct posix_acl *acl, int flags) +{ + struct posix_acl *clone = NULL; + + if (acl) { + int size = sizeof(struct posix_acl) + acl->a_count * + sizeof(struct posix_acl_entry); + clone = kmalloc(size, flags); + if (clone) { + memcpy(clone, acl, size); + atomic_set(&clone->a_refcount, 1); + } + } + return clone; +} + +/* + * Check if an acl is valid. Returns 0 if it is, or -E... otherwise. + */ +int +posix_acl_valid(const struct posix_acl *acl) +{ + const struct posix_acl_entry *pa, *pe; + int state = ACL_USER_OBJ; + unsigned int id = 0; /* keep gcc happy */ + int needs_mask = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE)) + return -EINVAL; + switch (pa->e_tag) { + case ACL_USER_OBJ: + if (state == ACL_USER_OBJ) { + id = 0; + state = ACL_USER; + break; + } + return -EINVAL; + + case ACL_USER: + if (state != ACL_USER) + return -EINVAL; + if (pa->e_id == ACL_UNDEFINED_ID || + pa->e_id < id) + return -EINVAL; + id = pa->e_id + 1; + needs_mask = 1; + break; + + case ACL_GROUP_OBJ: + if (state == ACL_USER) { + id = 0; + state = ACL_GROUP; + break; + } + return -EINVAL; + + case ACL_GROUP: + if (state != ACL_GROUP) + return -EINVAL; + if (pa->e_id == ACL_UNDEFINED_ID || + pa->e_id < id) + return -EINVAL; + id = pa->e_id + 1; + needs_mask = 1; + break; + + case ACL_MASK: + if (state != ACL_GROUP) + return -EINVAL; + state = ACL_OTHER; + break; + + case ACL_OTHER: + if (state == ACL_OTHER || + (state == ACL_GROUP && !needs_mask)) { + state = 0; + break; + } + return -EINVAL; + + default: + return -EINVAL; + } + } + if (state == 0) + return 0; + return -EINVAL; +} + +/* + * Returns 0 if the acl can be exactly represented in the traditional + * file mode permission bits, or else 1. Returns -E... on error. + */ +int +posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p) +{ + const struct posix_acl_entry *pa, *pe; + mode_t mode = 0; + int not_equiv = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch (pa->e_tag) { + case ACL_USER_OBJ: + mode |= (pa->e_perm & S_IRWXO) << 6; + break; + case ACL_GROUP_OBJ: + mode |= (pa->e_perm & S_IRWXO) << 3; + break; + case ACL_OTHER: + mode |= pa->e_perm & S_IRWXO; + break; + case ACL_MASK: + mode = (mode & ~S_IRWXG) | + ((pa->e_perm & S_IRWXO) << 3); + not_equiv = 1; + break; + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + default: + return -EINVAL; + } + } + if (mode_p) + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; + return not_equiv; +} + +/* + * Create an ACL representing the file mode permission bits of an inode. + */ +struct posix_acl * +posix_acl_from_mode(mode_t mode, int flags) +{ + struct posix_acl *acl = posix_acl_alloc(3, flags); + if (!acl) + return ERR_PTR(-ENOMEM); + + acl->a_entries[0].e_tag = ACL_USER_OBJ; + acl->a_entries[0].e_id = ACL_UNDEFINED_ID; + acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; + + acl->a_entries[1].e_tag = ACL_GROUP_OBJ; + acl->a_entries[1].e_id = ACL_UNDEFINED_ID; + acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; + + acl->a_entries[2].e_tag = ACL_OTHER; + acl->a_entries[2].e_id = ACL_UNDEFINED_ID; + acl->a_entries[2].e_perm = (mode & S_IRWXO); + return acl; +} + +/* + * Return 0 if current is granted want access to the inode + * by the acl. Returns -E... otherwise. + */ +int +posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want) +{ + const struct posix_acl_entry *pa, *pe, *mask_obj; + int found = 0; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + /* (May have been checked already) */ + if (inode->i_uid == current->fsuid) + goto check_perm; + break; + case ACL_USER: + if (pa->e_id == current->fsuid) + goto mask; + break; + case ACL_GROUP_OBJ: + if (in_group_p(inode->i_gid)) { + found = 1; + if ((pa->e_perm & want) == want) + goto mask; + } + break; + case ACL_GROUP: + if (in_group_p(pa->e_id)) { + found = 1; + if ((pa->e_perm & want) == want) + goto mask; + } + break; + case ACL_MASK: + break; + case ACL_OTHER: + if (found) + return -EACCES; + else + goto check_perm; + default: + return -EIO; + } + } + return -EIO; + +mask: + for (mask_obj = pa+1; mask_obj != pe; mask_obj++) { + if (mask_obj->e_tag == ACL_MASK) { + if ((pa->e_perm & mask_obj->e_perm & want) == want) + return 0; + return -EACCES; + } + } + +check_perm: + if ((pa->e_perm & want) == want) + return 0; + return -EACCES; +} + +/* + * Modify acl when creating a new inode. The caller must ensure the acl is + * only referenced once. + * + * mode_p initially must contain the mode parameter to the open() / creat() + * system calls. All permissions that are not granted by the acl are removed. + * The permissions in the acl are changed to reflect the mode_p parameter. + */ +int +posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p) +{ + struct posix_acl_entry *pa, *pe; + struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; + mode_t mode = *mode_p; + int not_equiv = 0; + + /* assert(atomic_read(acl->a_refcount) == 1); */ + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_perm &= (mode >> 6) | ~S_IRWXO; + mode &= (pa->e_perm << 6) | ~S_IRWXU; + break; + + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + + case ACL_GROUP_OBJ: + group_obj = pa; + break; + + case ACL_OTHER: + pa->e_perm &= mode | ~S_IRWXO; + mode &= pa->e_perm | ~S_IRWXO; + break; + + case ACL_MASK: + mask_obj = pa; + not_equiv = 1; + break; + + default: + return -EIO; + } + } + + if (mask_obj) { + mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (mask_obj->e_perm << 3) | ~S_IRWXG; + } else { + if (!group_obj) + return -EIO; + group_obj->e_perm &= (mode >> 3) | ~S_IRWXO; + mode &= (group_obj->e_perm << 3) | ~S_IRWXG; + } + + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; + return not_equiv; +} + +/* + * Modify the ACL for the chmod syscall. + */ +int +posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode) +{ + struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; + struct posix_acl_entry *pa, *pe; + + /* assert(atomic_read(acl->a_refcount) == 1); */ + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_perm = (mode & S_IRWXU) >> 6; + break; + + case ACL_USER: + case ACL_GROUP: + break; + + case ACL_GROUP_OBJ: + group_obj = pa; + break; + + case ACL_MASK: + mask_obj = pa; + break; + + case ACL_OTHER: + pa->e_perm = (mode & S_IRWXO); + break; + + default: + return -EIO; + } + } + + if (mask_obj) { + mask_obj->e_perm = (mode & S_IRWXG) >> 3; + } else { + if (!group_obj) + return -EIO; + group_obj->e_perm = (mode & S_IRWXG) >> 3; + } + + return 0; +} + +/* + * Adjust the mode parameter so that NFSv2 grants nobody permissions + * that may not be granted by the ACL. This is necessary because NFSv2 + * may compute access permissions on the client side, and may serve cached + * data whenever it assumes access would be granted. Since ACLs may also + * be used to deny access to specific users, the minimal permissions + * for secure operation over NFSv2 are very restrictive. Permissions + * granted to users via Access Control Lists will not be effective over + * NFSv2. + * + * Privilege escalation can only happen for read operations, as writes are + * always carried out on the NFS server, where the proper access checks are + * implemented. + */ +int +posix_acl_masq_nfs_mode(struct posix_acl *acl, mode_t *mode_p) +{ + struct posix_acl_entry *pa, *pe; int min_perm = S_IRWXO; + + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + break; + + case ACL_USER: + case ACL_GROUP_OBJ: + case ACL_GROUP: + case ACL_MASK: + case ACL_OTHER: + min_perm &= pa->e_perm; + break; + + default: + return -EIO; + } + } + *mode_p = (*mode_p & ~(S_IRWXG|S_IRWXO)) | (min_perm << 3) | min_perm; + + return 0; +} diff -Naur linux-2.4.22-ppc-dev.orig/fs/solaris_acl.c linux-2.4.22-ppc-dev/fs/solaris_acl.c --- linux-2.4.22-ppc-dev.orig/fs/solaris_acl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/solaris_acl.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,198 @@ +/* + * linux/fs/solaris_acl.c + * + * Copyright (C) 2002 by Andreas Gruenbacher + */ + +/* + * The Solaris nfs_acl protocol represents some ACLs slightly differently + * than POSIX 1003.1e draft 17 does (and we do): + * + * - Minimal ACLs always have an ACL_MASK entry, so they have + * four instead of three entries. + * - The ACL_MASK entry in such minimal ACLs always has the same + * permissions as the ACL_GROUP_OBJ entry. (In extended ACLs + * the ACL_MASK and ACL_GROUP_OBJ entries may differ.) + * - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ + * entries contain the identifiers of the owner and owning group. + * (In POSIX ACLs we always set them to ACL_UNDEFINED_ID). + * - ACL entries in the kernel are kept sorted in ascending order + * of (e_tag, e_id). Solaris ACLs are unsorted. + */ + +#include +#include +#include + +EXPORT_SYMBOL(nfs_acl_encode); +EXPORT_SYMBOL(nfs_acl_decode); + +u32 * +nfs_acl_encode(u32 *p, u32 *end, struct inode *inode, struct posix_acl *acl, + int encode_entries, int typeflag) +{ + int entries = acl ? acl->a_count : 0; + + if (entries == 3) + entries++; /* need to fake up ACL_MASK entry */ + if (entries > NFS3_ACL_MAX_ENTRIES || + p + 2 + (encode_entries ? (3 * entries) : 0) > end) + return NULL; + *p++ = htonl(entries); + if (acl && encode_entries) { + struct posix_acl_entry *pa, *pe; + int group_obj_perm = ACL_READ|ACL_WRITE|ACL_EXECUTE; + + *p++ = htonl(entries); + FOREACH_ACL_ENTRY(pa, acl, pe) { + *p++ = htonl(pa->e_tag | typeflag); + switch(pa->e_tag) { + case ACL_USER_OBJ: + *p++ = htonl(inode->i_uid); + break; + case ACL_GROUP_OBJ: + *p++ = htonl(inode->i_gid); + group_obj_perm = pa->e_perm; + break; + default: + *p++ = htonl(pa->e_id); + break; + } + *p++ = htonl(pa->e_perm & S_IRWXO); + } + if (acl->a_count < entries) { + /* fake up ACL_MASK entry */ + *p++ = htonl(ACL_MASK | typeflag); + *p++ = htonl(ACL_UNDEFINED_ID); + *p++ = htonl(group_obj_perm & S_IRWXO); + } + } else + *p++ = 0; + + return p; +} + +static int +cmp_acl_entry(const struct posix_acl_entry *a, const struct posix_acl_entry *b) +{ + if (a->e_tag != b->e_tag) + return a->e_tag - b->e_tag; + else if (a->e_id > b->e_id) + return 1; + else if (a->e_id < b->e_id) + return -1; + else + return 0; +} + +/* + * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL. + */ +static int +posix_acl_from_solaris(struct posix_acl *acl) +{ + struct posix_acl_entry *pa, *pe, + *group_obj = NULL, *mask = NULL; + + if (!acl) + return 0; + + qsort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry), + (int(*)(const void *,const void *))cmp_acl_entry); + + /* Clear undefined identifier fields and find the ACL_GROUP_OBJ + and ACL_MASK entries. */ + FOREACH_ACL_ENTRY(pa, acl, pe) { + switch(pa->e_tag) { + case ACL_USER_OBJ: + pa->e_id = ACL_UNDEFINED_ID; + break; + case ACL_GROUP_OBJ: + pa->e_id = ACL_UNDEFINED_ID; + group_obj = pa; + break; + case ACL_MASK: + mask = pa; + /* fall through */ + case ACL_OTHER: + pa->e_id = ACL_UNDEFINED_ID; + break; + } + } + if (acl->a_count == 4 && group_obj && mask && + mask->e_perm == group_obj->e_perm) { + /* remove bogus ACL_MASK entry */ + memmove(mask, mask+1, (acl->a_entries + 4 - mask) * + sizeof(struct posix_acl_entry)); + acl->a_count = 3; + } + return 0; +} + +static u32 * +nfs_acl_decode_entry(u32 *p, struct posix_acl_entry *entry) +{ + entry->e_tag = ntohl(*p++) & ~NFS3_ACL_DEFAULT; + entry->e_id = ntohl(*p++); + entry->e_perm = ntohl(*p++); + + switch(entry->e_tag) { + case ACL_USER_OBJ: + case ACL_USER: + case ACL_GROUP_OBJ: + case ACL_GROUP: + case ACL_OTHER: + if (entry->e_perm & ~S_IRWXO) + return NULL; + break; + case ACL_MASK: + /* Solaris sometimes sets additonal bits in the mask */ + entry->e_perm &= S_IRWXO; + break; + default: + return NULL; + } + return p; +} + +u32 * +nfs_acl_decode(u32 *p, u32 *end, unsigned int *aclcnt, struct posix_acl **pacl) +{ + struct posix_acl_entry *pa, *pe; + unsigned int entries, array_len; + + if (p + 2 > end) + return NULL; + entries = ntohl(*p++); + array_len = ntohl(*p++); + if (entries > NFS3_ACL_MAX_ENTRIES || (pacl && entries != array_len)) + return NULL; + if (p + 3 * array_len > end) + return NULL; + if (pacl) { + *pacl = NULL; + if (entries) { + struct posix_acl *acl; + + if (!(acl = posix_acl_alloc(array_len, GFP_KERNEL))) + return NULL; + FOREACH_ACL_ENTRY(pa, acl, pe) { + if (!(p = nfs_acl_decode_entry(p, pa))) { + posix_acl_release(acl); + return NULL; + } + } + if (posix_acl_from_solaris(acl) != 0) { + posix_acl_release(acl); + return NULL; + } + *pacl = acl; + } + } else + p += 3 * array_len; + + if (aclcnt) + *aclcnt = entries; + return p; +} + diff -Naur linux-2.4.22-ppc-dev.orig/fs/super.c linux-2.4.22-ppc-dev/fs/super.c --- linux-2.4.22-ppc-dev.orig/fs/super.c 2009-08-07 06:23:18.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/super.c 2009-08-07 06:36:09.000000000 +0200 @@ -89,7 +89,7 @@ * unregistered. */ -int register_filesystem(struct file_system_type * fs) +int __register_filesystem(struct file_system_type * fs, int lifo) { int res = 0; struct file_system_type ** p; @@ -103,8 +103,14 @@ p = find_filesystem(fs->name); if (*p) res = -EBUSY; - else - *p = fs; + else { + if (!lifo) + *p = fs; + else { + fs->next = file_systems; + file_systems = fs; + } + } write_unlock(&file_systems_lock); return res; } diff -Naur linux-2.4.22-ppc-dev.orig/fs/xattr.c linux-2.4.22-ppc-dev/fs/xattr.c --- linux-2.4.22-ppc-dev.orig/fs/xattr.c 2009-08-07 06:23:18.000000000 +0200 +++ linux-2.4.22-ppc-dev/fs/xattr.c 2009-08-07 06:36:08.000000000 +0200 @@ -159,11 +159,9 @@ 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 @@ 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) diff -Naur linux-2.4.22-ppc-dev.orig/fs/xattr_acl.c linux-2.4.22-ppc-dev/fs/xattr_acl.c --- linux-2.4.22-ppc-dev.orig/fs/xattr_acl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/fs/xattr_acl.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,99 @@ +/* + * linux/fs/xattr_acl.c + * + * Almost all from linux/fs/ext2/acl.c: + * Copyright (C) 2001 by Andreas Gruenbacher, + */ + +#include +#include +#include +#include +#include + + +/* + * Convert from extended attribute to in-memory representation. + */ +struct posix_acl * +posix_acl_from_xattr(const void *value, size_t size) +{ + xattr_acl_header *header = (xattr_acl_header *)value; + xattr_acl_entry *entry = (xattr_acl_entry *)(header+1), *end; + int count; + struct posix_acl *acl; + struct posix_acl_entry *acl_e; + + if (!value) + return NULL; + if (size < sizeof(xattr_acl_header)) + return ERR_PTR(-EINVAL); + if (header->a_version != cpu_to_le32(XATTR_ACL_VERSION)) + return ERR_PTR(-EINVAL); + + count = xattr_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + acl_e = acl->a_entries; + + for (end = entry + count; entry != end; acl_e++, entry++) { + acl_e->e_tag = le16_to_cpu(entry->e_tag); + acl_e->e_perm = le16_to_cpu(entry->e_perm); + + switch(acl_e->e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + acl_e->e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + acl_e->e_id = le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL (posix_acl_from_xattr); + +/* + * Convert from in-memory to extended attribute representation. + */ +int +posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size) +{ + xattr_acl_header *ext_acl = (xattr_acl_header *)buffer; + xattr_acl_entry *ext_entry = ext_acl->a_entries; + int real_size, n; + + real_size = xattr_acl_size(acl->a_count); + if (!buffer) + return real_size; + if (real_size > size) + return -ERANGE; + + ext_acl->a_version = cpu_to_le32(XATTR_ACL_VERSION); + + for (n=0; n < acl->a_count; n++, ext_entry++) { + ext_entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id); + } + return real_size; +} +EXPORT_SYMBOL (posix_acl_to_xattr); diff -Naur linux-2.4.22-ppc-dev.orig/include/asm-arm/unistd.h linux-2.4.22-ppc-dev/include/asm-arm/unistd.h --- linux-2.4.22-ppc-dev.orig/include/asm-arm/unistd.h 2009-08-07 06:23:33.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/asm-arm/unistd.h 2009-08-07 06:36:08.000000000 +0200 @@ -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) diff -Naur linux-2.4.22-ppc-dev.orig/include/asm-s390/unistd.h linux-2.4.22-ppc-dev/include/asm-s390/unistd.h --- linux-2.4.22-ppc-dev.orig/include/asm-s390/unistd.h 2009-08-07 06:23:38.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/asm-s390/unistd.h 2009-08-07 06:36:08.000000000 +0200 @@ -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 diff -Naur linux-2.4.22-ppc-dev.orig/include/asm-s390x/unistd.h linux-2.4.22-ppc-dev/include/asm-s390x/unistd.h --- linux-2.4.22-ppc-dev.orig/include/asm-s390x/unistd.h 2009-08-07 06:23:40.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/asm-s390x/unistd.h 2009-08-07 06:36:08.000000000 +0200 @@ -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 diff -Naur linux-2.4.22-ppc-dev.orig/include/asm-sparc/unistd.h linux-2.4.22-ppc-dev/include/asm-sparc/unistd.h --- linux-2.4.22-ppc-dev.orig/include/asm-sparc/unistd.h 2009-08-07 06:23:32.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/asm-sparc/unistd.h 2009-08-07 06:36:08.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: unistd.h,v 1.72 2001/10/18 08:27:05 davem Exp $ */ +/* $Id: unistd.h,v 1.74 2002/02/08 03:57:18 davem Exp $ */ #ifndef _SPARC_UNISTD_H #define _SPARC_UNISTD_H diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/cache_def.h linux-2.4.22-ppc-dev/include/linux/cache_def.h --- linux-2.4.22-ppc-dev.orig/include/linux/cache_def.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/cache_def.h 2009-08-07 06:36:08.000000000 +0200 @@ -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 *); diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext2_acl.h linux-2.4.22-ppc-dev/include/linux/ext2_acl.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext2_acl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/ext2_acl.h 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,101 @@ +/* + File: linux/ext2_acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include +#include + +#define EXT2_ACL_VERSION 0x0001 +#define EXT2_ACL_MAX_ENTRIES 32 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} ext2_acl_entry; + +typedef struct { + __u16 e_tag; + __u16 e_perm; +} ext2_acl_entry_short; + +typedef struct { + __u32 a_version; +} ext2_acl_header; + +static inline size_t ext2_acl_size(int count) +{ + if (count <= 4) { + return sizeof(ext2_acl_header) + + count * sizeof(ext2_acl_entry_short); + } else { + return sizeof(ext2_acl_header) + + 4 * sizeof(ext2_acl_entry_short) + + (count - 4) * sizeof(ext2_acl_entry); + } +} + +static inline int ext2_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext2_acl_header); + s = size - 4 * sizeof(ext2_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext2_acl_entry_short)) + return -1; + return size / sizeof(ext2_acl_entry_short); + } else { + if (s % sizeof(ext2_acl_entry)) + return -1; + return s / sizeof(ext2_acl_entry) + 4; + } +} + +#ifdef __KERNEL__ +# ifdef CONFIG_EXT2_FS_POSIX_ACL + +/* Value for inode->u.ext2_i.i_acl and inode->u.ext2_i.i_default_acl + if the ACL has not been cached */ +# define EXT2_ACL_NOT_CACHED ((void *)-1) + +/* acl.c */ +extern int ext2_permission (struct inode *, int); +extern int ext2_acl_chmod (struct inode *); +extern int ext2_init_acl (struct inode *, struct inode *); + +extern int init_ext2_acl(void) __init; +extern void exit_ext2_acl(void); + +# else +# include +# define ext2_permission NULL + +static inline int +ext2_acl_chmod (struct inode *inode) +{ + return 0; +} + +static inline int ext2_init_acl (struct inode *inode, struct inode *dir) +{ + inode->i_mode &= ~current->fs->umask; + mark_inode_dirty(inode); + return 0; +} + +static inline int +init_ext2_acl(void) +{ + return 0; +} + +static inline void +exit_ext2_acl(void) +{ +} + +# endif +#endif diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext2_fs.h linux-2.4.22-ppc-dev/include/linux/ext2_fs.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext2_fs.h 2009-08-07 06:23:26.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/ext2_fs.h 2009-08-07 06:36:08.000000000 +0200 @@ -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 @@ #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 @@ -397,6 +373,7 @@ #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 @@ -466,7 +443,7 @@ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 #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 #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ @@ -573,7 +550,7 @@ extern int ext2_fsync_inode (struct inode *, int); /* ialloc.c */ -extern struct inode * ext2_new_inode (const struct inode *, int); +extern struct inode * ext2_new_inode (struct inode *, int); extern void ext2_free_inode (struct inode *); extern unsigned long ext2_count_free_inodes (struct super_block *); extern void ext2_check_inodes_bitmap (struct super_block *); @@ -588,6 +565,7 @@ extern void ext2_discard_prealloc (struct inode *); extern void ext2_truncate (struct inode *); extern void ext2_set_inode_flags(struct inode *inode); +extern int ext2_setattr (struct dentry *, struct iattr *); /* ioctl.c */ extern int ext2_ioctl (struct inode *, struct file *, unsigned int, @@ -624,8 +602,10 @@ /* 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__ */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext2_fs_i.h linux-2.4.22-ppc-dev/include/linux/ext2_fs_i.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext2_fs_i.h 2009-08-07 06:23:26.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/ext2_fs_i.h 2009-08-07 06:36:08.000000000 +0200 @@ -34,6 +34,20 @@ __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 +#ifdef CONFIG_EXT2_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif int i_new_inode:1; /* Is a freshly allocated inode */ }; diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext2_xattr.h linux-2.4.22-ppc-dev/include/linux/ext2_xattr.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext2_xattr.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/ext2_xattr.h 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,180 @@ +/* + 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 *, const 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; +extern void exit_ext2_xattr_user(void); + +# else /* CONFIG_EXT2_FS_XATTR_USER */ + +static inline int +init_ext2_xattr_user(void) +{ + return 0; +} + +static inline void +exit_ext2_xattr_user(void) +{ +} + +# endif /* CONFIG_EXT2_FS_XATTR_USER */ + +# ifdef CONFIG_EXT2_FS_XATTR_TRUSTED + +extern int init_ext2_xattr_trusted(void) __init; +extern void exit_ext2_xattr_trusted(void); + +# else /* CONFIG_EXT2_FS_XATTR_TRUSTED */ + +static inline int +init_ext2_xattr_trusted(void) +{ + return 0; +} + +static inline void +exit_ext2_xattr_trusted(void) +{ +} + +# endif /* CONFIG_EXT2_FS_XATTR_TRUSTED */ + +#endif /* __KERNEL__ */ + diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext3_acl.h linux-2.4.22-ppc-dev/include/linux/ext3_acl.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext3_acl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/ext3_acl.h 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,108 @@ +/* + File: linux/ext3_acl.h + + (C) 2001 Andreas Gruenbacher, +*/ + +#include +#include +#include + +#define EXT3_ACL_VERSION 0x0001 +#define EXT3_ACL_MAX_ENTRIES 32 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} ext3_acl_entry; + +typedef struct { + __u16 e_tag; + __u16 e_perm; +} ext3_acl_entry_short; + +typedef struct { + __u32 a_version; +} ext3_acl_header; + +static inline size_t ext3_acl_size(int count) +{ + if (count <= 4) { + return sizeof(ext3_acl_header) + + count * sizeof(ext3_acl_entry_short); + } else { + return sizeof(ext3_acl_header) + + 4 * sizeof(ext3_acl_entry_short) + + (count - 4) * sizeof(ext3_acl_entry); + } +} + +static inline int ext3_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext3_acl_header); + s = size - 4 * sizeof(ext3_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext3_acl_entry_short)) + return -1; + return size / sizeof(ext3_acl_entry_short); + } else { + if (s % sizeof(ext3_acl_entry)) + return -1; + return s / sizeof(ext3_acl_entry) + 4; + } +} + +#ifdef __KERNEL__ +# ifdef CONFIG_EXT3_FS_POSIX_ACL + +/* Value for inode->u.ext3_i.i_acl and inode->u.ext3_i.i_default_acl + if the ACL has not been cached */ +# define EXT3_ACL_NOT_CACHED ((void *)-1) + +/* acl.c */ +extern int ext3_permission (struct inode *, int); +extern struct posix_acl *ext3_get_acl (struct inode *, int); +extern int ext3_set_acl (struct inode *, int, struct posix_acl *); +extern int ext3_acl_chmod (handle_t *, struct inode *); +extern int ext3_init_acl (handle_t *, struct inode *, struct inode *); +extern int ext3_get_acl_xattr (struct inode *, int, void *, size_t); +extern int ext3_set_acl_xattr (struct inode *, int, void *, size_t); + +extern int init_ext3_acl(void) __init; +extern void exit_ext3_acl(void); + +# else /* CONFIG_EXT3_FS_POSIX_ACL */ +# include +# define ext3_permission NULL +# define ext3_get_acl NULL +# define ext3_set_acl NULL + +static inline int +ext3_acl_chmod(handle_t *handle, struct inode *inode) +{ + return 0; +} + +static inline int +ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) +{ + inode->i_mode &= ~current->fs->umask; + ext3_mark_inode_dirty(handle, inode); + return 0; +} + +static inline int +init_ext3_acl(void) +{ + return 0; +} + +static inline void +exit_ext3_acl(void) +{ +} + +# endif /* CONFIG_EXT3_FS_POSIX_ACL */ +#endif /* __KERNEL__ */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext3_fs.h linux-2.4.22-ppc-dev/include/linux/ext3_fs.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext3_fs.h 2009-08-07 06:23:25.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/ext3_fs.h 2009-08-07 06:36:08.000000000 +0200 @@ -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,8 @@ #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 */ +#define EXT3_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H @@ -513,7 +490,7 @@ #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ -#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) #define EXT3_FEATURE_RO_COMPAT_SUPP (EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER| \ @@ -621,7 +598,7 @@ extern int ext3_sync_file (struct file *, struct dentry *, int); /* ialloc.c */ -extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int); +extern struct inode * ext3_new_inode (handle_t *, struct inode *, int); extern void ext3_free_inode (handle_t *, struct inode *); extern struct inode * ext3_orphan_get (struct super_block *, unsigned long); extern unsigned long ext3_count_free_inodes (struct super_block *); @@ -629,6 +606,7 @@ 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 *); @@ -696,8 +674,10 @@ /* 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; diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext3_fs_i.h linux-2.4.22-ppc-dev/include/linux/ext3_fs_i.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext3_fs_i.h 2009-08-07 06:23:28.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/ext3_fs_i.h 2009-08-07 06:36:08.000000000 +0200 @@ -42,6 +42,20 @@ __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 +#ifdef CONFIG_EXT3_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif struct list_head i_orphan; /* unlinked but open inodes */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext3_jbd.h linux-2.4.22-ppc-dev/include/linux/ext3_jbd.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext3_jbd.h 2009-08-07 06:23:28.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/ext3_jbd.h 2009-08-07 06:36:09.000000000 +0200 @@ -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); diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/ext3_xattr.h linux-2.4.22-ppc-dev/include/linux/ext3_xattr.h --- linux-2.4.22-ppc-dev.orig/include/linux/ext3_xattr.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/ext3_xattr.h 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,182 @@ +/* + 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 *, const 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; +extern void exit_ext3_xattr_user(void); + +# else /* CONFIG_EXT3_FS_XATTR_USER */ + +static inline int +init_ext3_xattr_user(void) +{ + return 0; +} + +static inline void +exit_ext3_xattr_user(void) +{ +} + +#endif /* CONFIG_EXT3_FS_XATTR_USER */ + +# ifdef CONFIG_EXT3_FS_XATTR_TRUSTED + +extern int init_ext3_xattr_trusted(void) __init; +extern void exit_ext3_xattr_trusted(void); + +# else /* CONFIG_EXT3_FS_XATTR_TRUSTED */ + +static inline int +init_ext3_xattr_trusted(void) +{ + return 0; +} + +static inline void +exit_ext3_xattr_trusted(void) +{ +} + +#endif /* CONFIG_EXT3_FS_XATTR_TRUSTED */ + +#endif /* __KERNEL__ */ + diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/fs.h linux-2.4.22-ppc-dev/include/linux/fs.h --- linux-2.4.22-ppc-dev.orig/include/linux/fs.h 2009-08-07 06:35:00.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/fs.h 2009-08-07 06:36:08.000000000 +0200 @@ -111,6 +111,7 @@ #define MS_MOVE 8192 #define MS_REC 16384 #define MS_VERBOSE 32768 +#define MS_POSIXACL 65536 /* VFS does not apply the umask */ #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) @@ -161,6 +162,7 @@ #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_NOATIME(inode) (__IS_FLG(inode, MS_NOATIME) || ((inode)->i_flags & S_NOATIME)) #define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME) +#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) @@ -898,7 +900,7 @@ int (*revalidate) (struct dentry *); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct dentry *, struct iattr *); - int (*setxattr) (struct dentry *, const char *, void *, size_t, int); + int (*setxattr) (struct dentry *, const char *, const void *, size_t, int); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*removexattr) (struct dentry *, const char *); @@ -1022,7 +1024,9 @@ __MOD_DEC_USE_COUNT((fops)->owner); \ } while(0) -extern int register_filesystem(struct file_system_type *); +extern int __register_filesystem(struct file_system_type *, int); +#define register_filesystem(fs) __register_filesystem(fs, 0) +#define register_filesystem_lifo(fs) __register_filesystem(fs, 1) extern int unregister_filesystem(struct file_system_type *); extern struct vfsmount *kern_mount(struct file_system_type *); extern int may_umount(struct vfsmount *); diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/jbd.h linux-2.4.22-ppc-dev/include/linux/jbd.h --- linux-2.4.22-ppc-dev.orig/include/linux/jbd.h 2009-08-07 06:23:25.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/jbd.h 2009-08-07 06:36:09.000000000 +0200 @@ -359,6 +359,10 @@ /* Number of remaining buffers we are allowed to dirty: */ int h_buffer_credits; + /* Number of credits consumed by the last journal_get_write_access + or get_undo_access operation */ + int h_last_buffer_credits; + /* Reference count on this handle */ int h_ref; diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/kernel.h linux-2.4.22-ppc-dev/include/linux/kernel.h --- linux-2.4.22-ppc-dev.orig/include/linux/kernel.h 2009-08-07 06:35:00.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/kernel.h 2009-08-07 06:36:08.000000000 +0200 @@ -83,6 +83,8 @@ extern int vsscanf(const char *, const char *, va_list) __attribute__ ((format (scanf, 2, 0))); +extern void qsort(void *, size_t, size_t, int (*)(const void *,const void *)); + extern int get_option(char **str, int *pint); extern char *get_options(char *str, int nints, int *ints); extern unsigned long long memparse(char *ptr, char **retptr); diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/mbcache.h linux-2.4.22-ppc-dev/include/linux/mbcache.h --- linux-2.4.22-ppc-dev.orig/include/linux/mbcache.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/mbcache.h 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,69 @@ +/* + 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_op { + int (*free)(struct mb_cache_entry *, int); +}; + +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]; +}; + +struct mb_cache_entry_index { + struct list_head o_list; + unsigned int o_key; +}; + +struct mb_cache_entry { + struct list_head e_lru_list; + struct mb_cache *e_cache; + atomic_t e_used; + kdev_t e_dev; + unsigned long e_block; + struct list_head e_block_list; + struct mb_cache_entry_index e_indexes[0]; +}; + +/* 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_rehash(struct mb_cache_entry *, unsigned int[]); +void mb_cache_entry_release(struct mb_cache_entry *); +void mb_cache_entry_takeout(struct mb_cache_entry *); +void mb_cache_entry_free(struct mb_cache_entry *); +struct mb_cache_entry *mb_cache_entry_dup(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 diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfs3.h linux-2.4.22-ppc-dev/include/linux/nfs3.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfs3.h 2009-08-07 06:23:27.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfs3.h 2009-08-07 06:36:08.000000000 +0200 @@ -36,6 +36,18 @@ NFS3_CREATE_EXCLUSIVE = 2 }; +/* Flags for the getacl/setacl mode */ +#define NFS3_ACL 0x0001 +#define NFS3_ACLCNT 0x0002 +#define NFS3_DFACL 0x0004 +#define NFS3_DFACLCNT 0x0008 + +/* Flag for Default ACL entries */ +#define NFS3_ACL_DEFAULT 0x1000 + +/* Maximum number of ACL entries over NFS */ +#define NFS3_ACL_MAX_ENTRIES 1024 + /* NFSv3 file system properties */ #define NFS3_FSF_LINK 0x0001 #define NFS3_FSF_SYMLINK 0x0002 @@ -82,6 +94,10 @@ #define NFS3PROC_PATHCONF 20 #define NFS3PROC_COMMIT 21 +#define NFS3_ACL_PROGRAM 100227 +#define NFS3_ACL_PROC_GETACL 1 +#define NFS3_ACL_PROC_SETACL 2 + #define NFS_MNT3_PROGRAM 100005 #define NFS_MNT3_VERSION 3 #define MOUNTPROC3_NULL 0 diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfs_fs.h linux-2.4.22-ppc-dev/include/linux/nfs_fs.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfs_fs.h 2009-08-07 06:23:25.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfs_fs.h 2009-08-07 06:36:08.000000000 +0200 @@ -102,6 +102,8 @@ #define NFS_FILEID(inode) ((inode)->u.nfs_i.fileid) +#define NFS_ACL_CLIENT(inode) (NFS_SERVER(inode)->acl_client) + /* Inode Flags */ #define NFS_USE_READDIRPLUS(inode) ((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0) @@ -163,6 +165,22 @@ extern struct file_operations nfs_file_operations; extern struct address_space_operations nfs_file_aops; +/* + * linux/fs/nfs/xattr.c + */ +#ifdef CONFIG_NFS_ACL +extern ssize_t nfs_listxattr(struct dentry *, char *, size_t); +extern ssize_t nfs_getxattr(struct dentry *, const char *, void *, size_t); +extern int nfs_setxattr(struct dentry *, const char *, + const void *, size_t, int); +extern int nfs_removexattr (struct dentry *, const char *name); +#else +# define nfs_listxattr NULL +# define nfs_getxattr NULL +# define nfs_setxattr NULL +# define nfs_removexattr NULL +#endif + static __inline__ struct rpc_cred * nfs_file_cred(struct file *file) { diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfs_fs_i.h linux-2.4.22-ppc-dev/include/linux/nfs_fs_i.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfs_fs_i.h 2009-08-07 06:23:26.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfs_fs_i.h 2009-08-07 06:36:09.000000000 +0200 @@ -6,6 +6,16 @@ #include /* + * NFSv3 Access mode cache + */ +struct nfs_access_cache { + unsigned long jiffies; + struct rpc_cred * cred; + int mask; + int err; +}; + +/* * nfs fs inode data in memory */ struct nfs_inode_info { @@ -54,6 +64,8 @@ */ unsigned long cache_mtime_jiffies; + struct nfs_access_cache cache_access; + /* * This is the cookie verifier used for NFSv3 readdir * operations diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfs_fs_sb.h linux-2.4.22-ppc-dev/include/linux/nfs_fs_sb.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfs_fs_sb.h 2009-08-07 06:23:26.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfs_fs_sb.h 2009-08-07 06:36:09.000000000 +0200 @@ -8,6 +8,9 @@ */ struct nfs_server { struct rpc_clnt * client; /* RPC client handle */ +#ifdef CONFIG_NFS_ACL + struct rpc_clnt * acl_client; /* ACL RPC client handle */ +#endif struct nfs_rpc_ops * rpc_ops; /* NFS protocol vector */ int flags; /* various flags */ unsigned int rsize; /* read size */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfs_mount.h linux-2.4.22-ppc-dev/include/linux/nfs_mount.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfs_mount.h 2009-08-07 06:23:26.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfs_mount.h 2009-08-07 06:36:08.000000000 +0200 @@ -53,6 +53,11 @@ #define NFS_MOUNT_KERBEROS 0x0100 /* 3 */ #define NFS_MOUNT_NONLM 0x0200 /* 3 */ #define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */ +#define NFS_MOUNT_NOACL 0x0800 /* 4 */ #define NFS_MOUNT_FLAGMASK 0xFFFF +/* Feature flags for the NFS_ACL GETACL and SETACL RPC's */ +#define NFS_SOLARIS_GETACL 0x10000 +#define NFS_SOLARIS_SETACL 0x20000 + #endif diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfs_xdr.h linux-2.4.22-ppc-dev/include/linux/nfs_xdr.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfs_xdr.h 2009-08-07 06:23:25.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfs_xdr.h 2009-08-07 06:36:09.000000000 +0200 @@ -1,6 +1,8 @@ #ifndef _LINUX_NFS_XDR_H #define _LINUX_NFS_XDR_H +#include + struct nfs_fattr { unsigned short valid; /* which fields are valid */ __u64 pre_size; /* pre_op_attr.size */ @@ -254,6 +256,18 @@ struct page ** pages; }; +struct nfs3_getaclargs { + struct nfs_fh * fh; + int mask; +}; + +struct nfs3_setaclargs { + struct inode * inode; + int mask; + struct posix_acl * acl_access; + struct posix_acl * acl_default; +}; + struct nfs3_diropres { struct nfs_fattr * dir_attr; struct nfs_fh * fh; @@ -287,6 +301,15 @@ int plus; }; +struct nfs3_getaclres { + struct nfs_fattr * fattr; + int mask; + unsigned int acl_access_count; + unsigned int acl_default_count; + struct posix_acl * acl_access; + struct posix_acl * acl_default; +}; + /* * RPC procedure vector for NFSv2/NFSv3 demuxing */ @@ -300,7 +323,7 @@ struct iattr *); int (*lookup) (struct inode *, struct qstr *, struct nfs_fh *, struct nfs_fattr *); - int (*access) (struct inode *, int , int); + int (*access) (struct inode *, struct rpc_cred *, int); int (*readlink)(struct inode *, struct page *); int (*read) (struct inode *, struct rpc_cred *, struct nfs_fattr *, @@ -334,6 +357,11 @@ int (*statfs) (struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus); +#ifdef CONFIG_NFS_ACL + struct posix_acl * (*getacl)(struct inode *, int); + int (*setacl)(struct inode *, int, struct posix_acl *); + int (*checkacls)(struct inode *inode); +#endif /* CONFIG_NFS_ACL */ }; /* @@ -353,4 +381,9 @@ extern struct rpc_program nfs_program; extern struct rpc_stat nfs_rpcstat; +#ifdef CONFIG_NFS_ACL +extern struct rpc_version nfs_acl_version3; +extern struct rpc_program nfs_acl_program; +#endif /* CONFIG_NFS_ACL */ + #endif diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfsd/export.h linux-2.4.22-ppc-dev/include/linux/nfsd/export.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfsd/export.h 2009-08-07 06:23:27.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfsd/export.h 2009-08-07 06:36:08.000000000 +0200 @@ -40,7 +40,8 @@ #define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */ #define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect */ #define NFSEXP_FSID 0x2000 -#define NFSEXP_ALLFLAGS 0x3FFF +#define NFSEXP_NOACL 0x4000 +#define NFSEXP_ALLFLAGS 0x7FFF #ifdef __KERNEL__ @@ -83,6 +84,19 @@ #define EX_CROSSMNT(exp) ((exp)->ex_flags & NFSEXP_CROSSMNT) #define EX_SUNSECURE(exp) ((exp)->ex_flags & NFSEXP_SUNSECURE) #define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES) +/* + * With Posix Access Control Lists, pre-NFS3 clients and older Linux + * NFSv3 clients in some cases mis-interpret the file mode permission + * bits, and either allow the remote user to read data she is not + * permitted to, or deny the user read access that should be granted. + * (With proper NFSv3, the access RPC is used to check access, and + * access decisions are not implemented on the client.) + * + * The no_acl option should be set in environments with clients that do + * not use the NFSv3 ACCESS RPC. This option should always be set for NFSv2 + * clients. + */ +#define EX_ACL(exp) (!((exp)->ex_flags & NFSEXP_NOACL)) /* diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfsd/nfsd.h linux-2.4.22-ppc-dev/include/linux/nfsd/nfsd.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfsd/nfsd.h 2009-08-07 06:23:27.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfsd/nfsd.h 2009-08-07 06:36:09.000000000 +0200 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,11 @@ #endif /* CONFIG_NFSD_V3 */ extern struct svc_program nfsd_program; +#ifdef CONFIG_NFSD_ACL +extern struct svc_procedure nfsd_acl_procedures3[]; +extern struct svc_program nfsd_acl_program; +#endif /* CONFIG_NFSD_ACL */ + /* * Function prototypes. */ @@ -127,6 +133,22 @@ int nfsd_notify_change(struct inode *, struct iattr *); int nfsd_permission(struct svc_export *, struct dentry *, int); +#ifdef CONFIG_FS_POSIX_ACL +struct posix_acl *nfsd_get_posix_acl(struct svc_fh *, int); +int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *); +#else +static inline struct posix_acl * +nfsd_get_posix_acl(struct svc_fh *fhp, int acl_type) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline int +nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl) +{ + return -EOPNOTSUPP; +} +#endif + /* * lockd binding diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/nfsd/xdr3.h linux-2.4.22-ppc-dev/include/linux/nfsd/xdr3.h --- linux-2.4.22-ppc-dev.orig/include/linux/nfsd/xdr3.h 2009-08-07 06:23:27.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/nfsd/xdr3.h 2009-08-07 06:36:09.000000000 +0200 @@ -10,6 +10,7 @@ #define _LINUX_NFSD_XDR3_H #include +#include struct nfsd3_sattrargs { struct svc_fh fh; @@ -101,6 +102,18 @@ __u32 count; }; +struct nfsd3_getaclargs { + struct svc_fh fh; + int mask; +}; + +struct nfsd3_setaclargs { + struct svc_fh fh; + int mask; + struct posix_acl *acl_access; + struct posix_acl *acl_default; +}; + struct nfsd3_attrstat { __u32 status; struct svc_fh fh; @@ -192,6 +205,14 @@ struct svc_fh fh; }; +struct nfsd3_getaclres { + __u32 status; + struct svc_fh fh; + int mask; + struct posix_acl *acl_access; + struct posix_acl *acl_default; +}; + /* dummy type for release */ struct nfsd3_fhandle_pair { __u32 dummy; @@ -224,6 +245,7 @@ struct nfsd3_fsinfores fsinfores; struct nfsd3_pathconfres pathconfres; struct nfsd3_commitres commitres; + struct nfsd3_getaclres getaclres; }; #define NFS3_SVC_XDRSIZE sizeof(union nfsd3_xdrstore) @@ -257,6 +279,10 @@ struct nfsd3_readdirargs *); int nfs3svc_decode_commitargs(struct svc_rqst *, u32 *, struct nfsd3_commitargs *); +int nfs3svc_decode_getaclargs(struct svc_rqst *, u32 *, + struct nfsd3_getaclargs *); +int nfs3svc_decode_setaclargs(struct svc_rqst *, u32 *, + struct nfsd3_setaclargs *); int nfs3svc_encode_voidres(struct svc_rqst *, u32 *, void *); int nfs3svc_encode_attrstat(struct svc_rqst *, u32 *, struct nfsd3_attrstat *); @@ -286,11 +312,19 @@ struct nfsd3_pathconfres *); int nfs3svc_encode_commitres(struct svc_rqst *, u32 *, struct nfsd3_commitres *); +int nfs3svc_encode_getaclres(struct svc_rqst *, u32 *, + struct nfsd3_getaclres *); +int nfs3svc_encode_setaclres(struct svc_rqst *, u32 *, + struct nfsd3_attrstat *); + int nfs3svc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd3_attrstat *); int nfs3svc_release_fhandle2(struct svc_rqst *, u32 *, struct nfsd3_fhandle_pair *); +int nfs3svc_release_getacl(struct svc_rqst *rqstp, u32 *p, + struct nfsd3_getaclres *resp); + int nfs3svc_encode_entry(struct readdir_cd *, const char *name, int namlen, loff_t offset, ino_t ino, unsigned int); diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/posix_acl.h linux-2.4.22-ppc-dev/include/linux/posix_acl.h --- linux-2.4.22-ppc-dev.orig/include/linux/posix_acl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/posix_acl.h 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,87 @@ +/* + File: linux/posix_acl.h + + (C) 2002 Andreas Gruenbacher, +*/ + + +#ifndef __LINUX_POSIX_ACL_H +#define __LINUX_POSIX_ACL_H + +#include + +#define ACL_UNDEFINED_ID (-1) + +/* a_type field in acl_user_posix_entry_t */ +#define ACL_TYPE_ACCESS (0x8000) +#define ACL_TYPE_DEFAULT (0x4000) + +/* e_tag entry in struct posix_acl_entry */ +#define ACL_USER_OBJ (0x01) +#define ACL_USER (0x02) +#define ACL_GROUP_OBJ (0x04) +#define ACL_GROUP (0x08) +#define ACL_MASK (0x10) +#define ACL_OTHER (0x20) + +/* permissions in the e_perm field */ +#define ACL_READ (0x04) +#define ACL_WRITE (0x02) +#define ACL_EXECUTE (0x01) +//#define ACL_ADD (0x08) +//#define ACL_DELETE (0x10) + +struct posix_acl_entry { + short e_tag; + unsigned short e_perm; + unsigned int e_id; +}; + +struct posix_acl { + atomic_t a_refcount; + unsigned int a_count; + struct posix_acl_entry a_entries[0]; +}; + +#define FOREACH_ACL_ENTRY(pa, acl, pe) \ + for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; paa_refcount); + return acl; +} + +/* + * Free an ACL handle. + */ +static inline void +posix_acl_release(struct posix_acl *acl) +{ + if (acl && atomic_dec_and_test(&acl->a_refcount)) + kfree(acl); +} + + +/* posix_acl.c */ + +extern struct posix_acl *posix_acl_alloc(int, int); +extern struct posix_acl *posix_acl_clone(const struct posix_acl *, int); +extern int posix_acl_valid(const struct posix_acl *); +extern int posix_acl_permission(struct inode *, const struct posix_acl *, int); +extern struct posix_acl *posix_acl_from_mode(mode_t, int); +extern int posix_acl_equiv_mode(const struct posix_acl *, mode_t *); +extern int posix_acl_create_masq(struct posix_acl *, mode_t *); +extern int posix_acl_chmod_masq(struct posix_acl *, mode_t); +extern int posix_acl_masq_nfs_mode(struct posix_acl *, mode_t *); + +extern struct posix_acl *get_posix_acl(struct inode *, int); +extern int set_posix_acl(struct inode *, int, struct posix_acl *); + +#endif /* __LINUX_POSIX_ACL_H */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/posix_acl_xattr.h linux-2.4.22-ppc-dev/include/linux/posix_acl_xattr.h --- linux-2.4.22-ppc-dev.orig/include/linux/posix_acl_xattr.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/posix_acl_xattr.h 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,66 @@ +/* + File: linux/posix_acl_xattr.h + + Extended attribute system call representation of Access Control Lists. + + Copyright (C) 2000 by Andreas Gruenbacher + */ +#ifndef _POSIX_ACL_XATTR_H +#define _POSIX_ACL_XATTR_H + +/* Extended attribute names */ +#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access" +#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default" + +/* Supported ACL a_version fields */ +#define POSIX_ACL_XATTR_VERSION 0x0002 + + +/* An undefined entry e_id value */ +#define ACL_UNDEFINED_ID (-1) + +/* ACL entry e_tag field values */ +#define ACL_USER_OBJ (0x01) +#define ACL_USER (0x02) +#define ACL_GROUP_OBJ (0x04) +#define ACL_GROUP (0x08) +#define ACL_MASK (0x10) +#define ACL_OTHER (0x20) + +/* ACL entry e_perm bitfield values */ +#define ACL_READ (0x04) +#define ACL_WRITE (0x02) +#define ACL_EXECUTE (0x01) + + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} posix_acl_xattr_entry; + +typedef struct { + __u32 a_version; + posix_acl_xattr_entry a_entries[0]; +} posix_acl_xattr_header; + + +static inline size_t +posix_acl_xattr_size(int count) +{ + return (sizeof(posix_acl_xattr_header) + + (count * sizeof(posix_acl_xattr_entry))); +} + +static inline int +posix_acl_xattr_count(size_t size) +{ + if (size < sizeof(posix_acl_xattr_header)) + return -1; + size -= sizeof(posix_acl_xattr_header); + if (size % sizeof(posix_acl_xattr_entry)) + return -1; + return size / sizeof(posix_acl_xattr_entry); +} + +#endif /* _POSIX_ACL_XATTR_H */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/solaris_acl.h linux-2.4.22-ppc-dev/include/linux/solaris_acl.h --- linux-2.4.22-ppc-dev.orig/include/linux/solaris_acl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/solaris_acl.h 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,16 @@ +/* + File: linux/solaris_acl.h + + (C) 2002 Andreas Gruenbacher, +*/ + + +#ifndef __LINUX_SOLARIS_ACL_H +#define __LINUX_SOLARIS_ACL_H + +#include + +u32 *nfs_acl_encode(u32 *, u32 *, struct inode *, struct posix_acl *, int, int); +u32 *nfs_acl_decode(u32 *, u32 *, unsigned int *, struct posix_acl **); + +#endif /* __LINUX_SOLARIS_ACL_H */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/sunrpc/svc.h linux-2.4.22-ppc-dev/include/linux/sunrpc/svc.h --- linux-2.4.22-ppc-dev.orig/include/linux/sunrpc/svc.h 2009-08-07 06:23:27.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/sunrpc/svc.h 2009-08-07 06:36:09.000000000 +0200 @@ -131,9 +131,10 @@ }; /* - * RPC program + * List of RPC programs on the same transport endpoint */ struct svc_program { + struct svc_program * pg_next; /* other programs */ u32 pg_prog; /* program number */ unsigned int pg_lovers; /* lowest version */ unsigned int pg_hivers; /* lowest version */ diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/sunrpc/xprt.h linux-2.4.22-ppc-dev/include/linux/sunrpc/xprt.h --- linux-2.4.22-ppc-dev.orig/include/linux/sunrpc/xprt.h 2009-08-07 06:23:27.000000000 +0200 +++ linux-2.4.22-ppc-dev/include/linux/sunrpc/xprt.h 2009-08-07 06:36:08.000000000 +0200 @@ -152,6 +152,8 @@ nocong : 1, /* no congestion control */ resvport : 1, /* use a reserved port */ stream : 1; /* TCP */ + unsigned int clients; /* Number of clients using + this transport */ /* * State of TCP reply receive stuff diff -Naur linux-2.4.22-ppc-dev.orig/include/linux/xattr_acl.h linux-2.4.22-ppc-dev/include/linux/xattr_acl.h --- linux-2.4.22-ppc-dev.orig/include/linux/xattr_acl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/include/linux/xattr_acl.h 2009-08-07 06:36:09.000000000 +0200 @@ -0,0 +1,50 @@ +/* + File: linux/xattr_acl.h + + (extended attribute representation of access control lists) + + (C) 2000 Andreas Gruenbacher, +*/ + +#ifndef _LINUX_XATTR_ACL_H +#define _LINUX_XATTR_ACL_H + +#include + +#define XATTR_NAME_ACL_ACCESS "system.posix_acl_access" +#define XATTR_NAME_ACL_DEFAULT "system.posix_acl_default" + +#define XATTR_ACL_VERSION 0x0002 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} xattr_acl_entry; + +typedef struct { + __u32 a_version; + xattr_acl_entry a_entries[0]; +} xattr_acl_header; + +static inline size_t xattr_acl_size(int count) +{ + return sizeof(xattr_acl_header) + count * sizeof(xattr_acl_entry); +} + +static inline int xattr_acl_count(size_t size) +{ + if (size < sizeof(xattr_acl_header)) + return -1; + size -= sizeof(xattr_acl_header); + if (size % sizeof(xattr_acl_entry)) + return -1; + return size / sizeof(xattr_acl_entry); +} + +struct posix_acl * posix_acl_from_xattr(const void *value, size_t size); +int posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size); + + + +#endif /* _LINUX_XATTR_ACL_H */ diff -Naur linux-2.4.22-ppc-dev.orig/kernel/Makefile linux-2.4.22-ppc-dev/kernel/Makefile --- linux-2.4.22-ppc-dev.orig/kernel/Makefile 2009-08-07 06:32:32.000000000 +0200 +++ linux-2.4.22-ppc-dev/kernel/Makefile 2009-08-07 10:10:21.000000000 +0200 @@ -9,7 +9,7 @@ O_TARGET := kernel.o -export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o cpufreq.o +export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o cpufreq.o fork.o obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o info.o time.o softirq.o resource.o \ diff -Naur linux-2.4.22-ppc-dev.orig/kernel/fork.c linux-2.4.22-ppc-dev/kernel/fork.c --- linux-2.4.22-ppc-dev.orig/kernel/fork.c 2009-08-07 06:35:01.000000000 +0200 +++ linux-2.4.22-ppc-dev/kernel/fork.c 2009-08-07 06:36:09.000000000 +0200 @@ -409,6 +409,7 @@ { return __copy_fs_struct(old); } +EXPORT_SYMBOL(copy_fs_struct); static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) { diff -Naur linux-2.4.22-ppc-dev.orig/kernel/ksyms.c linux-2.4.22-ppc-dev/kernel/ksyms.c --- linux-2.4.22-ppc-dev.orig/kernel/ksyms.c 2009-08-07 06:35:01.000000000 +0200 +++ linux-2.4.22-ppc-dev/kernel/ksyms.c 2009-08-07 06:36:09.000000000 +0200 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -109,6 +110,8 @@ 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); @@ -342,7 +345,7 @@ EXPORT_SYMBOL(do_SAK); /* filesystem registration */ -EXPORT_SYMBOL(register_filesystem); +EXPORT_SYMBOL(__register_filesystem); EXPORT_SYMBOL(unregister_filesystem); EXPORT_SYMBOL(kern_mount); EXPORT_SYMBOL(__mntput); @@ -568,6 +571,9 @@ EXPORT_SYMBOL(strnicmp); EXPORT_SYMBOL(strspn); EXPORT_SYMBOL(strsep); +#if defined(CONFIG_QSORT) && ! defined(CONFIG_QSORT_MODULE) +EXPORT_SYMBOL(qsort); +#endif #ifdef CONFIG_CRC32 EXPORT_SYMBOL(crc32_le); diff -Naur linux-2.4.22-ppc-dev.orig/lib/Config.in linux-2.4.22-ppc-dev/lib/Config.in --- linux-2.4.22-ppc-dev.orig/lib/Config.in 2009-08-07 06:23:23.000000000 +0200 +++ linux-2.4.22-ppc-dev/lib/Config.in 2009-08-07 06:36:09.000000000 +0200 @@ -41,4 +41,6 @@ fi fi +tristate 'Quick Sort' CONFIG_QSORT + endmenu diff -Naur linux-2.4.22-ppc-dev.orig/lib/Makefile linux-2.4.22-ppc-dev/lib/Makefile --- linux-2.4.22-ppc-dev.orig/lib/Makefile 2009-08-07 06:23:23.000000000 +0200 +++ linux-2.4.22-ppc-dev/lib/Makefile 2009-08-07 10:11:24.000000000 +0200 @@ -9,13 +9,14 @@ L_TARGET := lib.a export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \ - rbtree.o crc32.o + rbtree.o crc32.o qsort.o obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \ bust_spinlocks.o rbtree.o dump_stack.o obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o +obj-$(CONFIG_QSORT) += qsort.o ifneq ($(CONFIG_HAVE_DEC_LOCK),y) obj-y += dec_and_lock.o diff -Naur linux-2.4.22-ppc-dev.orig/lib/qsort.c linux-2.4.22-ppc-dev/lib/qsort.c --- linux-2.4.22-ppc-dev.orig/lib/qsort.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.22-ppc-dev/lib/qsort.c 2009-08-07 06:36:08.000000000 +0200 @@ -0,0 +1,254 @@ +/* Copyright (C) 1991, 1992, 1996, 1997, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Douglas C. Schmidt (schmidt@ics.uci.edu). + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* If you consider tuning this algorithm, you should consult first: + Engineering a sort function; Jon Bentley and M. Douglas McIlroy; + Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */ + +# include +# include +# include + +MODULE_LICENSE("GPL"); + +/* Byte-wise swap two items of size SIZE. */ +#define SWAP(a, b, size) \ + do \ + { \ + register size_t __size = (size); \ + register char *__a = (a), *__b = (b); \ + do \ + { \ + char __tmp = *__a; \ + *__a++ = *__b; \ + *__b++ = __tmp; \ + } while (--__size > 0); \ + } while (0) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define MAX_THRESH 4 + +/* Stack node declarations used to store unfulfilled partition obligations. */ +typedef struct + { + char *lo; + char *hi; + } stack_node; + +/* The next 5 #defines implement a very fast in-line stack abstraction. */ +/* The stack needs log (total_elements) entries (we could even subtract + log(MAX_THRESH)). Since total_elements has type size_t, we get as + upper bound for log (total_elements): + bits per byte (CHAR_BIT) * sizeof(size_t). */ +#define CHAR_BIT 8 +#define STACK_SIZE (CHAR_BIT * sizeof(size_t)) +#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top)) +#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi))) +#define STACK_NOT_EMPTY (stack < top) + + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that store the + next array partition to sort. To save time, this maximum amount + of space required to store an array of SIZE_MAX is allocated on the + stack. Assuming a 32-bit (64 bit) integer for size_t, this needs + only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). + Pretty cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segments. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (total_elems) + stack size is needed (actually O(1) in this case)! */ + +void +qsort(void *const pbase, size_t total_elems, size_t size, + int(*cmp)(const void *,const void *)) +{ + register char *base_ptr = (char *) pbase; + + const size_t max_thresh = MAX_THRESH * size; + + if (total_elems == 0) + /* Avoid lossage with unsigned arithmetic below. */ + return; + + if (total_elems > MAX_THRESH) + { + char *lo = base_ptr; + char *hi = &lo[size * (total_elems - 1)]; + stack_node stack[STACK_SIZE]; + stack_node *top = stack + 1; + + while (STACK_NOT_EMPTY) + { + char *left_ptr; + char *right_ptr; + + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR in + the while loops. */ + + char *mid = lo + size * ((hi - lo) / size >> 1); + + if ((*cmp) ((void *) mid, (void *) lo) < 0) + SWAP (mid, lo, size); + if ((*cmp) ((void *) hi, (void *) mid) < 0) + SWAP (mid, hi, size); + else + goto jump_over; + if ((*cmp) ((void *) mid, (void *) lo) < 0) + SWAP (mid, lo, size); + jump_over:; + + left_ptr = lo + size; + right_ptr = hi - size; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do + { + while ((*cmp) ((void *) left_ptr, (void *) mid) < 0) + left_ptr += size; + + while ((*cmp) ((void *) mid, (void *) right_ptr) < 0) + right_ptr -= size; + + if (left_ptr < right_ptr) + { + SWAP (left_ptr, right_ptr, size); + if (mid == left_ptr) + mid = right_ptr; + else if (mid == right_ptr) + mid = left_ptr; + left_ptr += size; + right_ptr -= size; + } + else if (left_ptr == right_ptr) + { + left_ptr += size; + right_ptr -= size; + break; + } + } + while (left_ptr <= right_ptr); + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if ((size_t) (right_ptr - lo) <= max_thresh) + { + if ((size_t) (hi - left_ptr) <= max_thresh) + /* Ignore both small partitions. */ + POP (lo, hi); + else + /* Ignore small left partition. */ + lo = left_ptr; + } + else if ((size_t) (hi - left_ptr) <= max_thresh) + /* Ignore small right partition. */ + hi = right_ptr; + else if ((right_ptr - lo) > (hi - left_ptr)) + { + /* Push larger left partition indices. */ + PUSH (lo, right_ptr); + lo = left_ptr; + } + else + { + /* Push larger right partition indices. */ + PUSH (left_ptr, hi); + hi = right_ptr; + } + } + } + + /* Once the BASE_PTR array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE_PTR points to the beginning + of the array to sort, and END_PTR points at the very last element in + the array (*not* one beyond it!). */ + + { + char *end_ptr = &base_ptr[size * (total_elems - 1)]; + char *tmp_ptr = base_ptr; + char *thresh = min(end_ptr, base_ptr + max_thresh); + register char *run_ptr; + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size) + if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr) < 0) + tmp_ptr = run_ptr; + + if (tmp_ptr != base_ptr) + SWAP (tmp_ptr, base_ptr, size); + + /* Insertion sort, running from left-hand-side up to right-hand-side. */ + + run_ptr = base_ptr + size; + while ((run_ptr += size) <= end_ptr) + { + tmp_ptr = run_ptr - size; + while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr) < 0) + tmp_ptr -= size; + + tmp_ptr += size; + if (tmp_ptr != run_ptr) + { + char *trav; + + trav = run_ptr + size; + while (--trav >= run_ptr) + { + char c = *trav; + char *hi, *lo; + + for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo) + *hi = *lo; + *hi = c; + } + } + } + } +} +/* If qsort is built into the kernel, we export the qsort symbol in + kernel/ksyms.c: The files in lib/ end up in lib.a, so qsort.o only + gets linked into the kernel image if it is used somewhere. */ +#if defined(CONFIG_QSORT_MODULE) +EXPORT_SYMBOL(qsort); +#endif diff -Naur linux-2.4.22-ppc-dev.orig/mm/vmscan.c linux-2.4.22-ppc-dev/mm/vmscan.c --- linux-2.4.22-ppc-dev.orig/mm/vmscan.c 2009-08-07 06:23:23.000000000 +0200 +++ linux-2.4.22-ppc-dev/mm/vmscan.c 2009-08-07 06:36:09.000000000 +0200 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,49 @@ #define DEF_PRIORITY (6) /* + * 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, @@ -580,6 +624,7 @@ #ifdef CONFIG_QUOTA shrink_dqcache_memory(DEF_PRIORITY, gfp_mask); #endif + shrink_other_caches(priority, gfp_mask); return nr_pages; } diff -Naur linux-2.4.22-ppc-dev.orig/net/sunrpc/clnt.c linux-2.4.22-ppc-dev/net/sunrpc/clnt.c --- linux-2.4.22-ppc-dev.orig/net/sunrpc/clnt.c 2009-08-07 06:23:42.000000000 +0200 +++ linux-2.4.22-ppc-dev/net/sunrpc/clnt.c 2009-08-07 06:36:09.000000000 +0200 @@ -89,6 +89,8 @@ memset(clnt, 0, sizeof(*clnt)); atomic_set(&clnt->cl_users, 0); + xprt->clients++; + clnt->cl_xprt = xprt; clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; @@ -166,7 +168,7 @@ rpcauth_destroy(clnt->cl_auth); clnt->cl_auth = NULL; } - if (clnt->cl_xprt) { + if (clnt->cl_xprt && !(--clnt->cl_xprt->clients)) { xprt_destroy(clnt->cl_xprt); clnt->cl_xprt = NULL; } @@ -923,6 +925,16 @@ return p; case RPC_GARBAGE_ARGS: break; /* retry */ + case RPC_PROG_UNAVAIL: + /* report requested program number*/ + dprintk(KERN_WARNING "RPC: unknown program\n"); + rpc_exit(task, -ENOSYS); + return NULL; + case RPC_PROC_UNAVAIL: + /* report requested program and procedure number, version */ + dprintk(KERN_WARNING "RPC: unknown procedure\n"); + rpc_exit(task, -ENOSYS); + return NULL; default: printk(KERN_WARNING "call_verify: server accept status: %x\n", n); /* Also retry */ diff -Naur linux-2.4.22-ppc-dev.orig/net/sunrpc/svc.c linux-2.4.22-ppc-dev/net/sunrpc/svc.c --- linux-2.4.22-ppc-dev.orig/net/sunrpc/svc.c 2009-08-07 06:23:42.000000000 +0200 +++ linux-2.4.22-ppc-dev/net/sunrpc/svc.c 2009-08-07 06:36:09.000000000 +0200 @@ -281,8 +281,10 @@ if (auth_stat != rpc_auth_ok) goto err_bad_auth; - progp = serv->sv_program; - if (prog != progp->pg_prog) + for (progp = serv->sv_program; progp; progp = progp->pg_next) + if (prog == progp->pg_prog) + break; + if (progp == NULL) goto err_bad_prog; if (vers >= progp->pg_nvers || @@ -390,9 +392,7 @@ err_bad_prog: #ifdef RPC_PARANOIA - if (prog != 100227 || progp->pg_prog != 100003) - printk("svc: unknown program %d (me %d)\n", prog, progp->pg_prog); - /* else it is just a Solaris client seeing if ACLs are supported */ + printk("svc: unknown program %d\n", prog); #endif serv->sv_stats->rpcbadfmt++; svc_putlong(resp, rpc_prog_unavail);