r1546 - in trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches: . series

Joshua Kwan joshk@haydn.debian.org
Sat, 04 Sep 2004 14:52:49 -0600


Author: joshk
Date: 2004-09-04 14:52:43 -0600 (Sat, 04 Sep 2004)
New Revision: 1546

Added:
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/079_ide_config_deps.diff
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/080_modular_ide-proc.diff
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/081_modular_atiixp.diff
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/082_global_scan_direction.diff
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/083_ide_pci_scan_delay.diff
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/084_ea_acl-2.diff
Modified:
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/022_radeonfb_fixes.diff
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/023_jbd_commit_interval.diff
   trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-6
Log:
more splits and annotations


Modified: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/022_radeonfb_fixes.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/022_radeonfb_fixes.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/022_radeonfb_fixes.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -1,3 +1,9 @@
+# origin: Debian (herbert)
+# cset: n/a
+# inclusion: not submitted
+# description: set owner for radeonfb module, fix red/blue typo
+# revision date: 2004-09-04
+
 diff -urN kernel-source-2.4.26/drivers/video/radeonfb.c kernel-source-2.4.26-1/drivers/video/radeonfb.c
 --- kernel-source-2.4.26/drivers/video/radeonfb.c	2004-04-14 23:05:39.000000000 +1000
 +++ kernel-source-2.4.26-1/drivers/video/radeonfb.c	2004-04-17 14:24:02.000000000 +1000

Modified: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/023_jbd_commit_interval.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/023_jbd_commit_interval.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/023_jbd_commit_interval.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -1,3 +1,9 @@
+# origin: EA/ACL upstream
+# cset: n/a
+# inclusion: not suitable for mainline
+# description: this hunk actually belongs in ea_acl.diff
+# revision date: 2004-09-04
+
 diff -urN kernel-source-2.4.26/fs/jbd/journal.c kernel-source-2.4.26-1/fs/jbd/journal.c
 --- kernel-source-2.4.26/fs/jbd/journal.c	2004-04-14 23:05:40.000000000 +1000
 +++ kernel-source-2.4.26-1/fs/jbd/journal.c	2004-04-11 20:59:07.000000000 +1000

Added: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/079_ide_config_deps.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/079_ide_config_deps.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/079_ide_config_deps.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -0,0 +1,95 @@
+# origin: Debian (herbert)
+# cset: n/a
+# inclusion: not submitted
+# description: Config.in dependency fixes for IDE
+# revision date: 2004-09-04
+
+--- linux-2.4.27/drivers/ide/Config.in	2004-08-08 01:26:04.000000000 +0200
++++ linux-2.4.27+debian/drivers/ide/Config.in	2004-08-12 00:35:30.519655788 +0200
+@@ -27,13 +27,13 @@
+ 
+    comment 'IDE chipset support/bugfixes'
+    if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+-      dep_bool '  CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86
+-      dep_bool '    CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
+-      dep_bool '  ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
++      dep_tristate '  CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_BLK_DEV_IDE $CONFIG_X86
++      dep_mbool '    CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
++      dep_tristate '  ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_BLK_DEV_IDE $CONFIG_ISAPNP
+       if [ "$CONFIG_PCI" = "y" ]; then
+ 	 bool '  PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+ 	 if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
+-	    dep_bool '    Generic PCI IDE Chipset Support' CONFIG_BLK_DEV_GENERIC $CONFIG_BLK_DEV_IDEPCI
++	    dep_tristate '    Generic PCI IDE Chipset Support' CONFIG_BLK_DEV_GENERIC $CONFIG_BLK_DEV_IDE
+ 	    bool '    Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
+ 	    bool '    Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    bool '    Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
+@@ -43,43 +43,43 @@
+ 	    define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    dep_bool '      ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
+ #	    dep_bool '      Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
+-            dep_tristate '    Pacific Digital ADMA-100 basic support' CONFIG_BLK_DEV_ADMA100 $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI
++            dep_tristate '    Pacific Digital ADMA-100 basic support' CONFIG_BLK_DEV_ADMA100 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    dep_mbool    '      ALI M15x3 WDC support (DANGEROUS)' CONFIG_WDC_ALI15X3 $CONFIG_BLK_DEV_ALI15X3
+-	    dep_tristate '    AMD and nVidia IDE support' CONFIG_BLK_DEV_AMD74XX $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    AMD and nVidia IDE support' CONFIG_BLK_DEV_AMD74XX $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    dep_mbool    '      AMD Viper ATA-66 Override' CONFIG_AMD74XX_OVERRIDE $CONFIG_BLK_DEV_AMD74XX
+-	    dep_tristate '    ATI IXP chipset IDE support' CONFIG_BLK_DEV_ATIIXP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
+-	    dep_tristate '    CMD64{3|6|8|9} chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    Compaq Triflex IDE support' CONFIG_BLK_DEV_TRIFLEX $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    CY82C693 chipset support' CONFIG_BLK_DEV_CY82C693 $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI
+-  	    dep_tristate '    HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    ATI IXP chipset IDE support' CONFIG_BLK_DEV_ATIIXP $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
++	    dep_tristate '    CMD64{3|6|8|9} chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    Compaq Triflex IDE support' CONFIG_BLK_DEV_TRIFLEX $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    CY82C693 chipset support' CONFIG_BLK_DEV_CY82C693 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++  	    dep_tristate '    HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    dep_mbool    '      HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA $CONFIG_BLK_DEV_HPT34X $CONFIG_IDEDMA_PCI_WIP
+-	    dep_tristate '    HPT36X/37X chipset support' CONFIG_BLK_DEV_HPT366 $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    HPT36X/37X chipset support' CONFIG_BLK_DEV_HPT366 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    if [ "$CONFIG_MIPS_ITE8172" = "y" -o "$CONFIG_MIPS_IVR" = "y" ]; then
+ 	       dep_mbool '    IT8172 IDE support' CONFIG_BLK_DEV_IT8172 $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    fi
+-	    dep_tristate '    NS87415 chipset support' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL
+-	    dep_tristate '    Promise PDC202{46|62|65|67} support' CONFIG_BLK_DEV_PDC202XX_OLD $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    NS87415 chipset support' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_BLK_DEV_IDE $CONFIG_EXPERIMENTAL
++	    dep_tristate '    Promise PDC202{46|62|65|67} support' CONFIG_BLK_DEV_PDC202XX_OLD $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    dep_mbool     '      Force (U)DMA burst transfers' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX_OLD
+-	    dep_tristate '    Promise PDC202{68|69|70|71|75|76|77} support' CONFIG_BLK_DEV_PDC202XX_NEW $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    Promise PDC202{68|69|70|71|75|76|77} support' CONFIG_BLK_DEV_PDC202XX_NEW $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    if [ "$CONFIG_BLK_DEV_PDC202XX_OLD" = "y" -o "$CONFIG_BLK_DEV_PDC202XX_OLD" = "m" -o "$CONFIG_BLK_DEV_PDC202XX_NEW" = "y" -o "$CONFIG_BLK_DEV_PDC202XX_NEW" = "m" ]; then
+ 	        bool     '    Ignore BIOS port disabled setting on FastTrak' CONFIG_PDC202XX_FORCE
+ 	    fi
+-	    dep_tristate '    RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86
+-	    dep_tristate '    SCx200 chipset support' CONFIG_BLK_DEV_SC1200 $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    ServerWorks OSB4/CSB5/CSB6 chipsets support' CONFIG_BLK_DEV_SVWKS $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_BLK_DEV_IDE $CONFIG_X86
++	    dep_tristate '    SCx200 chipset support' CONFIG_BLK_DEV_SC1200 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    ServerWorks OSB4/CSB5/CSB6 chipsets support' CONFIG_BLK_DEV_SVWKS $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    if [ "$CONFIG_IA64_GENERIC" = "y" -o "$CONFIG_IA64_SGI_SN2" = "y" ] ; then
+-		dep_tristate '    SGI IOC4 chipset support' CONFIG_BLK_DEV_SGIIOC4 $CONFIG_BLK_DEV_IDEDMA_PCI
++		dep_tristate '    SGI IOC4 chipset support' CONFIG_BLK_DEV_SGIIOC4 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    fi
+-	    dep_tristate '    Silicon Image chipset support' CONFIG_BLK_DEV_SIIMAGE $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
+-	    dep_tristate '    SLC90E66 chipset support' CONFIG_BLK_DEV_SLC90E66 $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    Tekram TRM290 chipset support' CONFIG_BLK_DEV_TRM290 $CONFIG_BLK_DEV_IDEDMA_PCI
+-	    dep_tristate '    VIA82CXXX chipset support' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    Silicon Image chipset support' CONFIG_BLK_DEV_SIIMAGE $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
++	    dep_tristate '    SLC90E66 chipset support' CONFIG_BLK_DEV_SLC90E66 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    Tekram TRM290 chipset support' CONFIG_BLK_DEV_TRM290 $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
++	    dep_tristate '    VIA82CXXX chipset support' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_BLK_DEV_IDE $CONFIG_BLK_DEV_IDEDMA_PCI
+ 	    if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
+ 	       dep_tristate '    Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 $CONFIG_BLK_DEV_IDEPCI
+ 	    fi

Added: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/080_modular_ide-proc.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/080_modular_ide-proc.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/080_modular_ide-proc.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -0,0 +1,49 @@
+# origin: Debian (herbert)
+# cset: n/a
+# inclusion: not submitted
+# description: fix crash on ide-core unload
+# revision date: 2004-09-04
+
+--- linux-2.4.27/drivers/ide/ide-proc.c	2004-04-14 15:05:29.000000000 +0200
++++ linux-2.4.27+debian/drivers/ide/ide-proc.c	2004-08-11 23:52:58.000000000 +0200
+@@ -895,6 +895,16 @@
+ EXPORT_SYMBOL(destroy_proc_ide_interfaces);
+ 
+ #ifdef CONFIG_BLK_DEV_IDEPCI
++static inline void create_pci_proc_entry(ide_pci_host_proc_t *p)
++{
++	if (p->name == NULL || p->set != 1 || p->get_info == NULL) 
++		return;
++
++	p->parent = proc_ide_root;
++	create_proc_info_entry(p->name, 0, p->parent, p->get_info);
++	p->set = 2;
++}
++
+ void ide_pci_register_host_proc (ide_pci_host_proc_t *p)
+ {
+ 	ide_pci_host_proc_t *tmp;
+@@ -908,6 +918,9 @@
+ 		tmp->next = p;
+ 	} else
+ 		ide_pci_host_proc_list = p;
++
++	if (proc_ide_root)
++		create_pci_proc_entry(p);
+ }
+ 
+ EXPORT_SYMBOL(ide_pci_register_host_proc);
+@@ -931,12 +944,7 @@
+ #ifdef CONFIG_BLK_DEV_IDEPCI
+ 	while (p != NULL)
+ 	{
+-		if (p->name != NULL && p->set == 1 && p->get_info != NULL) 
+-		{
+-			p->parent = proc_ide_root;
+-			create_proc_info_entry(p->name, 0, p->parent, p->get_info);
+-			p->set = 2;
+-		}
++		create_pci_proc_entry(p);
+ 		p = p->next;
+ 	}
+ #endif /* CONFIG_BLK_DEV_IDEPCI */

Added: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/081_modular_atiixp.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/081_modular_atiixp.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/081_modular_atiixp.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -0,0 +1,30 @@
+# origin: Debian (herbert)
+# cset: n/a
+# inclusion: not submitted
+# description: add exit function for atiixp for modular operation
+# revision date: 2004-09-04
+
+--- linux-2.4.27/drivers/ide/pci/atiixp.c	2004-04-14 15:05:29.000000000 +0200
++++ linux-2.4.27+debian/drivers/ide/pci/atiixp.c	2004-08-11 23:52:58.000000000 +0200
+@@ -494,6 +494,7 @@
+ 	if (dev->device != d->device)
+ 		BUG();
+ 	ide_setup_pci_device(dev, d);
++	MOD_INC_USE_COUNT;
+ 	return 0;
+ }
+ 
+@@ -513,7 +514,13 @@
+ 	return ide_pci_register_driver(&driver);
+ }
+ 
++static void __exit atiixp_ide_exit(void)
++{
++	ide_pci_unregister_driver(&driver);
++}
++
+ module_init(atiixp_ide_init);
++module_exit(atiixp_ide_exit);
+ 
+ MODULE_AUTHOR("HUI YU");
+ MODULE_DESCRIPTION("PCI driver module for ATI IXP IDE");

Added: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/082_global_scan_direction.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/082_global_scan_direction.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/082_global_scan_direction.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -0,0 +1,73 @@
+# origin: Debian (herbert)
+# cset: n/a
+# inclusion: not submitted
+# description: allow setup-pci.c to use ide_scan_direction
+# revision date: 2004-09-04
+
+--- linux-2.4.27/drivers/ide/ide.c	2004-08-08 01:26:04.000000000 +0200
++++ linux-2.4.27+debian/drivers/ide/ide.c	2004-08-11 23:52:58.000000000 +0200
+@@ -172,7 +172,7 @@
+ static int system_bus_speed;	/* holds what we think is VESA/PCI bus speed */
+ static int initializing;	/* set while initializing built-in drivers */
+ 
+-static int ide_scan_direction;	/* THIS was formerly 2.2.x pci=reverse */
++int ide_scan_direction;		/* THIS was formerly 2.2.x pci=reverse */
+ 
+ #ifdef CONFIG_IDEDMA_AUTO
+ int noautodma = 0;
+@@ -2489,9 +2489,9 @@
+  */
+ static void __init probe_for_hwifs (void)
+ {
+ #ifdef CONFIG_BLK_DEV_IDEPCI
+ 	if (pci_present())
+ 	{
+-		ide_scan_pcibus(ide_scan_direction);
++		ide_scan_pcibus();
+ 	}
+ #endif /* CONFIG_BLK_DEV_IDEPCI */
+--- linux-2.4.27/drivers/ide/setup-pci.c	2003-08-25 13:44:41.000000000 +0200
++++ linux-2.4.27+debian/drivers/ide/setup-pci.c	2004-08-11 23:52:58.000000000 +0200
+@@ -793,21, +793,20 @@
+ 
+ /**
+  *	ide_scan_pcibus		-	perform the initial IDE driver scan
+- *	@scan_direction: set for reverse order scanning
+  *
+  *	Perform the initial bus rather than driver ordered scan of the
+  *	PCI drivers. After this all IDE pci handling becomes standard
+  *	module ordering not traditionally ordered.
+  */
+  	
+-void __init ide_scan_pcibus (int scan_direction)
++void __init ide_scan_pcibus(void)
+ {
+ 	struct pci_dev *dev;
+ 	struct pci_driver *d;
+ 	struct list_head *l, *n;
+ 	
+        pre_init = 0;
+-	if (!scan_direction) {
++	if (!ide_scan_direction) {
+ 		pci_for_each_dev(dev) {
+ 			ide_scan_pcidev(dev);
+ 		}
+--- linux-2.4.27/include/linux/ide.h	2004-04-14 15:05:40.000000000 +0200
++++ linux-2.4.27+debian/include/linux/ide.h	2004-08-11 23:52:58.000000000 +0200
+@@ -1254,6 +1254,7 @@
+ 
+ #endif
+ extern int noautodma;
++extern int ide_scan_direction;
+ 
+ /*
+  * We need blk.h, but we replace its end_request by our own version.
+@@ -1614,7 +1615,7 @@
+ extern int idescsi_attach(ide_drive_t *);
+ extern int idescsi_init(void);
+ 
+-extern void ide_scan_pcibus(int scan_direction) __init;
++extern void ide_scan_pcibus(void) __init;
+ extern int ide_pci_register_driver(struct pci_driver *driver);
+ extern void ide_pci_unregister_driver(struct pci_driver *driver);
+ 

Added: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/083_ide_pci_scan_delay.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/083_ide_pci_scan_delay.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/083_ide_pci_scan_delay.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -0,0 +1,56 @@
+# origin: Debian (herbert)
+# cset: n/a
+# inclusion: not submitted
+# description: don't scan PCI bus until ide-probe-mod tells us to
+# revision date: 2004-09-04
+
+--- linux-2.4.27/drivers/ide/ide.c	2004-08-08 01:26:04.000000000 +0200
++++ linux-2.4.27+debian/drivers/ide/ide.c	2004-08-11 23:52:58.000000000 +0200
+@@ -2489,12 +2489,9 @@
+  */
+ static void __init probe_for_hwifs (void)
+ {
+-#ifdef CONFIG_BLK_DEV_IDEPCI
+-	if (pci_present())
+-	{
+-		ide_scan_pcibus();
+-	}
+-#endif /* CONFIG_BLK_DEV_IDEPCI */
++#if defined(CONFIG_BLK_DEV_IDEPCI) && !defined(MODULE)
++	ide_scan_pcibus();
++#endif /* CONFIG_BLK_DEV_IDEPCI && !MODULE */
+ 	ide_scan_drivers();
+ 
+ 	/*
+--- linux-2.4.27/drivers/ide/setup-pci.c	2003-08-25 13:44:41.000000000 +0200
++++ linux-2.4.27+debian/drivers/ide/setup-pci.c	2004-08-11 23:52:58.000000000 +0200
+@@ -793,9 +793,12 @@
+ 	struct pci_dev *dev;
+ 	struct pci_driver *d;
+ 	struct list_head *l, *n;
+ 
++	if (!pci_present() || !pre_init)
++		return;
++
+ 	pre_init = 0;
+ 	if (!ide_scan_direction) {
+ 		pci_for_each_dev(dev) {
+ 			ide_scan_pcidev(dev);
+ 		}
+@@ -829,3 +832,5 @@
+ 		pci_register_driver(d);
+ 	}
+ }
++
++EXPORT_SYMBOL_GPL(ide_scan_pcibus);
+--- linux-2.4.27/include/linux/ide.h	2004-04-14 15:05:40.000000000 +0200
++++ linux-2.4.27+debian/include/linux/ide.h	2004-08-11 23:52:58.000000000 +0200
+@@ -1749,6 +1749,8 @@
+ typedef void (*ide_driver_call)(void);
+ extern void __init ide_register_driver(ide_driver_call);
+ 
++extern void ide_driver_module(int revaldiate);
++
+ /* ide locks for 2.4 */
+ 
+ #define ide_lock		(io_request_lock)

Added: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/084_ea_acl-2.diff
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/084_ea_acl-2.diff	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/084_ea_acl-2.diff	2004-09-04 20:52:43 UTC (rev 1546)
@@ -0,0 +1,9002 @@
+# origin: Debian (kernel-patch-acl)
+# cset: n/a
+# inclusion: not suitable for mainline
+# description: Extended Attributes and Access Control Lists for Linux 2.4
+# revision date: 2004-09-04
+
+--- a/Documentation/Configure.help
++++ b/Documentation/Configure.help
+@@ -16485,6 +16567,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
+@@ -16517,6 +16636,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
+@@ -17423,6 +17579,16 @@
+ 
+   If unsure, say N.
+ 
++POSIX ACL support
++CONFIG_XFS_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 don't know what Access Control Lists are, say N.
++
+ Tracing support (EXPERIMENTAL)
+ CONFIG_XFS_TRACE
+   Say Y here to get an XFS build with activity tracing enabled.
+
+diff -urN kernel-source-2.4.26/Documentation/filesystems/Locking kernel-source-2.4.26-1/Documentation/filesystems/Locking
+--- kernel-source-2.4.26/Documentation/filesystems/Locking	2003-06-14 00:51:29.000000000 +1000
++++ kernel-source-2.4.26-1/Documentation/filesystems/Locking	2004-02-22 20:27:58.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/arch/alpha/defconfig kernel-source-2.4.26-1/arch/alpha/defconfig
+--- kernel-source-2.4.26/arch/alpha/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/alpha/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/alpha/kernel/entry.S kernel-source-2.4.26-1/arch/alpha/kernel/entry.S
+--- kernel-source-2.4.26/arch/alpha/kernel/entry.S	2003-06-14 00:51:29.000000000 +1000
++++ kernel-source-2.4.26-1/arch/alpha/kernel/entry.S	2004-02-22 20:27:59.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/arch/arm/defconfig kernel-source-2.4.26-1/arch/arm/defconfig
+--- kernel-source-2.4.26/arch/arm/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/arm/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/arm/kernel/calls.S kernel-source-2.4.26-1/arch/arm/kernel/calls.S
+--- kernel-source-2.4.26/arch/arm/kernel/calls.S	2003-08-25 21:44:39.000000000 +1000
++++ kernel-source-2.4.26-1/arch/arm/kernel/calls.S	2004-04-17 13:32:29.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/i386/defconfig kernel-source-2.4.26-1/arch/i386/defconfig
+--- kernel-source-2.4.26/arch/i386/defconfig	2004-04-14 23:05:25.000000000 +1000
++++ kernel-source-2.4.26-1/arch/i386/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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_SBUS is not set
+ CONFIG_UID16=y
+
+diff -urN kernel-source-2.4.26/arch/ia64/defconfig kernel-source-2.4.26-1/arch/ia64/defconfig
+--- kernel-source-2.4.26/arch/ia64/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/ia64/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/m68k/defconfig kernel-source-2.4.26-1/arch/m68k/defconfig
+--- kernel-source-2.4.26/arch/m68k/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/m68k/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/mips/defconfig kernel-source-2.4.26-1/arch/mips/defconfig
+--- kernel-source-2.4.26/arch/mips/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/mips/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/mips64/defconfig kernel-source-2.4.26-1/arch/mips64/defconfig
+--- kernel-source-2.4.26/arch/mips64/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/mips64/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/ppc/defconfig kernel-source-2.4.26-1/arch/ppc/defconfig
+--- kernel-source-2.4.26/arch/ppc/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/ppc/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/ppc64/defconfig kernel-source-2.4.26-1/arch/ppc64/defconfig
+--- kernel-source-2.4.26/arch/ppc64/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/ppc64/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/ppc64/kernel/misc.S kernel-source-2.4.26-1/arch/ppc64/kernel/misc.S
+--- kernel-source-2.4.26/arch/ppc64/kernel/misc.S	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/ppc64/kernel/misc.S	2004-02-22 20:28:08.000000000 +1100
+@@ -807,7 +807,6 @@
+ 	.llong .sys_madvise		/* 205 */
+ 	.llong .sys_mincore		/* 206 */
+ 	.llong .sys_gettid		/* 207 */
+-#if 0 /* Reserved syscalls */
+ 	.llong .sys_tkill		/* 208 */
+ 	.llong .sys_setxattr
+ 	.llong .sys_lsetxattr	/* 210 */
+@@ -821,10 +820,7 @@
+ 	.llong .sys_removexattr
+ 	.llong .sys_lremovexattr
+ 	.llong .sys_fremovexattr	/* 220 */
+-	.llong .sys_futex
+-#endif
+-	.llong .sys_perfmonctl   /* Put this here for now ... */
+-	.rept NR_syscalls-222
++	.rept NR_syscalls-221
+ 		.llong .sys_ni_syscall
+ 	.endr
+ #endif
+@@ -1038,7 +1034,6 @@
+ 	.llong .sys_madvise		/* 205 */
+ 	.llong .sys_mincore		/* 206 */
+ 	.llong .sys_gettid		/* 207 */
+-#if 0 /* Reserved syscalls */
+ 	.llong .sys_tkill		/* 208 */
+ 	.llong .sys_setxattr
+ 	.llong .sys_lsetxattr	/* 210 */
+@@ -1052,9 +1047,6 @@
+ 	.llong .sys_removexattr
+ 	.llong .sys_lremovexattr
+ 	.llong .sys_fremovexattr	/* 220 */
+-	.llong .sys_futex
+-#endif
+-	.llong .sys_perfmonctl   /* Put this here for now ... */
+-	.rept NR_syscalls-222
++	.rept NR_syscalls-221
+ 	.llong .sys_ni_syscall
+ 	.endr
+diff -urN kernel-source-2.4.26/arch/s390/defconfig kernel-source-2.4.26-1/arch/s390/defconfig
+--- kernel-source-2.4.26/arch/s390/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/s390/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/s390/kernel/entry.S kernel-source-2.4.26-1/arch/s390/kernel/entry.S
+--- kernel-source-2.4.26/arch/s390/kernel/entry.S	2003-06-14 00:51:32.000000000 +1000
++++ kernel-source-2.4.26-1/arch/s390/kernel/entry.S	2004-04-17 13:32:29.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/s390x/defconfig kernel-source-2.4.26-1/arch/s390x/defconfig
+--- kernel-source-2.4.26/arch/s390x/defconfig	2004-02-19 00:36:30.000000000 +1100
++++ kernel-source-2.4.26-1/arch/s390x/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/s390x/kernel/entry.S kernel-source-2.4.26-1/arch/s390x/kernel/entry.S
+--- kernel-source-2.4.26/arch/s390x/kernel/entry.S	2003-08-25 21:44:40.000000000 +1000
++++ kernel-source-2.4.26-1/arch/s390x/kernel/entry.S	2004-04-17 13:32:29.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/s390x/kernel/wrapper32.S kernel-source-2.4.26-1/arch/s390x/kernel/wrapper32.S
+--- kernel-source-2.4.26/arch/s390x/kernel/wrapper32.S	2003-08-25 21:44:40.000000000 +1000
++++ kernel-source-2.4.26-1/arch/s390x/kernel/wrapper32.S	2004-04-17 13:32:29.000000000 +1000
+@@ -1107,3 +1107,93 @@
+ sys32_sysctl_wrapper:
+ 	llgtr   %r2,%r2                 # struct __sysctl_args32 *
+ 	jg      sys32_sysctl
++
++	.globl	sys32_setxattr_wrapper
++sys32_setxattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	llgtr	%r4,%r4			# void *
++	llgfr	%r5,%r5			# size_t
++	lgfr	%r6,%r6			# int
++	jg	sys_setxattr
++
++	.globl	sys32_lsetxattr_wrapper
++sys32_lsetxattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	llgtr	%r4,%r4			# void *
++	llgfr	%r5,%r5			# size_t
++	lgfr	%r6,%r6			# int
++	jg	sys_lsetxattr
++
++	.globl	sys32_fsetxattr_wrapper
++sys32_fsetxattr_wrapper:
++	lgfr	%r2,%r2			# int
++	llgtr	%r3,%r3			# char *
++	llgtr	%r4,%r4			# void *
++	llgfr	%r5,%r5			# size_t
++	lgfr	%r6,%r6			# int
++	jg	sys_fsetxattr
++
++	.globl	sys32_getxattr_wrapper
++sys32_getxattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	llgtr	%r4,%r4			# void *
++	llgfr	%r5,%r5			# size_t
++	jg	sys_getxattr
++
++	.globl	sys32_lgetxattr_wrapper
++sys32_lgetxattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	llgtr	%r4,%r4			# void *
++	llgfr	%r5,%r5			# size_t
++	jg	sys_lgetxattr
++
++	.globl	sys32_fgetxattr_wrapper
++sys32_fgetxattr_wrapper:
++	lgfr	%r2,%r2			# int
++	llgtr	%r3,%r3			# char *
++	llgtr	%r4,%r4			# void *
++	llgfr	%r5,%r5			# size_t
++	jg	sys_fgetxattr
++
++	.globl	sys32_listxattr_wrapper
++sys32_listxattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	llgfr	%r4,%r4			# size_t
++	jg	sys_listxattr
++
++	.globl	sys32_llistxattr_wrapper
++sys32_llistxattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	llgfr	%r4,%r4			# size_t
++	jg	sys_llistxattr
++
++	.globl	sys32_flistxattr_wrapper
++sys32_flistxattr_wrapper:
++	lgfr	%r2,%r2			# int
++	llgtr	%r3,%r3			# char *
++	llgfr	%r4,%r4			# size_t
++	jg	sys_flistxattr
++
++	.globl	sys32_removexattr_wrapper
++sys32_removexattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	jg	sys_removexattr
++
++	.globl	sys32_lremovexattr_wrapper
++sys32_lremovexattr_wrapper:
++	llgtr	%r2,%r2			# char *
++	llgtr	%r3,%r3			# char *
++	jg	sys_lremovexattr
++
++	.globl	sys32_fremovexattr_wrapper
++sys32_fremovexattr_wrapper:
++	lgfr	%r2,%r2			# int
++	llgtr	%r3,%r3			# char *
++	jg	sys_fremovexattr
+diff -urN kernel-source-2.4.26/arch/sparc/defconfig kernel-source-2.4.26-1/arch/sparc/defconfig
+--- kernel-source-2.4.26/arch/sparc/defconfig	2004-04-14 23:05:27.000000000 +1000
++++ kernel-source-2.4.26-1/arch/sparc/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/arch/sparc64/defconfig kernel-source-2.4.26-1/arch/sparc64/defconfig
+--- kernel-source-2.4.26/arch/sparc64/defconfig	2004-04-14 23:05:27.000000000 +1000
++++ kernel-source-2.4.26-1/arch/sparc64/defconfig	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/Config.in kernel-source-2.4.26-1/fs/Config.in
+--- kernel-source-2.4.26/fs/Config.in	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/Config.in	2004-04-17 13:32:34.000000000 +1000
+@@ -29,6 +29,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
+@@ -92,6 +101,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
+ 
+@@ -103,6 +121,7 @@
+ 
+ tristate 'XFS filesystem support' CONFIG_XFS_FS
+ dep_mbool    '  Quota support' CONFIG_XFS_QUOTA $CONFIG_XFS_FS
++dep_mbool    '  POSIX ACL support' CONFIG_XFS_POSIX_ACL $CONFIG_XFS_FS
+ dep_mbool    '  Realtime support (EXPERIMENTAL)' CONFIG_XFS_RT $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL
+ dep_mbool    '  Tracing support (EXPERIMENTAL)' CONFIG_XFS_TRACE $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL
+ dep_mbool    '  Debugging support (EXPERIMENTAL)' CONFIG_XFS_DEBUG $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL
+@@ -171,6 +190,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 -urN kernel-source-2.4.26/fs/Makefile kernel-source-2.4.26-1/fs/Makefile
+--- kernel-source-2.4.26/fs/Makefile	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/Makefile	2004-04-17 13:32:34.000000000 +1000
+@@ -77,6 +77,10 @@
+ 
+ obj-$(CONFIG_BINFMT_ELF)	+= binfmt_elf.o
+ 
++export-objs += mbcache.o posix_acl.o xattr_acl.o
++obj-$(CONFIG_FS_MBCACHE)	+= mbcache.o
++obj-$(CONFIG_FS_POSIX_ACL)	+= posix_acl.o xattr_acl.o
++
+ # persistent filesystems
+ obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
+ 
+diff -urN kernel-source-2.4.26/fs/befs/linuxvfs.c kernel-source-2.4.26-1/fs/befs/linuxvfs.c
+--- kernel-source-2.4.26/fs/befs/linuxvfs.c	2003-06-14 00:51:37.000000000 +1000
++++ kernel-source-2.4.26-1/fs/befs/linuxvfs.c	2004-04-17 13:32:29.000000000 +1000
+@@ -58,8 +58,8 @@
+ static ssize_t befs_listxattr(struct dentry *dentry, char *buffer, size_t size);
+ static ssize_t befs_getxattr(struct dentry *dentry, const char *name,
+ 			     void *buffer, size_t size);
+-static int befs_setxattr(struct dentry *dentry, const char *name, void *value,
+-			 size_t size, int flags);
++static int befs_setxattr(struct dentry *dentry, const char *name,
++			 const void *value, size_t size, int flags);
+ static int befs_removexattr(struct dentry *dentry, const char *name);
+ 
+ /* slab cache for befs_inode_info objects */
+@@ -693,7 +693,7 @@
+ 
+ static int
+ befs_setxattr(struct dentry *dentry, const char *name,
+-	      void *value, size_t size, int flags)
++	      const void *value, size_t size, int flags)
+ {
+ 	return 0;
+ }
+diff -urN kernel-source-2.4.26/fs/ext2/Makefile kernel-source-2.4.26-1/fs/ext2/Makefile
+--- kernel-source-2.4.26/fs/ext2/Makefile	2001-10-12 01:05:18.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext2/Makefile	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext2/acl.c kernel-source-2.4.26-1/fs/ext2/acl.c
+--- kernel-source-2.4.26/fs/ext2/acl.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext2/acl.c	2004-02-22 20:28:30.000000000 +1100
+@@ -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;
++	int 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 -urN kernel-source-2.4.26/fs/ext2/file.c kernel-source-2.4.26-1/fs/ext2/file.c
+--- kernel-source-2.4.26/fs/ext2/file.c	2001-10-12 01:05:18.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext2/file.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext2/ialloc.c kernel-source-2.4.26-1/fs/ext2/ialloc.c
+--- kernel-source-2.4.26/fs/ext2/ialloc.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext2/ialloc.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext2/inode.c kernel-source-2.4.26-1/fs/ext2/inode.c
+--- kernel-source-2.4.26/fs/ext2/inode.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext2/inode.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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>
+@@ -35,6 +36,8 @@
+ MODULE_DESCRIPTION("Second Extended Filesystem");
+ MODULE_LICENSE("GPL");
+ 
++static int ext2_update_inode(struct inode * inode, int do_sync);
++
+ /*
+  * Test whether an inode is a fast symlink.
+  */
+@@ -47,8 +50,6 @@
+ 		inode->i_blocks - ea_blocks == 0);
+ }
+ 
+-static int ext2_update_inode(struct inode * inode, int do_sync);
+-
+ /*
+  * Called at each iput()
+  */
+@@ -64,9 +65,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);
+@@ -916,8 +915,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",
+@@ -1003,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;
+@@ -1018,15 +1013,29 @@
+ 		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:
+@@ -1177,3 +1186,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 -urN kernel-source-2.4.26/fs/ext2/namei.c kernel-source-2.4.26-1/fs/ext2/namei.c
+--- kernel-source-2.4.26/fs/ext2/namei.c	2001-10-04 15:57:36.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext2/namei.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext2/super.c kernel-source-2.4.26-1/fs/ext2/super.c
+--- kernel-source-2.4.26/fs/ext2/super.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext2/super.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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")) {
+@@ -446,8 +485,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;
+ 	}
+ 
+@@ -737,17 +782,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;
+@@ -838,12 +885,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 -urN kernel-source-2.4.26/fs/ext2/symlink.c kernel-source-2.4.26-1/fs/ext2/symlink.c
+--- kernel-source-2.4.26/fs/ext2/symlink.c	2000-09-28 07:41:33.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext2/symlink.c	2004-04-17 13:32:29.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext2/xattr.c kernel-source-2.4.26-1/fs/ext2/xattr.c
+--- kernel-source-2.4.26/fs/ext2/xattr.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext2/xattr.c	2004-02-22 19:32:21.000000000 +1100
+@@ -0,0 +1,1166 @@
++/*
++ * 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 *)here - bh->b_data;
++			here = ENTRY((char *)header + offset);
++			offset = (char *)last - bh->b_data;
++			last = ENTRY((char *)header + offset);
++		}
++	} else {
++		/* Allocate a buffer where we construct the new block. */
++		header = kmalloc(sb->s_blocksize, GFP_KERNEL);
++		error = -ENOMEM;
++		if (header == NULL)
++			goto cleanup;
++		memset(header, 0, sb->s_blocksize);
++		end = (char *)header + sb->s_blocksize;
++		header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC);
++		header->h_blocks = header->h_refcount = cpu_to_le32(1);
++		last = here = ENTRY(header+1);
++	}
++
++	/* Iff we are modifying the block in-place, bh is locked here. */
++
++	if (not_found) {
++		/* Insert the new name. */
++		int size = EXT2_XATTR_LEN(name_len);
++		int rest = (char *)last - (char *)here;
++		memmove((char *)here + size, here, rest);
++		memset(here, 0, size);
++		here->e_name_index = name_index;
++		here->e_name_len = name_len;
++		memcpy(here->e_name, name, name_len);
++	} else {
++		if (!here->e_value_block && here->e_value_size) {
++			char *first_val = (char *)header + min_offs;
++			int offs = le16_to_cpu(here->e_value_offs);
++			char *val = (char *)header + offs;
++			size_t size = EXT2_XATTR_SIZE(
++				le32_to_cpu(here->e_value_size));
++
++			if (size == EXT2_XATTR_SIZE(value_len)) {
++				/* The old and the new value have the same
++				   size. Just replace. */
++				here->e_value_size = cpu_to_le32(value_len);
++				memset(val + size - EXT2_XATTR_PAD, 0,
++				       EXT2_XATTR_PAD); /* Clear pad bytes. */
++				memcpy(val, value, value_len);
++				goto skip_replace;
++			}
++
++			/* Remove the old value. */
++			memmove(first_val + size, first_val, val - first_val);
++			memset(first_val, 0, size);
++			here->e_value_offs = 0;
++			min_offs += size;
++
++			/* Adjust all value offsets. */
++			last = ENTRY(header+1);
++			while (!IS_LAST_ENTRY(last)) {
++				int o = le16_to_cpu(last->e_value_offs);
++				if (!last->e_value_block && o < offs)
++					last->e_value_offs =
++						cpu_to_le16(o + size);
++				last = EXT2_XATTR_NEXT(last);
++			}
++		}
++		if (value == NULL) {
++			/* Remove the old name. */
++			int size = EXT2_XATTR_LEN(name_len);
++			last = ENTRY((char *)last - size);
++			memmove(here, (char*)here + size,
++				(char*)last - (char*)here);
++			memset(last, 0, size);
++		}
++	}
++
++	if (value != NULL) {
++		/* Insert the new value. */
++		here->e_value_size = cpu_to_le32(value_len);
++		if (value_len) {
++			size_t size = EXT2_XATTR_SIZE(value_len);
++			char *val = (char *)header + min_offs - size;
++			here->e_value_offs =
++				cpu_to_le16((char *)val - (char *)header);
++			memset(val + size - EXT2_XATTR_PAD, 0,
++			       EXT2_XATTR_PAD); /* Clear the pad bytes. */
++			memcpy(val, value, value_len);
++		}
++	}
++
++skip_replace:
++	if (IS_LAST_ENTRY(ENTRY(header+1))) {
++		/* 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. */
++			if (new_bh == old_bh)
++				ea_bdebug(new_bh, "keeping this block");
++			else {
++				/* The old block is released after updating
++				   the inode.  */
++				ea_bdebug(new_bh, "reusing block");
++				
++				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(1 +
++					le32_to_cpu(HDR(new_bh)->h_refcount));
++				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 -urN kernel-source-2.4.26/fs/ext2/xattr_trusted.c kernel-source-2.4.26-1/fs/ext2/xattr_trusted.c
+--- kernel-source-2.4.26/fs/ext2/xattr_trusted.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext2/xattr_trusted.c	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/ext2/xattr_user.c kernel-source-2.4.26-1/fs/ext2/xattr_user.c
+--- kernel-source-2.4.26/fs/ext2/xattr_user.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext2/xattr_user.c	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/ext3/Makefile kernel-source-2.4.26-1/fs/ext3/Makefile
+--- kernel-source-2.4.26/fs/ext3/Makefile	2001-12-22 04:41:55.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext3/Makefile	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext3/acl.c kernel-source-2.4.26-1/fs/ext3/acl.c
+--- kernel-source-2.4.26/fs/ext3/acl.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext3/acl.c	2004-02-22 20:28:30.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/ext3/file.c kernel-source-2.4.26-1/fs/ext3/file.c
+--- kernel-source-2.4.26/fs/ext3/file.c	2003-08-25 21:44:43.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext3/file.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext3/ialloc.c kernel-source-2.4.26-1/fs/ext3/ialloc.c
+--- kernel-source-2.4.26/fs/ext3/ialloc.c	2003-06-14 00:51:37.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext3/ialloc.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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_EXT3_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 -urN kernel-source-2.4.26/fs/ext3/inode.c kernel-source-2.4.26-1/fs/ext3/inode.c
+--- kernel-source-2.4.26/fs/ext3/inode.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext3/inode.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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>
+@@ -60,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)
+ {
+@@ -191,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();
+@@ -2031,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(
+@@ -2173,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;
+@@ -2187,14 +2182,28 @@
+ 		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:
+@@ -2379,10 +2388,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.  
+  */
+ 
+@@ -2403,7 +2408,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);
+@@ -2425,9 +2431,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 -urN kernel-source-2.4.26/fs/ext3/namei.c kernel-source-2.4.26-1/fs/ext3/namei.c
+--- kernel-source-2.4.26/fs/ext3/namei.c	2003-06-14 00:51:37.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext3/namei.c	2004-02-22 20:28:30.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/ext3/super.c kernel-source-2.4.26-1/fs/ext3/super.c
+--- kernel-source-2.4.26/fs/ext3/super.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext3/super.c	2004-04-17 14:24:03.000000000 +1000
+@@ -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);
+@@ -450,6 +453,26 @@
+ 
+ static struct dquot_operations ext3_qops;
+ 
++#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 */
+@@ -463,6 +486,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)
+@@ -500,10 +524,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;
+@@ -516,6 +542,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")) {
+@@ -951,7 +991,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;
+ 	}
+@@ -1672,19 +1722,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);
+@@ -1841,16 +1893,45 @@
+ 
+ static int __init init_ext3_fs(void)
+ {
++	int error;
++
+ #ifdef CONFIG_QUOTA
+ 	init_dquot_operations(&ext3_qops);
+ 	old_write_dquot = ext3_qops.write_dquot;
+ 	ext3_qops.write_dquot = ext3_write_dquot;
+ #endif
+-        return register_filesystem(&ext3_fs_type);
++	error = init_ext3_xattr();
++	if (error)
++		return error;
++	error = init_ext3_xattr_user();
++	if (error)
++		goto fail;
++	error = init_ext3_xattr_trusted();
++	if (error)
++		goto fail2;
++	error = init_ext3_acl();
++	if (error)
++		goto fail3;
++	error = register_filesystem(&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 -urN kernel-source-2.4.26/fs/ext3/symlink.c kernel-source-2.4.26-1/fs/ext3/symlink.c
+--- kernel-source-2.4.26/fs/ext3/symlink.c	2001-11-10 09:25:04.000000000 +1100
++++ kernel-source-2.4.26-1/fs/ext3/symlink.c	2004-04-17 13:32:29.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/ext3/xattr.c kernel-source-2.4.26-1/fs/ext3/xattr.c
+--- kernel-source-2.4.26/fs/ext3/xattr.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext3/xattr.c	2004-02-22 19:32:21.000000000 +1100
+@@ -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 *)here - bh->b_data;
++			here = ENTRY((char *)header + offset);
++			offset = (char *)last - bh->b_data;
++			last = ENTRY((char *)header + offset);
++		}
++	} else {
++		/* Allocate a buffer where we construct the new block. */
++		header = kmalloc(sb->s_blocksize, GFP_KERNEL);
++		error = -ENOMEM;
++		if (header == NULL)
++			goto cleanup;
++		memset(header, 0, sb->s_blocksize);
++		end = (char *)header + sb->s_blocksize;
++		header->h_magic = cpu_to_le32(EXT3_XATTR_MAGIC);
++		header->h_blocks = header->h_refcount = cpu_to_le32(1);
++		last = here = ENTRY(header+1);
++	}
++
++	/* Iff we are modifying the block in-place, bh is locked here. */
++
++	if (not_found) {
++		/* Insert the new name. */
++		int size = EXT3_XATTR_LEN(name_len);
++		int rest = (char *)last - (char *)here;
++		memmove((char *)here + size, here, rest);
++		memset(here, 0, size);
++		here->e_name_index = name_index;
++		here->e_name_len = name_len;
++		memcpy(here->e_name, name, name_len);
++	} else {
++		if (!here->e_value_block && here->e_value_size) {
++			char *first_val = (char *)header + min_offs;
++			int offs = le16_to_cpu(here->e_value_offs);
++			char *val = (char *)header + offs;
++			size_t size = EXT3_XATTR_SIZE(
++				le32_to_cpu(here->e_value_size));
++
++			if (size == EXT3_XATTR_SIZE(value_len)) {
++				/* The old and the new value have the same
++				   size. Just replace. */
++				here->e_value_size = cpu_to_le32(value_len);
++				memset(val + size - EXT3_XATTR_PAD, 0,
++				       EXT3_XATTR_PAD); /* Clear pad bytes. */
++				memcpy(val, value, value_len);
++				goto skip_replace;
++			}
++
++			/* Remove the old value. */
++			memmove(first_val + size, first_val, val - first_val);
++			memset(first_val, 0, size);
++			here->e_value_offs = 0;
++			min_offs += size;
++
++			/* Adjust all value offsets. */
++			last = ENTRY(header+1);
++			while (!IS_LAST_ENTRY(last)) {
++				int o = le16_to_cpu(last->e_value_offs);
++				if (!last->e_value_block && o < offs)
++					last->e_value_offs =
++						cpu_to_le16(o + size);
++				last = EXT3_XATTR_NEXT(last);
++			}
++		}
++		if (value == NULL) {
++			/* Remove the old name. */
++			int size = EXT3_XATTR_LEN(name_len);
++			last = ENTRY((char *)last - size);
++			memmove(here, (char*)here + size,
++				(char*)last - (char*)here);
++			memset(last, 0, size);
++		}
++	}
++
++	if (value != NULL) {
++		/* Insert the new value. */
++		here->e_value_size = cpu_to_le32(value_len);
++		if (value_len) {
++			size_t size = EXT3_XATTR_SIZE(value_len);
++			char *val = (char *)header + min_offs - size;
++			here->e_value_offs =
++				cpu_to_le16((char *)val - (char *)header);
++			memset(val + size - EXT3_XATTR_PAD, 0,
++			       EXT3_XATTR_PAD); /* Clear the pad bytes. */
++			memcpy(val, value, value_len);
++		}
++	}
++
++skip_replace:
++	if (IS_LAST_ENTRY(ENTRY(header+1))) {
++		/* 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. */
++			if (new_bh == old_bh)
++				ea_bdebug(new_bh, "keeping this block");
++			else {
++				/* The old block is released after updating
++				   the inode. */
++				ea_bdebug(new_bh, "reusing block");
++			
++				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(1 +
++					le32_to_cpu(HDR(new_bh)->h_refcount));
++				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 if (ext3_journal_get_write_access(handle, bh) == 0) {
++			/* ext3_journal_get_write_access() requires an unlocked
++			   bh, which complicates things here. */
++			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 -urN kernel-source-2.4.26/fs/ext3/xattr_trusted.c kernel-source-2.4.26-1/fs/ext3/xattr_trusted.c
+--- kernel-source-2.4.26/fs/ext3/xattr_trusted.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext3/xattr_trusted.c	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/ext3/xattr_user.c kernel-source-2.4.26-1/fs/ext3/xattr_user.c
+--- kernel-source-2.4.26/fs/ext3/xattr_user.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/ext3/xattr_user.c	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/intermezzo/vfs.c kernel-source-2.4.26-1/fs/intermezzo/vfs.c
+--- kernel-source-2.4.26/fs/intermezzo/vfs.c	2003-06-14 00:51:37.000000000 +1000
++++ kernel-source-2.4.26-1/fs/intermezzo/vfs.c	2004-04-17 13:32:29.000000000 +1000
+@@ -74,7 +74,7 @@
+ #ifdef CONFIG_FS_EXT_ATTR
+ # include <linux/ext_attr.h>
+ 
+-# ifdef CONFIG_FS_POSIX_ACL
++#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL
+ #  include <linux/posix_acl.h>
+ # endif
+ #endif
+@@ -466,7 +466,7 @@
+         struct dentry *dentry;
+         struct presto_file_set *fset;
+         int error;
+-#ifdef  CONFIG_FS_POSIX_ACL
++#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL
+         int (*set_posix_acl)(struct inode *, int type, posix_acl_t *)=NULL;
+ #endif
+ 
+@@ -507,7 +507,7 @@
+                                  (dentry->d_inode->i_mode & ~S_IALLUGO);
+                 CDEBUG(D_PIOCTL, "chmod: orig %#o, set %#o, result %#o\n",
+                        dentry->d_inode->i_mode, set_mode, iattr->ia_mode);
+-#ifdef CONFIG_FS_POSIX_ACL
++#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL
+                 /* ACl code interacts badly with setattr 
+                  * since it tries to modify the ACL using 
+                  * set_ext_attr which recurses back into presto.  
+@@ -535,7 +535,7 @@
+                 }
+         }
+ 
+-#ifdef CONFIG_FS_POSIX_ACL
++#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL
+         /* restore the inode_operations if we changed them*/
+         if (iattr->ia_valid & ATTR_MODE) 
+                 dentry->d_inode->i_op->set_posix_acl=set_posix_acl;
+@@ -2271,7 +2271,7 @@
+ 
+ #ifdef CONFIG_FS_EXT_ATTR
+ 
+-#ifdef CONFIG_FS_POSIX_ACL
++#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL
+ /* Posix ACL code changes i_mode without using a notify_change (or
+  * a mark_inode_dirty!). We need to duplicate this at the reintegrator
+  * which is done by this function. This function also takes care of 
+@@ -2414,7 +2414,7 @@
+                 goto exit;
+         }
+ 
+-#ifdef CONFIG_FS_POSIX_ACL
++#ifdef CONFIG_INTERMEZZO_FS_POSIX_ACL
+         /* Reset mode if specified*/
+         /* XXX: when we do native acl support, move this code out! */
+         if (mode != NULL) {
+
+diff -urN kernel-source-2.4.26/fs/jfs/jfs_xattr.h kernel-source-2.4.26-1/fs/jfs/jfs_xattr.h
+--- kernel-source-2.4.26/fs/jfs/jfs_xattr.h	2002-11-29 10:53:15.000000000 +1100
++++ kernel-source-2.4.26-1/fs/jfs/jfs_xattr.h	2004-04-17 13:32:29.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/jfs/xattr.c kernel-source-2.4.26-1/fs/jfs/xattr.c
+--- kernel-source-2.4.26/fs/jfs/xattr.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/jfs/xattr.c	2004-04-17 13:32:29.000000000 +1000
+@@ -648,7 +648,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;
+@@ -667,7 +667,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;
+@@ -806,7 +806,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 */
+@@ -889,12 +889,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;
+@@ -938,6 +944,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 -urN kernel-source-2.4.26/fs/mbcache.c kernel-source-2.4.26-1/fs/mbcache.c
+--- kernel-source-2.4.26/fs/mbcache.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/mbcache.c	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/namei.c kernel-source-2.4.26-1/fs/namei.c
+--- kernel-source-2.4.26/fs/namei.c	2003-08-25 21:44:43.000000000 +1000
++++ kernel-source-2.4.26-1/fs/namei.c	2004-04-17 13:32:34.000000000 +1000
+@@ -1048,8 +1048,9 @@
+ 
+ 	/* Negative dentry, just create the file */
+ 	if (!dentry->d_inode) {
+-		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);
+ 		up(&dir->d_inode->i_sem);
+ 		dput(nd->dentry);
+ 		nd->dentry = dentry;
+@@ -1280,7 +1281,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)) {
+ 		switch (mode & S_IFMT) {
+ 		case 0: case S_IFREG:
+@@ -1348,8 +1350,9 @@
+ 		dentry = lookup_create(&nd, 1);
+ 		error = PTR_ERR(dentry);
+ 		if (!IS_ERR(dentry)) {
+-			error = vfs_mkdir(nd.dentry->d_inode, dentry,
+-					  mode & ~current->fs->umask);
++			if (!IS_POSIXACL(nd.dentry->d_inode))
++				mode &= ~current->fs->umask;
++			error = vfs_mkdir(nd.dentry->d_inode, dentry, mode);
+ 			dput(dentry);
+ 		}
+ 		up(&nd.dentry->d_inode->i_sem);
+diff -urN kernel-source-2.4.26/fs/nfs/dir.c kernel-source-2.4.26-1/fs/nfs/dir.c
+--- kernel-source-2.4.26/fs/nfs/dir.c	2004-04-14 23:05:40.000000000 +1000
++++ kernel-source-2.4.26-1/fs/nfs/dir.c	2004-04-17 14:24:03.000000000 +1000
+@@ -34,6 +34,7 @@
+ #define NFS_PARANOIA 1
+ /* #define NFS_DEBUG_VERBOSE 1 */
+ 
++static loff_t nfs_dir_llseek(struct file *, loff_t, int);
+ static int nfs_readdir(struct file *, void *, filldir_t);
+ static struct dentry *nfs_lookup(struct inode *, struct dentry *);
+ static int nfs_create(struct inode *, struct dentry *, int);
+@@ -48,6 +49,7 @@
+ static int nfs_fsync_dir(struct file *, struct dentry *, int);
+ 
+ struct file_operations nfs_dir_operations = {
++	llseek:		nfs_dir_llseek,
+ 	read:		generic_read_dir,
+ 	readdir:	nfs_readdir,
+ 	open:		nfs_open,
+@@ -70,6 +72,25 @@
+ 	setattr:	nfs_notify_change,
+ };
+ 
++static loff_t nfs_dir_llseek(struct file *file, loff_t offset, int origin)
++{
++	switch (origin) {
++		case 1:
++			if (offset == 0) {
++				offset = file->f_pos;
++				break;
++			}
++		case 2:
++			return -EINVAL;
++	}
++	if (offset != file->f_pos) {
++		file->f_pos = offset;
++		file->f_reada = 0;
++		file->f_version = ++event;
++	}
++	return (offset <= 0) ? 0 : offset;
++}
++
+ typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
+ typedef struct {
+ 	struct file	*file;
+@@ -1090,7 +1111,8 @@
+ {
+ 	int			error = vfs_permission(inode, mask);
+ 
+-	if (!NFS_PROTO(inode)->access)
++	if ((NFS_SERVER(inode)->flags & NFS_MOUNT_NOACL) ||
++	    !NFS_PROTO(inode)->access)
+ 		goto out;
+ 
+ 	if (error == -EROFS)
+diff -urN kernel-source-2.4.26/fs/nfs/inode.c kernel-source-2.4.26-1/fs/nfs/inode.c
+--- kernel-source-2.4.26/fs/nfs/inode.c	2004-04-14 23:05:40.000000000 +1000
++++ kernel-source-2.4.26-1/fs/nfs/inode.c	2004-04-17 13:32:34.000000000 +1000
+@@ -568,6 +568,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;
+
+diff -urN kernel-source-2.4.26/fs/nfs/nfs3proc.c kernel-source-2.4.26-1/fs/nfs/nfs3proc.c
+--- kernel-source-2.4.26/fs/nfs/nfs3proc.c	2003-11-29 05:26:21.000000000 +1100
++++ kernel-source-2.4.26-1/fs/nfs/nfs3proc.c	2003-11-29 20:53:46.000000000 +1100
+@@ -45,6 +45,7 @@
+ 
+ #define rpc_call(clnt, proc, argp, resp, flags) \
+ 		nfs3_rpc_call_wrapper(clnt, proc, argp, resp, flags)
++#undef rpc_call_sync
+ #define rpc_call_sync(clnt, msg, flags) \
+ 		nfs3_rpc_wrapper(clnt, msg, flags)
+ 
+diff -urN kernel-source-2.4.26/fs/nfs/nfs3xdr.c kernel-source-2.4.26-1/fs/nfs/nfs3xdr.c
+--- kernel-source-2.4.26/fs/nfs/nfs3xdr.c	2003-11-29 05:26:21.000000000 +1100
++++ kernel-source-2.4.26-1/fs/nfs/nfs3xdr.c	2003-11-29 20:53:46.000000000 +1100
+@@ -465,6 +465,13 @@
+ 	return 0;
+ }
+ 
++/* Hack to sign-extending 32-bit cookies */
++static inline
++u64 nfs_transform_cookie64(u64 cookie)
++{
++	return (cookie & 0x80000000) ? (cookie ^ 0xFFFFFFFF00000000ULL) : cookie;
++}
++
+ /*
+  * Encode arguments to readdir call
+  */
+@@ -476,7 +483,7 @@
+ 	u32 count = args->count;
+ 
+ 	p = xdr_encode_fhandle(p, args->fh);
+-	p = xdr_encode_hyper(p, args->cookie);
++	p = xdr_encode_hyper(p, nfs_transform_cookie64(args->cookie));
+ 	*p++ = args->verf[0];
+ 	*p++ = args->verf[1];
+ 	if (args->plus) {
+@@ -599,6 +606,8 @@
+ u32 *
+ nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+ {
++	u64 cookie;
++
+ 	if (!*p++) {
+ 		if (!*p)
+ 			return ERR_PTR(-EAGAIN);
+@@ -611,7 +620,8 @@
+ 	entry->name = (const char *) p;
+ 	p += XDR_QUADLEN(entry->len);
+ 	entry->prev_cookie = entry->cookie;
+-	p = xdr_decode_hyper(p, &entry->cookie);
++	p = xdr_decode_hyper(p, &cookie);
++	entry->cookie = nfs_transform_cookie64(cookie);
+ 
+ 	if (plus) {
+ 		struct nfs_fattr fattr;
+diff -urN kernel-source-2.4.26/fs/nfsd/export.c kernel-source-2.4.26-1/fs/nfsd/export.c
+--- kernel-source-2.4.26/fs/nfsd/export.c	2003-11-29 05:26:21.000000000 +1100
++++ kernel-source-2.4.26-1/fs/nfsd/export.c	2004-04-17 13:32:34.000000000 +1000
+@@ -643,6 +643,7 @@
+ 	{ NFSEXP_NOHIDE, {"nohide", ""}},
+ 	{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
+ 	{ NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
++	{ NFSEXP_NOACL, {"no_acl", ""}},
+ #ifdef MSNFS
+ 	{ NFSEXP_MSNFS, {"msnfs", ""}},
+ #endif
+diff -urN kernel-source-2.4.26/fs/nfsd/nfs3xdr.c kernel-source-2.4.26-1/fs/nfsd/nfs3xdr.c
+--- kernel-source-2.4.26/fs/nfsd/nfs3xdr.c	2003-06-14 00:51:37.000000000 +1000
++++ kernel-source-2.4.26-1/fs/nfsd/nfs3xdr.c	2004-04-17 13:32:34.000000000 +1000
+@@ -157,9 +157,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_NOACL(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));
+diff -urN kernel-source-2.4.26/fs/nfsd/nfssvc.c kernel-source-2.4.26-1/fs/nfsd/nfssvc.c
+--- kernel-source-2.4.26/fs/nfsd/nfssvc.c	2002-11-29 10:53:15.000000000 +1100
++++ kernel-source-2.4.26-1/fs/nfsd/nfssvc.c	2004-04-17 13:32:34.000000000 +1000
+@@ -155,6 +155,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 +166,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 +260,7 @@
+ 	list_del(&me.list);
+ 	nfsdstats.th_cnt --;
+ 
++out:
+ 	/* Release the thread */
+ 	svc_exit_thread(rqstp);
+ 
+diff -urN kernel-source-2.4.26/fs/nfsd/nfsxdr.c kernel-source-2.4.26-1/fs/nfsd/nfsxdr.c
+--- kernel-source-2.4.26/fs/nfsd/nfsxdr.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/nfsd/nfsxdr.c	2004-04-17 13:32:34.000000000 +1000
+@@ -146,10 +146,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_NOACL(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 -urN kernel-source-2.4.26/fs/nfsd/vfs.c kernel-source-2.4.26-1/fs/nfsd/vfs.c
+--- kernel-source-2.4.26/fs/nfsd/vfs.c	2003-11-29 05:26:21.000000000 +1100
++++ kernel-source-2.4.26-1/fs/nfsd/vfs.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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,99 @@
+ 	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)
++		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 */
++	error = inode->i_op->setxattr(fhp->fh_dentry, name, value, size, 0);
++	unlock_kernel();  /* goes away in 2.5 */
++
++getout:
++	kfree(value);
++	return error;
++}
++#endif
+diff -urN kernel-source-2.4.26/fs/posix_acl.c kernel-source-2.4.26-1/fs/posix_acl.c
+--- kernel-source-2.4.26/fs/posix_acl.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/posix_acl.c	2004-02-22 19:42:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/fs/xattr.c kernel-source-2.4.26-1/fs/xattr.c
+--- kernel-source-2.4.26/fs/xattr.c	2002-11-29 10:53:15.000000000 +1100
++++ kernel-source-2.4.26-1/fs/xattr.c	2004-04-17 13:32:30.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/xattr_acl.c kernel-source-2.4.26-1/fs/xattr_acl.c
+--- kernel-source-2.4.26/fs/xattr_acl.c	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/fs/xattr_acl.c	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/include/asm-arm/unistd.h kernel-source-2.4.26-1/include/asm-arm/unistd.h
+--- kernel-source-2.4.26/include/asm-arm/unistd.h	2003-08-25 21:44:43.000000000 +1000
++++ kernel-source-2.4.26-1/include/asm-arm/unistd.h	2004-04-17 13:32:30.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/include/asm-s390/unistd.h kernel-source-2.4.26-1/include/asm-s390/unistd.h
+--- kernel-source-2.4.26/include/asm-s390/unistd.h	2003-06-14 00:51:38.000000000 +1000
++++ kernel-source-2.4.26-1/include/asm-s390/unistd.h	2004-04-17 13:32:30.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/include/asm-s390x/unistd.h kernel-source-2.4.26-1/include/asm-s390x/unistd.h
+--- kernel-source-2.4.26/include/asm-s390x/unistd.h	2003-06-14 00:51:38.000000000 +1000
++++ kernel-source-2.4.26-1/include/asm-s390x/unistd.h	2004-04-17 13:32:30.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/include/linux/cache_def.h kernel-source-2.4.26-1/include/linux/cache_def.h
+--- kernel-source-2.4.26/include/linux/cache_def.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/cache_def.h	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/ext2_acl.h kernel-source-2.4.26-1/include/linux/ext2_acl.h
+--- kernel-source-2.4.26/include/linux/ext2_acl.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/ext2_acl.h	2004-02-22 19:42:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/ext2_fs.h kernel-source-2.4.26-1/include/linux/ext2_fs.h
+--- kernel-source-2.4.26/include/linux/ext2_fs.h	2004-02-19 00:36:32.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/ext2_fs.h	2004-04-17 13:32:34.000000000 +1000
+@@ -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
+@@ -410,6 +386,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
+@@ -480,7 +457,7 @@
+ #define EXT2_FEATURE_INCOMPAT_META_BG		0x0010
+ #define EXT2_FEATURE_INCOMPAT_ANY		0xffffffff
+ 
+-#define EXT2_FEATURE_COMPAT_SUPP	0
++#define EXT2_FEATURE_COMPAT_SUPP	EXT2_FEATURE_COMPAT_EXT_ATTR
+ #define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE| \
+ 					 EXT2_FEATURE_INCOMPAT_META_BG)
+ #define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+@@ -599,7 +576,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 *);
+@@ -614,6 +591,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,
+@@ -650,8 +628,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 -urN kernel-source-2.4.26/include/linux/ext2_fs_i.h kernel-source-2.4.26-1/include/linux/ext2_fs_i.h
+--- kernel-source-2.4.26/include/linux/ext2_fs_i.h	2004-02-19 00:36:32.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/ext2_fs_i.h	2004-04-17 13:32:34.000000000 +1000
+@@ -35,6 +35,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
+ };
+ 
+ /*
+diff -urN kernel-source-2.4.26/include/linux/ext2_xattr.h kernel-source-2.4.26-1/include/linux/ext2_xattr.h
+--- kernel-source-2.4.26/include/linux/ext2_xattr.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/ext2_xattr.h	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/ext3_acl.h kernel-source-2.4.26-1/include/linux/ext3_acl.h
+--- kernel-source-2.4.26/include/linux/ext3_acl.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/ext3_acl.h	2004-02-22 19:42:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/ext3_fs.h kernel-source-2.4.26-1/include/linux/ext3_fs.h
+--- kernel-source-2.4.26/include/linux/ext3_fs.h	2004-02-19 00:36:32.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/ext3_fs.h	2004-04-17 13:32:34.000000000 +1000
+@@ -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
+@@ -519,7 +496,7 @@
+ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x0008 /* Journal device */
+ #define EXT3_FEATURE_INCOMPAT_META_BG		0x0010
+ 
+-#define EXT3_FEATURE_COMPAT_SUPP	0
++#define EXT3_FEATURE_COMPAT_SUPP	EXT2_FEATURE_COMPAT_EXT_ATTR
+ #define EXT3_FEATURE_INCOMPAT_SUPP	(EXT3_FEATURE_INCOMPAT_FILETYPE| \
+ 					 EXT3_FEATURE_INCOMPAT_RECOVER| \
+ 					 EXT3_FEATURE_INCOMPAT_META_BG)
+@@ -638,7 +615,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 *);
+@@ -646,6 +623,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 *);
+ 
+@@ -713,8 +691,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 -urN kernel-source-2.4.26/include/linux/ext3_fs_i.h kernel-source-2.4.26-1/include/linux/ext3_fs_i.h
+--- kernel-source-2.4.26/include/linux/ext3_fs_i.h	2001-11-23 06:46:19.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/ext3_fs_i.h	2004-04-17 13:32:34.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/include/linux/ext3_jbd.h kernel-source-2.4.26-1/include/linux/ext3_jbd.h
+--- kernel-source-2.4.26/include/linux/ext3_jbd.h	2003-06-14 00:51:38.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/ext3_jbd.h	2004-04-17 13:32:30.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/include/linux/ext3_xattr.h kernel-source-2.4.26-1/include/linux/ext3_xattr.h
+--- kernel-source-2.4.26/include/linux/ext3_xattr.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/ext3_xattr.h	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/fs.h kernel-source-2.4.26-1/include/linux/fs.h
+--- kernel-source-2.4.26/include/linux/fs.h	2004-02-19 00:36:32.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/fs.h	2004-02-22 20:28:34.000000000 +1100
+@@ -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)
+ 
+@@ -896,7 +898,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 *);
+
+
+diff -urN kernel-source-2.4.26/include/linux/mbcache.h kernel-source-2.4.26-1/include/linux/mbcache.h
+--- kernel-source-2.4.26/include/linux/mbcache.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/mbcache.h	2004-02-22 19:32:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/nfs_mount.h kernel-source-2.4.26-1/include/linux/nfs_mount.h
+--- kernel-source-2.4.26/include/linux/nfs_mount.h	2001-11-23 06:47:41.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/nfs_mount.h	2004-04-17 13:32:34.000000000 +1000
+@@ -53,6 +53,7 @@
+ #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
+ 
+ #endif
+diff -urN kernel-source-2.4.26/include/linux/nfsd/export.h kernel-source-2.4.26-1/include/linux/nfsd/export.h
+--- kernel-source-2.4.26/include/linux/nfsd/export.h	2003-11-29 05:26:21.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/nfsd/export.h	2004-04-17 13:32:34.000000000 +1000
+@@ -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			0x8000
++#define NFSEXP_ALLFLAGS		0xFFFF
+ 
+ 
+ #ifdef __KERNEL__
+@@ -83,6 +84,19 @@
+ #define EX_NOHIDE(exp)		((exp)->ex_flags & NFSEXP_NOHIDE)
+ #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_NOACL(exp)		((exp)->ex_flags & NFSEXP_NOACL)
+ 
+ 
+ /*
+diff -urN kernel-source-2.4.26/include/linux/nfsd/nfsd.h kernel-source-2.4.26-1/include/linux/nfsd/nfsd.h
+--- kernel-source-2.4.26/include/linux/nfsd/nfsd.h	2002-11-29 10:53:15.000000000 +1100
++++ kernel-source-2.4.26-1/include/linux/nfsd/nfsd.h	2004-04-17 13:32:34.000000000 +1000
+@@ -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>
+@@ -127,6 +128,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 -urN kernel-source-2.4.26/include/linux/posix_acl.h kernel-source-2.4.26-1/include/linux/posix_acl.h
+--- kernel-source-2.4.26/include/linux/posix_acl.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/posix_acl.h	2004-02-22 19:42:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/posix_acl_xattr.h kernel-source-2.4.26-1/include/linux/posix_acl_xattr.h
+--- kernel-source-2.4.26/include/linux/posix_acl_xattr.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/posix_acl_xattr.h	2004-02-22 19:42:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/include/linux/xattr_acl.h kernel-source-2.4.26-1/include/linux/xattr_acl.h
+--- kernel-source-2.4.26/include/linux/xattr_acl.h	1970-01-01 10:00:00.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/xattr_acl.h	2004-02-22 19:42:21.000000000 +1100
+@@ -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 -urN kernel-source-2.4.26/kernel/Makefile kernel-source-2.4.26-1/kernel/Makefile
+--- kernel-source-2.4.26/kernel/Makefile	2001-09-17 14:22:40.000000000 +1000
++++ kernel-source-2.4.26-1/kernel/Makefile	2004-04-17 13:32:34.000000000 +1000
+@@ -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
++export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.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 -urN kernel-source-2.4.26/kernel/fork.c kernel-source-2.4.26-1/kernel/fork.c
+--- kernel-source-2.4.26/kernel/fork.c	2004-04-14 23:05:40.000000000 +1000
++++ kernel-source-2.4.26-1/kernel/fork.c	2004-04-17 14:24:07.000000000 +1000
+@@ -407,6 +407,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 -urN kernel-source-2.4.26/kernel/ksyms.c kernel-source-2.4.26-1/kernel/ksyms.c
+--- kernel-source-2.4.26/kernel/ksyms.c	2004-02-19 00:36:32.000000000 +1100
++++ kernel-source-2.4.26-1/kernel/ksyms.c	2004-02-22 20:28:35.000000000 +1100
+@@ -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>
+
+diff -urN kernel-source-2.4.26/mm/vmscan.c kernel-source-2.4.26-1/mm/vmscan.c
+--- kernel-source-2.4.26/mm/vmscan.c	2004-02-19 00:36:32.000000000 +1100
++++ kernel-source-2.4.26-1/mm/vmscan.c	2004-04-17 13:32:30.000000000 +1000
+@@ -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>
+@@ -65,6 +66,49 @@
+ int vm_vfs_scan_ratio = 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,
+@@ -523,6 +567,7 @@
+ #ifdef CONFIG_QUOTA
+ 				shrink_dqcache_memory(vm_vfs_scan_ratio, gfp_mask);
+ #endif
++				shrink_other_caches(vm_vfs_scan_ratio, gfp_mask);
+ 
+ 				if (!*failed_swapout)
+ 					*failed_swapout = !swap_out(classzone);
+
+
+diff -urN kernel-source-2.4.26/fs/jbd/journal.c kernel-source-2.4.26-1/fs/jbd/journal.c
+--- kernel-source-2.4.26/fs/jbd/journal.c	2004-04-14 23:05:40.000000000 +1000
++++ kernel-source-2.4.26-1/fs/jbd/journal.c	2004-04-11 20:59:07.000000000 +1000
+@@ -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 -urN kernel-source-2.4.26/fs/jbd/transaction.c kernel-source-2.4.26-1/fs/jbd/transaction.c
+--- kernel-source-2.4.26/fs/jbd/transaction.c	2004-02-19 00:36:31.000000000 +1100
++++ kernel-source-2.4.26-1/fs/jbd/transaction.c	2004-02-22 20:28:31.000000000 +1100
+@@ -628,6 +628,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. */
+@@ -646,6 +647,7 @@
+ 
+ 		J_ASSERT_JH(jh, handle->h_buffer_credits > 0);
+ 		handle->h_buffer_credits--;
++		handle->h_last_buffer_credits++;
+ 		goto done_locked;
+ 	}
+ 	
+@@ -721,6 +723,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
+@@ -1176,41 +1179,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 -urN kernel-source-2.4.26/include/linux/jbd.h kernel-source-2.4.26-1/include/linux/jbd.h
+--- kernel-source-2.4.26/include/linux/jbd.h	2003-06-14 00:51:38.000000000 +1000
++++ kernel-source-2.4.26-1/include/linux/jbd.h	2004-04-17 13:32:30.000000000 +1000
+@@ -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;
+ 

Modified: trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-6
===================================================================
--- trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-6	2004-09-04 20:11:25 UTC (rev 1545)
+++ trunk/kernel-2.4/source/kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-6	2004-09-04 20:52:43 UTC (rev 1546)
@@ -2,3 +2,12 @@
 + 076_isofs_acorn_support.diff
 + 077_isofs_ignore_volseqno.diff
 + 078_isofs_nodot_execute.diff
+- 021_ide_module_fix.diff
++ 079_ide_config_deps.diff
++ 081_modular_atiixp.diff
++ 080_modular_ide-proc.diff
++ 082_global_scan_direction.diff
++ 083_ide_pci_scan_delay.diff
+- 023_jbd_commit_interval.diff
+- 059_ea_acl.diff
++ 084_ea_acl-2.diff