Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 22211 Details for
Bug 35819
Proposed patches for ppc-sources-2.4.22-r4
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
50_ea+acl+nfsacl-0.8.60
50_ea+acl+nfsacl-0.8.60 (text/plain), 316.41 KB, created by
David Holm (RETIRED)
on 2003-12-14 12:14:24 UTC
(
hide
)
Description:
50_ea+acl+nfsacl-0.8.60
Filename:
MIME Type:
Creator:
David Holm (RETIRED)
Created:
2003-12-14 12:14:24 UTC
Size:
316.41 KB
patch
obsolete
>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 2003-09-14 14:13:38.000000000 +0200 >+++ linux-2.4.22-ppc-dev/Documentation/Configure.help 2003-09-14 14:13:51.000000000 +0200 >@@ -13024,6 +13024,20 @@ > The module will be called isp16.o. If you want to compile it as a > module, say M here and read <file:Documentation/modules.txt>. > >+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 <http://acl.bestbits.at/>. >+ >+ 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 >@@ -15627,6 +15641,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 >+ <http://acl.bestbits.at/> 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 <http://acl.bestbits.at/> 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 <http://acl.bestbits.at/>. >+ >+ If unsure, say N. >+ > Ext3 journalling file system support (EXPERIMENTAL) > CONFIG_EXT3_FS > This is the journalling version of the Second extended file system >@@ -15659,6 +15710,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 >+ <http://acl.bestbits.at/> 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 <http://acl.bestbits.at/> 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 <http://acl.bestbits.at/>. >+ >+ 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 >@@ -16153,6 +16241,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 >@@ -16197,6 +16294,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 2003-09-14 14:04:18.000000000 +0200 >+++ linux-2.4.22-ppc-dev/Documentation/filesystems/Locking 2003-09-14 14:13:51.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 2003-09-14 14:03:58.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/alpha/defconfig 2003-09-14 14:13:51.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 2003-09-14 14:04:00.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/alpha/kernel/entry.S 2003-09-14 14:13:51.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 <asm/machvec.h>. >@@ -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 2003-09-14 14:04:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/arm/defconfig 2003-09-14 14:13:51.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 2003-09-14 14:04:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/arm/kernel/calls.S 2003-09-14 14:13:51.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 2003-09-14 14:03:57.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/i386/defconfig 2003-09-14 14:13:51.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 2003-09-14 14:04:12.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/ia64/defconfig 2003-09-14 14:13:51.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 2003-09-14 14:04:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/m68k/defconfig 2003-09-14 14:13:51.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 2003-09-14 14:04:01.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/mips/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:04:14.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/mips64/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:04:03.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/ppc/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:03:57.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/ppc64/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:03:57.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/ppc64/kernel/misc.S 2003-09-14 14:13:52.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 2003-09-14 14:04:15.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/s390/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:04:15.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/s390/kernel/entry.S 2003-09-14 14:13:52.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 2003-09-14 14:04:17.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/s390x/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:04:17.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/s390x/kernel/entry.S 2003-09-14 14:13:52.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 2003-09-14 14:04:17.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/s390x/kernel/wrapper32.S 2003-09-14 14:13:52.000000000 +0200 >@@ -1098,6 +1098,96 @@ > llgfr %r4,%r4 # long > jg sys32_fstat64 # branch to system call > >+ .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 >+ > .globl sys32_stime_wrapper > sys32_stime_wrapper: > llgtr %r2,%r2 # int * >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 2003-09-14 14:04:00.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/sparc/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:04:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/arch/sparc64/defconfig 2003-09-14 14:13:52.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 2003-09-14 14:07:49.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/Config.in 2003-09-14 14:13:52.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 2003-09-14 14:07:49.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/Makefile 2003-09-14 14:13:52.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext2/Makefile 2003-09-14 14:13:52.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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,575 @@ >+/* >+ * linux/fs/ext2/acl.c >+ * >+ * Copyright (C) 2001-2003 by Andreas Gruenbacher, <agruen@suse.de> >+ */ >+ >+#include <linux/module.h> >+#include <linux/sched.h> >+#include <linux/slab.h> >+#include <linux/fs.h> >+#include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+#include <linux/ext2_acl.h> >+ >+/* >+ * 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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext2/file.c 2003-09-14 14:13:52.000000000 +0200 >@@ -20,6 +20,8 @@ > > #include <linux/fs.h> > #include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+#include <linux/ext2_acl.h> > #include <linux/sched.h> > > /* >@@ -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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext2/ialloc.c 2003-09-14 14:13:52.000000000 +0200 >@@ -15,6 +15,8 @@ > #include <linux/config.h> > #include <linux/fs.h> > #include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+#include <linux/ext2_acl.h> > #include <linux/locks.h> > #include <linux/quotaops.h> > >@@ -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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext2/inode.c 2003-09-14 14:13:52.000000000 +0200 >@@ -24,6 +24,7 @@ > > #include <linux/fs.h> > #include <linux/ext2_fs.h> >+#include <linux/ext2_acl.h> > #include <linux/locks.h> > #include <linux/smp_lock.h> > #include <linux/sched.h> >@@ -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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext2/namei.c 2003-09-14 14:13:52.000000000 +0200 >@@ -31,6 +31,8 @@ > > #include <linux/fs.h> > #include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+#include <linux/ext2_acl.h> > #include <linux/pagemap.h> > > /* >@@ -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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext2/super.c 2003-09-14 14:13:52.000000000 +0200 >@@ -21,6 +21,8 @@ > #include <linux/string.h> > #include <linux/fs.h> > #include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+#include <linux/ext2_acl.h> > #include <linux/slab.h> > #include <linux/init.h> > #include <linux/locks.h> >@@ -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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext2/symlink.c 2003-09-14 14:13:52.000000000 +0200 >@@ -19,6 +19,7 @@ > > #include <linux/fs.h> > #include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> > > 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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,1165 @@ >+/* >+ * linux/fs/ext2/xattr.c >+ * >+ * Copyright (C) 2001-2003 by Andreas Gruenbacher, <agruen@suse.de> >+ * >+ * Fix by Harrison Xing <harrison@mountainviewdata.com>. >+ * Extended attributes for symlinks and special files added per >+ * suggestion of Luka Renko <luka.renko@hermes.si>. >+ */ >+ >+/* >+ * 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 <linux/module.h> >+#include <linux/locks.h> >+#include <linux/slab.h> >+#include <linux/fs.h> >+#include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+#include <linux/mbcache.h> >+#include <linux/quotaops.h> >+#include <linux/rwsem.h> >+ >+/* These symbols may be needed by a module. */ >+EXPORT_SYMBOL(ext2_xattr_register); >+EXPORT_SYMBOL(ext2_xattr_unregister); >+EXPORT_SYMBOL(ext2_xattr_get); >+EXPORT_SYMBOL(ext2_xattr_list); >+EXPORT_SYMBOL(ext2_xattr_set); >+ >+#define HDR(bh) ((struct ext2_xattr_header *)((bh)->b_data)) >+#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr)) >+#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1) >+#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; i<EXT2_XATTR_INDEX_MAX; i++) { >+ if (ext2_xattr_handlers[i]) { >+ const char *n = strcmp_prefix(*name, >+ ext2_xattr_handlers[i]->prefix); >+ 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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,76 @@ >+/* >+ * linux/fs/ext2/xattr_trusted.c >+ * Handler for trusted extended attributes. >+ * >+ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org> >+ */ >+ >+#include <linux/sched.h> >+#include <linux/fs.h> >+#include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+ >+#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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,95 @@ >+/* >+ * linux/fs/ext2/xattr_user.c >+ * Handler for extended user attributes. >+ * >+ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org> >+ */ >+ >+#include <linux/module.h> >+#include <linux/string.h> >+#include <linux/fs.h> >+#include <linux/ext2_fs.h> >+#include <linux/ext2_xattr.h> >+ >+#ifdef CONFIG_EXT2_FS_POSIX_ACL >+# include <linux/ext2_acl.h> >+#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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext3/Makefile 2003-09-14 14:13:52.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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,585 @@ >+/* >+ * linux/fs/ext3/acl.c >+ * >+ * Copyright (C) 2001-2003 by Andreas Gruenbacher, <agruen@suse.de> >+ */ >+ >+#include <linux/module.h> >+#include <linux/sched.h> >+#include <linux/slab.h> >+#include <linux/fs.h> >+#include <linux/ext3_jbd.h> >+#include <linux/ext3_fs.h> >+#include <linux/ext3_xattr.h> >+#include <linux/ext3_acl.h> >+ >+/* >+ * 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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext3/file.c 2003-09-14 14:13:52.000000000 +0200 >@@ -21,8 +21,11 @@ > #include <linux/sched.h> > #include <linux/fs.h> > #include <linux/locks.h> >+#include <linux/ext3_jbd.h> > #include <linux/jbd.h> > #include <linux/ext3_fs.h> >+#include <linux/ext3_xattr.h> >+#include <linux/ext3_acl.h> > #include <linux/ext3_jbd.h> > #include <linux/smp_lock.h> > >@@ -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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext3/ialloc.c 2003-09-14 14:13:52.000000000 +0200 >@@ -17,6 +17,8 @@ > #include <linux/jbd.h> > #include <linux/ext3_fs.h> > #include <linux/ext3_jbd.h> >+#include <linux/ext3_xattr.h> >+#include <linux/ext3_acl.h> > #include <linux/stat.h> > #include <linux/string.h> > #include <linux/locks.h> >@@ -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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext3/inode.c 2003-09-14 14:13:52.000000000 +0200 >@@ -26,6 +26,8 @@ > #include <linux/sched.h> > #include <linux/ext3_jbd.h> > #include <linux/jbd.h> >+#include <linux/ext3_xattr.h> >+#include <linux/ext3_acl.h> > #include <linux/locks.h> > #include <linux/smp_lock.h> > #include <linux/highuid.h> >@@ -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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext3/namei.c 2003-09-14 14:13:52.000000000 +0200 >@@ -23,6 +23,8 @@ > #include <linux/sched.h> > #include <linux/ext3_fs.h> > #include <linux/ext3_jbd.h> >+#include <linux/ext3_xattr.h> >+#include <linux/ext3_acl.h> > #include <linux/fcntl.h> > #include <linux/stat.h> > #include <linux/string.h> >@@ -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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext3/super.c 2003-09-14 14:13:52.000000000 +0200 >@@ -24,6 +24,8 @@ > #include <linux/jbd.h> > #include <linux/ext3_fs.h> > #include <linux/ext3_jbd.h> >+#include <linux/ext3_xattr.h> >+#include <linux/ext3_acl.h> > #include <linux/slab.h> > #include <linux/init.h> > #include <linux/locks.h> >@@ -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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/ext3/symlink.c 2003-09-14 14:13:52.000000000 +0200 >@@ -20,6 +20,7 @@ > #include <linux/fs.h> > #include <linux/jbd.h> > #include <linux/ext3_fs.h> >+#include <linux/ext3_xattr.h> > > 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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,1221 @@ >+/* >+ * linux/fs/ext3/xattr.c >+ * >+ * Copyright (C) 2001-2003 by Andreas Gruenbacher, <agruen@suse.de> >+ * >+ * Fix by Harrison Xing <harrison@mountainviewdata.com>. >+ * Ext3 code with a lot of help from Eric Jarman <ejarman@acm.org>. >+ * Extended attributes for symlinks and special files added per >+ * suggestion of Luka Renko <luka.renko@hermes.si>. >+ */ >+ >+/* >+ * 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 <linux/fs.h> >+#include <linux/locks.h> >+#include <linux/slab.h> >+#include <linux/ext3_jbd.h> >+#include <linux/ext3_fs.h> >+#include <linux/ext3_xattr.h> >+#include <linux/mbcache.h> >+#include <linux/quotaops.h> >+#include <linux/rwsem.h> >+ >+#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; i<EXT3_XATTR_INDEX_MAX; i++) { >+ if (ext3_xattr_handlers[i]) { >+ const char *n = strcmp_prefix(*name, >+ ext3_xattr_handlers[i]->prefix); >+ 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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,76 @@ >+/* >+ * linux/fs/ext3/xattr_trusted.c >+ * Handler for trusted extended attributes. >+ * >+ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org> >+ */ >+ >+#include <linux/sched.h> >+#include <linux/fs.h> >+#include <linux/ext3_jbd.h> >+#include <linux/ext3_fs.h> >+#include <linux/ext3_xattr.h> >+ >+#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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,96 @@ >+/* >+ * linux/fs/ext3/xattr_user.c >+ * Handler for extended user attributes. >+ * >+ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org> >+ */ >+ >+#include <linux/module.h> >+#include <linux/string.h> >+#include <linux/fs.h> >+#include <linux/ext3_jbd.h> >+#include <linux/ext3_fs.h> >+#include <linux/ext3_xattr.h> >+ >+#ifdef CONFIG_EXT3_FS_POSIX_ACL >+# include <linux/ext3_acl.h> >+#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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/jbd/journal.c 2003-09-14 14:13:52.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 2003-09-14 14:06:54.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/jbd/transaction.c 2003-09-14 14:13:52.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/jfs/jfs_xattr.h 2003-09-14 14:13:52.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/jfs/xattr.c 2003-09-14 14:13:52.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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/lockd/svc.c 2003-09-14 14:13:52.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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,649 @@ >+/* >+ * linux/fs/mbcache.c >+ * (C) 2001-2002 Andreas Gruenbacher, <a.gruenbacher@computer.org> >+ */ >+ >+/* >+ * 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 <linux/kernel.h> >+#include <linux/module.h> >+ >+#include <linux/fs.h> >+#include <linux/slab.h> >+#include <linux/sched.h> >+#include <linux/cache_def.h> >+#include <linux/version.h> >+#include <linux/init.h> >+#include <linux/mbcache.h> >+ >+ >+#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 <a.gruenbacher@computer.org>"); >+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; n<mb_cache_indexes(ce->e_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; n<bucket_count; n++) >+ INIT_LIST_HEAD(&cache->c_block_hash[n]); >+ for (m=0; m<indexes_count; m++) { >+ cache->c_indexes_hash[m] = kmalloc(bucket_count * >+ sizeof(struct list_head), >+ GFP_KERNEL); >+ if (!cache->c_indexes_hash[m]) >+ goto fail; >+ for (n=0; n<bucket_count; n++) >+ INIT_LIST_HEAD(&cache->c_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; n<mb_cache_indexes(cache); n++) { >+ ce->e_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<=index<indexes_count) >+ * @dev: the device the cache entry should belong to >+ * @key: the key in the index >+ */ >+struct mb_cache_entry * >+mb_cache_entry_find_first(struct mb_cache *cache, int index, kdev_t dev, >+ unsigned int key) >+{ >+ 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 = 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<=index<indexes_count) >+ * @dev: the device the cache entry should belong to >+ * @key: the key in the index >+ */ >+struct mb_cache_entry * >+mb_cache_entry_find_next(struct mb_cache_entry *prev, int index, kdev_t dev, >+ unsigned int key) >+{ >+ struct mb_cache *cache = prev->e_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 2003-09-14 14:13:39.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/namei.c 2003-09-14 14:13:52.000000000 +0200 >@@ -1078,8 +1078,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); > >@@ -1343,7 +1344,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; >@@ -1433,9 +1435,11 @@ > if (!gr_acl_handle_mkdir(dentry, nd.dentry, nd.mnt)) > error = -EACCES; > >- if(!error) >- error = vfs_mkdir(nd.dentry->d_inode, dentry, >- mode & ~current->fs->umask); >+ if(!error) { >+ if (!IS_POSIXACL(nd.dentry->d_inode)) >+ 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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfs/Makefile 2003-09-14 14:13:52.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfs/dir.c 2003-09-14 14:13:52.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfs/file.c 2003-09-14 14:13:52.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfs/inode.c 2003-09-14 14:13:52.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfs/nfs3proc.c 2003-09-14 14:13:52.000000000 +0200 >@@ -14,6 +14,7 @@ > #include <linux/nfs.h> > #include <linux/nfs3.h> > #include <linux/nfs_fs.h> >+#include <linux/nfs_mount.h> > > #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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfs/nfs3xdr.c 2003-09-14 14:13:52.000000000 +0200 >@@ -21,6 +21,7 @@ > #include <linux/nfs.h> > #include <linux/nfs3.h> > #include <linux/nfs_fs.h> >+#include <linux/solaris_acl.h> > > #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 2003-09-14 14:13:52.000000000 +0200 >@@ -0,0 +1,115 @@ >+#include <linux/fs.h> >+#include <linux/nfs.h> >+#include <linux/nfs3.h> >+#include <linux/nfs_fs.h> >+#include <linux/xattr_acl.h> >+ >+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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfsd/export.c 2003-09-14 14:13:53.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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfsd/nfs3proc.c 2003-09-14 14:13:53.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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfsd/nfs3xdr.c 2003-09-14 14:13:53.000000000 +0200 >@@ -15,6 +15,8 @@ > #include <linux/nfsd/nfsd.h> > #include <linux/nfsd/xdr3.h> > >+#include <linux/solaris_acl.h> >+ > #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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfsd/nfsproc.c 2003-09-14 14:13:53.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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfsd/nfssvc.c 2003-09-14 14:13:53.000000000 +0200 >@@ -34,7 +34,10 @@ > #include <linux/lockd/bind.h> > > #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); > >@@ -314,6 +332,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 > }; >@@ -333,10 +380,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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfsd/nfsxdr.c 2003-09-14 14:13:53.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 2003-09-14 14:03:07.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/nfsd/vfs.c 2003-09-14 14:13:53.000000000 +0200 >@@ -22,6 +22,7 @@ > #include <linux/errno.h> > #include <linux/locks.h> > #include <linux/fs.h> >+#include <linux/xattr_acl.h> > #include <linux/major.h> > #include <linux/ext2_fs.h> > #include <linux/proc_fs.h> >@@ -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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,430 @@ >+/* >+ * linux/fs/posix_acl.c >+ * >+ * Copyright (C) 2002 by Andreas Gruenbacher <a.gruenbacher@computer.org> >+ * >+ * Fixes from William Schumacher incorporated on 15 March 2001. >+ * (Reported by Charles Bertsch, <CBertsch@microtest.com>). >+ */ >+ >+/* >+ * This file contains generic functions for manipulating >+ * POSIX 1003.1e draft standard 17 ACLs. >+ */ >+ >+#include <linux/version.h> >+#include <linux/kernel.h> >+#include <linux/slab.h> >+#include <asm/atomic.h> >+#include <linux/fs.h> >+#include <linux/posix_acl.h> >+#include <linux/module.h> >+ >+#include <linux/smp_lock.h> >+#include <linux/errno.h> >+ >+MODULE_AUTHOR("Andreas Gruenbacher <a.gruenbacher@computer.org>"); >+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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,198 @@ >+/* >+ * linux/fs/solaris_acl.c >+ * >+ * Copyright (C) 2002 by Andreas Gruenbacher <a.gruenbacher@computer.org> >+ */ >+ >+/* >+ * 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 <linux/module.h> >+#include <linux/solaris_acl.h> >+#include <linux/nfs3.h> >+ >+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 2003-09-14 14:03:05.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/super.c 2003-09-14 14:13:53.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 2003-09-14 14:03:06.000000000 +0200 >+++ linux-2.4.22-ppc-dev/fs/xattr.c 2003-09-14 14:13:53.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 2003-09-14 14:13:53.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, <a.gruenbacher@computer.org> >+ */ >+ >+#include <linux/module.h> >+#include <linux/sched.h> >+#include <linux/slab.h> >+#include <linux/fs.h> >+#include <linux/xattr_acl.h> >+ >+ >+/* >+ * 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 2003-09-14 14:03:17.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/asm-arm/unistd.h 2003-09-14 14:13:53.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 2003-09-14 14:03:22.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/asm-s390/unistd.h 2003-09-14 14:13:53.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 2003-09-14 14:03:22.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/asm-s390x/unistd.h 2003-09-14 14:13:53.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 2003-09-14 14:03:15.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/asm-sparc/unistd.h 2003-09-14 14:13:53.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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,15 @@ >+/* >+ * linux/cache_def.h >+ * Handling of caches defined in drivers, filesystems, ... >+ * >+ * Copyright (C) 2002 by Andreas Gruenbacher, <a.gruenbacher@computer.org> >+ */ >+ >+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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,101 @@ >+/* >+ File: linux/ext2_acl.h >+ >+ (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org> >+*/ >+ >+#include <linux/init.h> >+#include <linux/posix_acl.h> >+#include <linux/xattr_acl.h> >+ >+#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 <linux/sched.h> >+# 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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/ext2_fs.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/ext2_fs_i.h 2003-09-14 14:13:53.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 2003-09-14 14:13:53.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, <a.gruenbacher@computer.org> >+*/ >+ >+#include <linux/config.h> >+#include <linux/init.h> >+#include <linux/xattr.h> >+ >+/* 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<<EXT2_XATTR_PAD_BITS) >+#define EXT2_XATTR_ROUND (EXT2_XATTR_PAD-1) >+#define EXT2_XATTR_LEN(name_len) \ >+ (((name_len) + EXT2_XATTR_ROUND + \ >+ sizeof(struct ext2_xattr_entry)) & ~EXT2_XATTR_ROUND) >+#define EXT2_XATTR_NEXT(entry) \ >+ ( (struct ext2_xattr_entry *)( \ >+ (char *)(entry) + EXT2_XATTR_LEN((entry)->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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,108 @@ >+/* >+ File: linux/ext3_acl.h >+ >+ (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org> >+*/ >+ >+#include <linux/init.h> >+#include <linux/posix_acl.h> >+#include <linux/xattr_acl.h> >+ >+#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 <linux/sched.h> >+# 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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/ext3_fs.h 2003-09-14 14:13:53.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 2003-09-14 14:03:12.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/ext3_fs_i.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/ext3_jbd.h 2003-09-14 14:13:53.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 2003-09-14 14:13:53.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, <a.gruenbacher@computer.org> >+*/ >+ >+#include <linux/config.h> >+#include <linux/init.h> >+#include <linux/xattr.h> >+ >+/* 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<<EXT3_XATTR_PAD_BITS) >+#define EXT3_XATTR_ROUND (EXT3_XATTR_PAD-1) >+#define EXT3_XATTR_LEN(name_len) \ >+ (((name_len) + EXT3_XATTR_ROUND + \ >+ sizeof(struct ext3_xattr_entry)) & ~EXT3_XATTR_ROUND) >+#define EXT3_XATTR_NEXT(entry) \ >+ ( (struct ext3_xattr_entry *)( \ >+ (char *)(entry) + EXT3_XATTR_LEN((entry)->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 2003-09-14 14:13:40.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/fs.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/jbd.h 2003-09-14 14:13:53.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 2003-09-14 14:13:40.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/kernel.h 2003-09-14 14:13:53.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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,69 @@ >+/* >+ File: linux/mbcache.h >+ >+ (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org> >+*/ >+ >+/* 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 2003-09-14 14:03:12.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfs3.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfs_fs.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfs_fs_i.h 2003-09-14 14:13:53.000000000 +0200 >@@ -6,6 +6,16 @@ > #include <linux/nfs.h> > > /* >+ * 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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfs_fs_sb.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfs_mount.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfs_xdr.h 2003-09-14 14:13:53.000000000 +0200 >@@ -1,6 +1,8 @@ > #ifndef _LINUX_NFS_XDR_H > #define _LINUX_NFS_XDR_H > >+#include <linux/posix_acl.h> >+ > 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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfsd/export.h 2003-09-14 14:13:53.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 2003-09-14 14:03:11.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfsd/nfsd.h 2003-09-14 14:13:53.000000000 +0200 >@@ -15,6 +15,7 @@ > #include <linux/unistd.h> > #include <linux/dirent.h> > #include <linux/fs.h> >+#include <linux/posix_acl.h> > > #include <linux/nfsd/debug.h> > #include <linux/nfsd/nfsfh.h> >@@ -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 2003-09-14 14:03:12.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/nfsd/xdr3.h 2003-09-14 14:13:53.000000000 +0200 >@@ -10,6 +10,7 @@ > #define _LINUX_NFSD_XDR3_H > > #include <linux/nfsd/xdr.h> >+#include <linux/posix_acl.h> > > 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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,87 @@ >+/* >+ File: linux/posix_acl.h >+ >+ (C) 2002 Andreas Gruenbacher, <a.gruenbacher@computer.org> >+*/ >+ >+ >+#ifndef __LINUX_POSIX_ACL_H >+#define __LINUX_POSIX_ACL_H >+ >+#include <linux/slab.h> >+ >+#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; pa<pe; pa++) >+ >+ >+/* >+ * Duplicate an ACL handle. >+ */ >+static inline struct posix_acl * >+posix_acl_dup(struct posix_acl *acl) >+{ >+ if (acl) >+ atomic_inc(&acl->a_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 2003-09-14 14:13:53.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 <a.gruenbacher@computer.org> >+ */ >+#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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,16 @@ >+/* >+ File: linux/solaris_acl.h >+ >+ (C) 2002 Andreas Gruenbacher, <a.gruenbacher@computer.org> >+*/ >+ >+ >+#ifndef __LINUX_SOLARIS_ACL_H >+#define __LINUX_SOLARIS_ACL_H >+ >+#include <linux/posix_acl.h> >+ >+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 2003-09-14 14:03:12.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/sunrpc/svc.h 2003-09-14 14:13:53.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 2003-09-14 14:03:12.000000000 +0200 >+++ linux-2.4.22-ppc-dev/include/linux/sunrpc/xprt.h 2003-09-14 14:13:53.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 2003-09-14 14:13:53.000000000 +0200 >@@ -0,0 +1,50 @@ >+/* >+ File: linux/xattr_acl.h >+ >+ (extended attribute representation of access control lists) >+ >+ (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org> >+*/ >+ >+#ifndef _LINUX_XATTR_ACL_H >+#define _LINUX_XATTR_ACL_H >+ >+#include <linux/posix_acl.h> >+ >+#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 2003-09-14 14:06:54.000000000 +0200 >+++ linux-2.4.22-ppc-dev/kernel/Makefile 2003-09-14 14:13:53.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 2003-09-14 14:13:40.000000000 +0200 >+++ linux-2.4.22-ppc-dev/kernel/fork.c 2003-09-14 14:13:53.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 2003-09-14 14:13:40.000000000 +0200 >+++ linux-2.4.22-ppc-dev/kernel/ksyms.c 2003-09-14 14:13:53.000000000 +0200 >@@ -32,6 +32,7 @@ > #include <linux/genhd.h> > #include <linux/blkpg.h> > #include <linux/swap.h> >+#include <linux/cache_def.h> > #include <linux/ctype.h> > #include <linux/file.h> > #include <linux/iobuf.h> >@@ -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); >@@ -565,6 +568,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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/lib/Config.in 2003-09-14 14:13:53.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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/lib/Makefile 2003-09-14 14:13:53.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 2003-09-14 14:13:53.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 <linux/module.h> >+# include <linux/slab.h> >+# include <linux/string.h> >+ >+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 2003-09-14 14:03:10.000000000 +0200 >+++ linux-2.4.22-ppc-dev/mm/vmscan.c 2003-09-14 14:13:53.000000000 +0200 >@@ -18,6 +18,7 @@ > #include <linux/kernel_stat.h> > #include <linux/swap.h> > #include <linux/swapctl.h> >+#include <linux/cache_def.h> > #include <linux/smp_lock.h> > #include <linux/pagemap.h> > #include <linux/init.h> >@@ -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 2003-09-14 14:03:25.000000000 +0200 >+++ linux-2.4.22-ppc-dev/net/sunrpc/clnt.c 2003-09-14 14:13:53.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 2003-09-14 14:03:25.000000000 +0200 >+++ linux-2.4.22-ppc-dev/net/sunrpc/svc.c 2003-09-14 14:13:53.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);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 35819
:
22205
|
22206
|
22207
|
22208
|
22209
|
22210
| 22211 |
22212
|
22213
|
22214
|
22215