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