Linux Access Control Lists -- Kernel Patch
17 May 2002, 15:42:46


This patch is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This patch is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this patch; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


After extracting the linux-2.2.20.tar.gz package and applying the extended
attributes patch, apply this patch as follows:

	cd linux
	patch -p1 < ../linux-2.2.20acl-0.8.27.patch

diff -Nur linux-2.2.20ea/Documentation/Configure.help linux-2.2.20acl/Documentation/Configure.help
--- linux-2.2.20ea/Documentation/Configure.help	Sun Mar 24 21:33:25 2002
+++ linux-2.2.20acl/Documentation/Configure.help	Sun Mar 24 22:05:14 2002
@@ -8262,6 +8262,20 @@
   The module will be called isp16.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt.
 
+Posix Access Control Lists
+CONFIG_FS_POSIX_ACL
+  Posix Access Control Lists (ACLs) support permissions for users and
+  groups beyond the owner/group/world scheme.
+
+  To learn more about Access Control Lists, visit the Posix ACLs for
+  Linux website <http://acl.bestbits.at/>.
+
+  If you plan to use Access Control Lists, you may also need the
+  getfacl and setfacl utilities, along with some additional patches
+  from the website.
+
+  If you don't know what Access Control Lists are, say N.
+
 Quota support
 CONFIG_QUOTA
   If you say Y here, you will be able to set per user limits for disk
--- linux-2.2.20ea/fs/Config.in	Tue Mar 26 00:05:13 2002
+++ linux-2.2.20acl/fs/Config.in	Mon Mar 25 23:04:03 2002
@@ -4,6 +4,8 @@
 mainmenu_option next_comment
 comment 'Filesystems'
 
+bool 'POSIX Access Control Lists' CONFIG_FS_POSIX_ACL
+
 bool	 'Quota support' CONFIG_QUOTA
 tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
 
@@ -48,15 +50,19 @@
   fi    
 fi
 tristate 'ROM filesystem 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 POSIX Access Control Lists' \
+    CONFIG_EXT2_FS_POSIX_ACL $CONFIG_FS_POSIX_ACL
 if [ "$CONFIG_EXT2_FS_XATTR_SHARING" = "y" ]; then
     define_tristate CONFIG_FS_MBCACHE $CONFIG_EXT2_FS
 fi
+
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
 tristate 'UFS filesystem support' CONFIG_UFS_FS
 if [ "$CONFIG_UFS_FS" != "n" ]; then
diff -Nur linux-2.2.20ea/fs/Makefile linux-2.2.20acl/fs/Makefile
--- linux-2.2.20ea/fs/Makefile	Sun Mar 24 21:41:25 2002
+++ linux-2.2.20acl/fs/Makefile	Sun Mar 24 21:41:39 2002
@@ -27,6 +27,13 @@
 MX_OBJS += mbcache.o
 endif
 
+ifeq ($(CONFIG_FS_POSIX_ACL),y)
+OX_OBJS += posix_acl.o
+endif
+ifeq ($(CONFIG_FS_POSIX_ACL),m)
+MX_OBJS += posix_acl.o
+endif
+
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
 else
diff -Nur linux-2.2.20ea/fs/ext2/Makefile linux-2.2.20acl/fs/ext2/Makefile
--- linux-2.2.20ea/fs/ext2/Makefile	Tue Mar 26 00:04:03 2002
+++ linux-2.2.20acl/fs/ext2/Makefile	Mon Mar 25 23:17:03 2002
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := ext2.o
-O_OBJS   := acl.o balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+O_OBJS   := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
 		ioctl.o namei.o super.o symlink.o truncate.o
 M_OBJS   := $(O_TARGET)
 
@@ -18,6 +18,10 @@
 
 ifeq ($(CONFIG_EXT2_FS_XATTR_USER),y)
 O_OBJS += xattr_user.o
+endif
+
+ifeq ($(CONFIG_EXT2_FS_POSIX_ACL),y)
+O_OBJS += acl.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -Nur linux-2.2.20ea/fs/ext2/acl.c linux-2.2.20acl/fs/ext2/acl.c
--- linux-2.2.20ea/fs/ext2/acl.c	Tue Mar 26 00:52:03 2002
+++ linux-2.2.20acl/fs/ext2/acl.c	Fri May 17 15:39:39 2002
@@ -1,61 +1,653 @@
 /*
  * linux/fs/ext2/acl.c
  *
- * Copyright (C) 1993, 1994, 1995
- * Remy Card (card@masi.ibp.fr)
- * Laboratoire MASI - Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
+ * 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/ext2_fs.h>
+#include <linux/ext2_xattr.h>
+#include <linux/ext2_acl.h>
+
 /*
- * This file will contain the Access Control Lists management for the
- * second extended file system.
+ * Convert from filesystem to in-memory representation.
  */
+static posix_acl_t *
+ext2_acl_from_disk(const void *value, size_t size)
+{
+	const char *end = (char *)value + size;
+	int n, count;
+	posix_acl_t *acl;
 
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
-#include <linux/sched.h>
-#include <linux/stat.h>
+	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);
+	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 posix_acl_t *acl, size_t *size)
+{
+	ext2_acl_header *ext_acl;
+	char *e;
+	int 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);
+}
 
 /*
- * ext2_permission ()
+ * Convert from extended attribute to in-memory representation.
+ */
+static posix_acl_t *
+ext2_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;
+	posix_acl_t *acl;
+	posix_acl_entry_t *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);
+	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);
+}
+
+/*
+ * Convert from in-memory to extended attribute representation.
+ */
+static int
+ext2_acl_to_xattr(const posix_acl_t *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;
+}
+
+/*
+ * Inode operation get_posix_acl().
+ *
+ * inode->i_sem: down
+ * BKL held [before 2.5.x]
+ */
+posix_acl_t *
+ext2_get_acl(struct inode *inode, int type)
+{
+	int name_index;
+	char *value;
+	posix_acl_t *acl, **p_acl;
+	const size_t size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES);
+	int retval;
+
+	switch(type) {
+		case ACL_TYPE_ACCESS:
+			p_acl = &EXT2_I(inode)->i_acl;
+			name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
+			break;
+
+		case ACL_TYPE_DEFAULT:
+			p_acl = &EXT2_I(inode)->i_default_acl;
+			name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
+			break;
+
+		default:
+			return ERR_PTR(-EINVAL);
+	}
+	if (*p_acl != EXT2_ACL_NOT_CACHED)
+		return posix_acl_dup(*p_acl);
+	value = kmalloc(size, GFP_KERNEL);
+	if (!value)
+		return ERR_PTR(-ENOMEM);
+
+	retval = ext2_xattr_get(inode, name_index, "", value, size);
+
+	if (retval == -ENOATTR || retval == -ENOSYS)
+		*p_acl = acl = NULL;
+	else if (retval < 0)
+		acl = ERR_PTR(retval);
+	else {
+		acl = ext2_acl_from_disk(value, retval);
+		if (!IS_ERR(acl))
+			*p_acl = posix_acl_dup(acl);
+	}
+	kfree(value);
+	return acl;
+}
+
+/*
+ * Inode operation set_posix_acl().
  *
- * Check for access rights
+ * inode->i_sem: down
+ * BKL held [before 2.5.x]
  */
-int ext2_permission (struct inode * inode, int mask)
+int
+ext2_set_acl(struct inode *inode, int type, posix_acl_t *acl)
 {
-	unsigned short mode = inode->i_mode;
+	int name_index;
+	void *value = NULL;
+	posix_acl_t **p_acl;
+	size_t size;
+	int error;
 
-	/*
-	 * Nobody gets write access to a file on a readonly-fs
-	 */
-	if ((mask & S_IWOTH) && 
-            (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
-            IS_RDONLY(inode))
+	if (S_ISLNK(inode->i_mode))
+		return -ENOTSUP;
+
+	switch(type) {
+		case ACL_TYPE_ACCESS:
+			name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
+			p_acl = &EXT2_I(inode)->i_acl;
+			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;
+			p_acl = &EXT2_I(inode)->i_default_acl;
+			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) {
+		if (*p_acl && *p_acl != EXT2_ACL_NOT_CACHED)
+			posix_acl_release(*p_acl);
+		*p_acl = posix_acl_dup(acl);
+	}
+	return error;
+}
+
+/*
+ * Inode operation permission().
+ *
+ * inode->i_sem: up
+ * BKL held [before 2.5.x]
+ */
+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 & S_IWOTH) && IS_IMMUTABLE(inode))
-		return -EACCES;
-
-	/*
-	 * If no ACL, checks using the file mode
-	 */
-	else if (current->fsuid == inode->i_uid)
+	/* 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 (in_group_p (inode->i_gid))
-		mode >>= 3;
-	/*
-	 * Access is always granted for root. We now check last,
-         * though, for BSD process accounting correctness
-	 */
-	if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
-		return 0;
-	if ((mask == S_IROTH) ||
-	    (S_ISDIR(inode->i_mode)  && !(mask & ~(S_IROTH | S_IXOTH))))
-		if (capable(CAP_DAC_READ_SEARCH))
-			return 0;
+	} else if (IS_POSIX_ACL(inode)) {
+		/* ACL can't contain additional permissions if
+		   the ACL_MASK entry is 0 */
+		if (!(mode & S_IRWXG))
+			goto check_mode;
+		if (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED) {
+			posix_acl_t *acl;
+
+			down(&inode->i_sem);
+			acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+			up(&inode->i_sem);
+
+			if (IS_ERR(acl))
+				return PTR_ERR(acl);
+			posix_acl_release(acl);
+			if (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED)
+				return -EIO;
+		}
+		if (EXT2_I(inode)->i_acl) {
+			int error = posix_acl_permission(inode,
+				EXT2_I(inode)->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;
+	}
+check_mode:
+	if ((mode & mask & S_IRWXO) == mask)
+		return 0;
+
+check_capabilities:
+	/* Allowed to override Discretionary Access Control? */
+	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(mode) && !(mask & ~(MAY_READ | MAY_EXEC)))))
+		return 0;
 	return -EACCES;
+}
+
+/*
+ * Determine whether a process has a valid fs_struct (kernel daemons
+ * like knfsd don't have an fs_struct).
+ */
+static inline int
+has_fs_struct(struct task_struct *task)
+{
+	return (task->fs != init_task.fs);
+}
+
+/*
+ * Initialize the ACLs of a new inode. Called from ext2_new_inode.
+ *
+ * dir->i_sem: down
+ * inode->i_sem: up (access to inode is still exclusive)
+ * BKL held [before 2.5.x] 
+ */
+int
+ext2_init_acl(struct inode *inode, struct inode *dir)
+{
+	posix_acl_t *acl = NULL;
+	int error = 0;
+
+	if (!S_ISLNK(inode->i_mode)) {
+		if (IS_POSIX_ACL(dir)) {
+			acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT);
+			if (IS_ERR(acl))
+				return PTR_ERR(acl);
+		}
+		if (!acl && has_fs_struct(current)) {
+			inode->i_mode &= ~current->fs->umask;
+			mark_inode_dirty(inode);
+		}
+	}
+	if (IS_POSIX_ACL(inode) && acl) {
+               posix_acl_t *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);
+		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 [before 2.5.x]
+ */
+int
+ext2_acl_chmod(struct inode *inode)
+{
+	posix_acl_t *acl, *clone;
+        int error;
+
+	if (!IS_POSIX_ACL(inode))
+		return 0;
+	if (S_ISLNK(inode->i_mode))
+		return -ENOTSUP;
+	acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+	if (IS_ERR(acl) || !acl)
+		return PTR_ERR(acl);
+	clone = posix_acl_clone(acl);
+	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 len = sizeof(XATTR_NAME_ACL_ACCESS)-1;
+
+	if (!IS_POSIX_ACL(inode))
+		return 0;
+	if (list)
+		memcpy(list, XATTR_NAME_ACL_ACCESS, len);
+	return len;
+}
+
+static size_t
+ext2_xattr_list_acl_default(char *list, struct inode *inode,
+			    const char *name, int name_len)
+{
+	const size_t len = sizeof(XATTR_NAME_ACL_DEFAULT)-1;
+
+	if (!IS_POSIX_ACL(inode))
+		return 0;
+	if (list)
+		memcpy(list, XATTR_NAME_ACL_DEFAULT, len);
+	return len;
+}
+
+static int
+ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
+{
+	posix_acl_t *acl;
+	int error;
+
+	if (!IS_POSIX_ACL(inode))
+		return -ENOTSUP;
+
+	acl = ext2_get_acl(inode, type);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl == NULL)
+		return -ENOATTR;
+	error = ext2_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, void *value, size_t size)
+{
+	posix_acl_t *acl;
+	int error;
+
+	if (!IS_POSIX_ACL(inode))
+		return -ENOTSUP;
+	if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+		return -EPERM;
+
+	if (value) {
+		acl = ext2_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,
+			  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,
+			   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 -Nur linux-2.2.20ea/fs/ext2/dir.c linux-2.2.20acl/fs/ext2/dir.c
--- linux-2.2.20ea/fs/ext2/dir.c	Thu Feb 28 23:47:17 2002
+++ linux-2.2.20acl/fs/ext2/dir.c	Sat Mar  9 18:14:14 2002
@@ -24,6 +24,7 @@
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
 #include <linux/ext2_xattr.h>
+#include <linux/ext2_acl.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
 
@@ -82,6 +83,8 @@
 	ext2_getxattr,		/* getxattr */
 	ext2_listxattr,		/* listxattr */
 	ext2_removexattr,	/* removexattr */
+	ext2_get_acl,		/* get_posix_acl */
+	ext2_set_acl,		/* set_posix_acl */
 };
 
 int ext2_check_dir_entry (const char * function, struct inode * dir,
diff -Nur linux-2.2.20ea/fs/ext2/file.c linux-2.2.20acl/fs/ext2/file.c
--- linux-2.2.20ea/fs/ext2/file.c	Thu Feb 28 23:46:43 2002
+++ linux-2.2.20acl/fs/ext2/file.c	Sat Mar  9 18:15:14 2002
@@ -25,6 +25,7 @@
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
 #include <linux/ext2_xattr.h>
+#include <linux/ext2_acl.h>
 #include <linux/fcntl.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
@@ -110,6 +111,8 @@
 	ext2_getxattr,		/* getxattr */
 	ext2_listxattr,		/* listxattr */
 	ext2_removexattr,	/* removexattr */
+	ext2_get_acl,		/* get_posix_acl */
+	ext2_set_acl,		/* set_posix_acl */
 };
 
 /*
diff -Nur linux-2.2.20ea/fs/ext2/ialloc.c linux-2.2.20acl/fs/ext2/ialloc.c
--- linux-2.2.20ea/fs/ext2/ialloc.c	Fri Mar  1 00:21:04 2002
+++ linux-2.2.20acl/fs/ext2/ialloc.c	Sat Mar  9 18:27:54 2002
@@ -30,6 +30,7 @@
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
 #include <linux/ext2_xattr.h>
+#include <linux/ext2_acl.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
 #include <linux/string.h>
@@ -300,6 +301,8 @@
 	ext2_getxattr,		/* getxattr */
 	ext2_listxattr,		/* listxattr */
 	ext2_removexattr,	/* removexattr */
+	ext2_get_acl,		/* get_posix_acl */
+	ext2_set_acl,		/* set_posix_acl */
 };
 
 struct inode_operations ext2_blkdev_inode_operations = {
@@ -329,6 +332,8 @@
 	ext2_getxattr,		/* getxattr */
 	ext2_listxattr,		/* listxattr */
 	ext2_removexattr,	/* removexattr */
+	ext2_get_acl,		/* get_posix_acl */
+	ext2_set_acl,		/* set_posix_acl */
 };
 
 struct inode_operations ext2_fifo_inode_operations = {
@@ -358,6 +363,8 @@
 	ext2_getxattr,		/* getxattr */
 	ext2_listxattr,		/* listxattr */
 	ext2_removexattr,	/* removexattr */
+	ext2_get_acl,		/* get_posix_acl */
+	ext2_set_acl,		/* set_posix_acl */
 };
 
 struct inode_operations ext2_sock_inode_operations = {
@@ -387,6 +394,8 @@
 	ext2_getxattr,		/* getxattr */
 	ext2_listxattr,		/* listxattr */
 	ext2_removexattr,	/* removexattr */
+	ext2_get_acl,		/* get_posix_acl */
+	ext2_set_acl,		/* set_posix_acl */
 };
 
 /*
@@ -399,7 +408,7 @@
  * For other inodes, search forward from the parent directory\'s block
  * group to find a free inode.
  */
-struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
+struct inode * ext2_new_inode (struct inode * dir, int mode, int * err)
 {
 	struct super_block * sb;
 	struct buffer_head * bh;
@@ -613,15 +622,23 @@
 	unlock_super (sb);
 	if(DQUOT_ALLOC_INODE(sb, inode)) {
 		sb->dq_op->drop(inode);
-		inode->i_nlink = 0;
-		iput(inode);
 		*err = -EDQUOT;
-		return NULL;
+		goto fail;
+	}
+	*err = ext2_init_acl(inode, dir);
+	if (*err) {
+		DQUOT_FREE_INODE(sb, inode);
+		goto fail;
 	}
 	ext2_debug ("allocating inode %lu\n", inode->i_ino);
 
 	*err = 0;
 	return inode;
+
+fail:
+	inode->i_nlink = 0;
+	iput(inode);
+	return NULL;
 }
 
 unsigned long ext2_count_free_inodes (struct super_block * sb)
diff -Nur linux-2.2.20ea/fs/ext2/inode.c linux-2.2.20acl/fs/ext2/inode.c
--- linux-2.2.20ea/fs/ext2/inode.c	Thu Feb 28 23:57:53 2002
+++ linux-2.2.20acl/fs/ext2/inode.c	Sat Mar  9 19:26:04 2002
@@ -26,6 +26,7 @@
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
+#include <linux/ext2_acl.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
 #include <linux/string.h>
@@ -589,6 +590,15 @@
 		inode->i_attr_flags |= ATTR_FLAG_NOATIME;
 		inode->i_flags |= MS_NOATIME;
 	}
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+	if (IS_POSIX_ACL(inode) && 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:
@@ -790,6 +800,10 @@
 		}
 	}
 	mark_inode_dirty(inode);
+
+	if (iattr->ia_valid & ATTR_MODE)
+		retval = ext2_acl_chmod(inode);
+
 out:
 	return retval;
 }
diff -Nur linux-2.2.20ea/fs/ext2/namei.c linux-2.2.20acl/fs/ext2/namei.c
--- linux-2.2.20ea/fs/ext2/namei.c	Fri Mar  1 00:18:29 2002
+++ linux-2.2.20acl/fs/ext2/namei.c	Sat Mar  9 18:34:07 2002
@@ -371,7 +371,6 @@
 		return err;
 
 	inode->i_op = &ext2_file_inode_operations;
-	inode->i_mode = mode;
 	mark_inode_dirty(inode);
 	bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
 	if (!bh) {
@@ -407,8 +406,6 @@
 	if (!inode)
 		goto out;
 
-	inode->i_uid = current->fsuid;
-	inode->i_mode = mode;
 	inode->i_op = NULL;
 	bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
 	if (!bh)
@@ -476,13 +473,12 @@
 		goto out;
 
 	err = -EIO;
-	inode = ext2_new_inode (dir, S_IFDIR, &err);
+       inode = ext2_new_inode (dir, S_IFDIR | mode, &err);
 	if (!inode)
 		goto out;
 
 	inode->i_op = &ext2_dir_inode_operations;
 	inode->i_size = inode->i_sb->s_blocksize;
-	inode->i_blocks = 0;	
 	dir_block = ext2_bread (inode, 0, 1, &err);
 	if (!dir_block) {
 		ext2_xattr_drop_inode(inode);
@@ -510,9 +506,6 @@
 	inode->i_nlink = 2;
 	mark_buffer_dirty(dir_block, 1);
 	brelse (dir_block);
-	inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
-	if (dir->i_mode & S_ISGID)
-		inode->i_mode |= S_ISGID;
 	mark_inode_dirty(inode);
 	bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
 	if (!bh)
@@ -714,10 +707,9 @@
 	int i, l, err = -EIO;
 	char c;
 
-	if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
+       if (!(inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO, &err))) {
 		return err;
 	}
-	inode->i_mode = S_IFLNK | S_IRWXUGO;
 	inode->i_op = &ext2_symlink_inode_operations;
 	for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
 	     symname [l]; l++)
diff -Nur linux-2.2.20ea/fs/ext2/super.c linux-2.2.20acl/fs/ext2/super.c
--- linux-2.2.20ea/fs/ext2/super.c	Tue Mar 26 00:11:54 2002
+++ linux-2.2.20acl/fs/ext2/super.c	Mon Mar 25 23:12:14 2002
@@ -28,6 +28,7 @@
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
 #include <linux/ext2_xattr.h>
+#include <linux/ext2_acl.h>
 #include <linux/malloc.h>
 #include <linux/sched.h>
 #include <linux/stat.h>
@@ -131,6 +132,24 @@
 	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 = NULL;
+	}
+	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 = NULL;
+	}
+}
+#else
+# define ext2_clear_inode NULL
+#endif
+
 static struct super_operations ext2_sops = {
 	ext2_read_inode,
 	ext2_write_inode,
@@ -140,7 +159,8 @@
 	ext2_put_super,
 	ext2_write_super,
 	ext2_statfs,
-	ext2_remount
+	ext2_remount,
+	ext2_clear_inode,
 };
 
 /*
@@ -172,6 +192,12 @@
 					*c++ = '\0';
 				if (!strcmp(value, "off"))
 					flags = 0;
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+				else if (!strcmp(value, "acl"))
+				       flags |= XATTR_MNT_FLAG_POSIX_ACL;
+				else if (!strcmp(value, "noacl"))
+				      flags &= ~XATTR_MNT_FLAG_POSIX_ACL;
+#endif
 #ifdef CONFIG_EXT2_FS_XATTR_USER
 				else if (!strcmp(value, "user"))
 					flags |= XATTR_MNT_FLAG_USER;
@@ -437,6 +463,9 @@
 	sb->u.ext2_sb.s_mount_opt = 0;
 	set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
 	sb->s_xattr_flags = 0;
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+	sb->s_xattr_flags |= XATTR_MNT_FLAG_POSIX_ACL;
+#endif
 #ifdef CONFIG_EXT2_FS_XATTR_USER
 	sb->s_xattr_flags |= XATTR_MNT_FLAG_USER;
 #endif
@@ -775,6 +804,7 @@
 static void exit_ext2_fs(void)
 {
 	unregister_filesystem(&ext2_fs_type);
+	exit_ext2_acl();
 	exit_ext2_xattr_user();
 	exit_ext2_xattr();
 }
@@ -784,6 +814,8 @@
 	int error = init_ext2_xattr();
 	if (!error)
 		error = init_ext2_xattr_user();
+	if (!error)
+		error = init_ext2_acl();
 	if (!error)
 		error = register_filesystem(&ext2_fs_type);
 	if (!error)
diff -Nur linux-2.2.20ea/fs/ext2/truncate.c linux-2.2.20acl/fs/ext2/truncate.c
--- linux-2.2.20ea/fs/ext2/truncate.c	Thu Mar 14 21:56:42 2002
+++ linux-2.2.20acl/fs/ext2/truncate.c	Thu Mar 14 21:24:00 2002
@@ -386,8 +386,12 @@
 	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
 	    S_ISLNK(inode->i_mode)))
 		return;
-	if (ext2_inode_is_fast_symlink(inode))
-		return;
+	if (S_ISLNK(inode->i_mode)) {
+		int ea_blocks = inode->u.ext2_i.i_file_acl ?
+			(inode->i_sb->s_blocksize >> 9) : 0;
+		if (inode->i_blocks - ea_blocks == 0)
+			return;
+	}
 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 		return;
 	
diff -Nur linux-2.2.20ea/fs/namei.c linux-2.2.20acl/fs/namei.c
--- linux-2.2.20ea/fs/namei.c	Fri Nov  2 17:39:08 2001
+++ linux-2.2.20acl/fs/namei.c	Sat Mar  9 18:40:20 2002
@@ -683,9 +683,6 @@
 	struct inode *inode;
 	struct dentry *dentry;
 
-	mode &= S_IALLUGO & ~current->fs->umask;
-	mode |= S_IFREG;
-
 	dentry = lookup_dentry(pathname, NULL, lookup_flags(flag));
 	if (IS_ERR(dentry))
 		return dentry;
@@ -730,8 +727,12 @@
 			if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
 				error = -EACCES;
 			else {
+                               if (!IS_POSIX_ACL(dir->d_inode))
+                                       mode &= ~current->fs->umask;
+
 				DQUOT_INIT(dir->d_inode);
-				error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);
+                               error = dir->d_inode->i_op->create(dir->d_inode,
+                                       dentry, S_IFREG | (mode & S_IALLUGO));
 				/* Don't check for write permission, don't truncate */
 				acc_mode = 0;
 				flag &= ~O_TRUNC;
@@ -823,7 +824,6 @@
 	struct dentry *dir;
 	struct dentry *dentry, *retval;
 
-	mode &= ~current->fs->umask;
 	dentry = lookup_dentry(filename, NULL, 0);
 	if (IS_ERR(dentry))
 		return dentry;
@@ -841,6 +841,9 @@
 	if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod)
 		goto exit_lock;
 
+       if (!IS_POSIX_ACL(dir->d_inode))
+               mode &= ~current->fs->umask;
+
 	DQUOT_INIT(dir->d_inode);
 	error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);
 exit_lock:
@@ -923,9 +926,11 @@
 	if (!dir->d_inode->i_op || !dir->d_inode->i_op->mkdir)
 		goto exit_lock;
 
+       if (!IS_POSIX_ACL(dir->d_inode))
+               mode &= ~current->fs->umask;
+
 	DQUOT_INIT(dir->d_inode);
-	mode &= 0777 & ~current->fs->umask;
-	error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);
+       error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode & S_IALLUGO);
 
 exit_lock:
 	unlock_dir(dir);
diff -Nur linux-2.2.20ea/fs/nfsd/nfs3xdr.c linux-2.2.20acl/fs/nfsd/nfs3xdr.c
--- linux-2.2.20ea/fs/nfsd/nfs3xdr.c	Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/nfs3xdr.c	Fri May 17 15:36:42 2002
@@ -151,21 +151,34 @@
 }
 
 static inline u32 *
-encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
-	struct inode	*inode = dentry->d_inode;
+	struct inode	*inode = fhp->fh_dentry->d_inode;
+	mode_t		mode = inode->i_mode;
 
 	if (!inode) {
 		printk("nfsd: NULL inode in %s:%d", __FILE__, __LINE__);
 		return NULL;
 	}
+ 	if (nfs_permission_mode == NFS_FILE_MODE_PERMISSION_BITS_SECURE) {
+ 		posix_acl_t *acl = nfsd_get_posix_acl(fhp, ACL_TYPE_ACCESS);
+ 
+  		if (IS_ERR(acl))
+  			return 0;
+  		else if (acl) {
+ 			int error = posix_acl_masq_nfs_mode(acl, &mode);
+  			posix_acl_release(acl);
+  			if (error)
+  				return 0;
+  		}
+	}
 
-	*p++ = htonl(nfs3_ftypes[(inode->i_mode & S_IFMT) >> 12]);
-	*p++ = htonl((u32) inode->i_mode);
+	*p++ = htonl(nfs3_ftypes[(mode & S_IFMT) >> 12]);
+	*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));
-	if (S_ISLNK(inode->i_mode) && inode->i_size > NFS3_MAXPATHLEN) {
+	if (S_ISLNK(mode) && inode->i_size > NFS3_MAXPATHLEN) {
 		p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
 	} else {
 		p = xdr_encode_hyper(p, (u64) inode->i_size);
@@ -231,11 +244,11 @@
  * handle. In this case, no attributes are returned.
  */
 static u32 *
-encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
+encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
-	if (dentry && dentry->d_inode != NULL) {
+	if (fhp->fh_dentry && fhp->fh_dentry->d_inode != NULL) {
 		*p++ = xdr_one;		/* attributes follow */
-		return encode_fattr3(rqstp, p, dentry);
+		return encode_fattr3(rqstp, p, fhp);
 	}
 	*p++ = xdr_zero;
 	return p;
@@ -262,7 +275,7 @@
 	}
 	/* no pre- or post-attrs */
 	*p++ = xdr_zero;
-	return encode_post_op_attr(rqstp, p, dentry);
+	return encode_post_op_attr(rqstp, p, fhp);
 }
 
 /*
@@ -520,7 +533,7 @@
 					struct nfsd3_attrstat *resp)
 {
 	if (resp->status == 0
-	 && !(p = encode_fattr3(rqstp, p, resp->fh.fh_dentry)))
+	 && !(p = encode_fattr3(rqstp, p, &resp->fh)))
 		return 0;
 	return xdr_ressize_check(rqstp, p);
 }
@@ -542,9 +555,9 @@
 {
 	if (resp->status == 0) {
 		p = encode_fh(p, &resp->fh);
-		p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+		p = encode_post_op_attr(rqstp, p, &resp->fh);
 	}
-	p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_dentry);
+	p = encode_post_op_attr(rqstp, p, &resp->dirfh);
 	return xdr_ressize_check(rqstp, p);
 }
 
@@ -553,7 +566,7 @@
 nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_accessres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+	p = encode_post_op_attr(rqstp, p, &resp->fh);
 	if (resp->status == 0)
 		*p++ = htonl(resp->access);
 	return xdr_ressize_check(rqstp, p);
@@ -564,7 +577,7 @@
 nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readlinkres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+	p = encode_post_op_attr(rqstp, p, &resp->fh);
 	if (resp->status == 0) {
 		*p++ = htonl(resp->len);
 		p += XDR_QUADLEN(resp->len);
@@ -577,7 +590,7 @@
 nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+	p = encode_post_op_attr(rqstp, p, &resp->fh);
 	if (resp->status == 0) {
 		*p++ = htonl(resp->count);
 		*p++ = htonl(resp->eof);
@@ -610,7 +623,7 @@
 	if (resp->status == 0) {
 		*p++ = xdr_one;
 		p = encode_fh(p, &resp->fh);
-		p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+		p = encode_post_op_attr(rqstp, p, &resp->fh);
 	}
 	p = encode_wcc_data(rqstp, p, &resp->dirfh);
 	return xdr_ressize_check(rqstp, p);
@@ -631,7 +644,7 @@
 nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_linkres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+	p = encode_post_op_attr(rqstp, p, &resp->fh);
 	p = encode_wcc_data(rqstp, p, &resp->tfh);
 	return xdr_ressize_check(rqstp, p);
 }
@@ -641,7 +654,7 @@
 nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd3_readdirres *resp)
 {
-	p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+	p = encode_post_op_attr(rqstp, p, &resp->fh);
 	if (resp->status == 0) {
 		/* stupid readdir cookie */
 		memcpy(p, resp->verf, 8); p += 2;
@@ -708,7 +721,7 @@
 		fh_init(&fh);
 		/* Disabled for now because of lock-up */
 		if (0 && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh) == 0) {
-			p = encode_post_op_attr(cd->rqstp, p, fh.fh_dentry);
+			p = encode_post_op_attr(cd->rqstp, p, &fh);
 			p = encode_fh(p, &fh);
 			fh_put(&fh);
 		} else {
diff -Nur linux-2.2.20ea/fs/nfsd/nfsctl.c linux-2.2.20acl/fs/nfsd/nfsctl.c
--- linux-2.2.20ea/fs/nfsd/nfsctl.c	Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/nfsctl.c	Mon Apr 22 07:54:06 2002
@@ -8,6 +8,7 @@
 #define NFS_GETFH_NEW
 
 #include <linux/config.h>
+#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/version.h>
 
@@ -313,13 +314,41 @@
 	return err;
 }
 
+/*
+   About the nfs_permission_mode module parameter:
+
+   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 nfs_permission_mode module parameter can either send the mode
+   permission bits (inode->i_mode & S_IRWXUGO) of files that have
+   Access Control Lists unchanged, or remove all permissions that
+   might lead to wrong decisions on the client machine.
+   
+   The file mode permission bits should only be set to 'unchanged' in
+   an environment where it is known that all NFSv3 clients use the
+   access RPC.  (The file mode permission bits for NFSv2 clients are
+   always "secured".)
+*/
+
+int nfs_permission_mode = NFS_FILE_MODE_PERMISSION_BITS_SECURE;
+
 #ifdef MODULE
+
 /* New-style module support since 2.1.18 */
 #if LINUX_VERSION_CODE >= 131346
 EXPORT_NO_SYMBOLS;
 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
 #endif
 
+MODULE_PARM(nfs_permission_mode, "i");
+MODULE_PARM_DESC(nfs_permission_mode,
+                 "nfsd file mode permission bits: 0=unchanged, 1=secure (1)");
+
 extern int (*do_nfsservctl)(int, void *, void *);
 
 /*
@@ -367,4 +396,18 @@
 	nfsd_stat_shutdown();
 	nfsd_lockd_shutdown();
 }
+
+#else
+
+static int __init
+nfs_permission_mode_setup(char *str)
+{
+       nfs_permission_mode = simple_strtol(str, &str, 10) ?
+               NFS_FILE_MODE_PERMISSION_BITS_SECURE :
+               NFS_FILE_MODE_PERMISSION_BITS_UNCHANGED;
+       return 0;
+}
+
+__setup("nfs_permission_mode=", nfs_permission_mode_setup);
+
 #endif
diff -Nur linux-2.2.20ea/fs/nfsd/nfsxdr.c linux-2.2.20acl/fs/nfsd/nfsxdr.c
--- linux-2.2.20ea/fs/nfsd/nfsxdr.c	Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/nfsxdr.c	Fri May 17 15:31:37 2002
@@ -133,24 +133,37 @@
 }
 
 static inline u32 *
-encode_fattr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
-	int type = (inode->i_mode & S_IFMT);
+	posix_acl_t	*acl = nfsd_get_posix_acl(fhp, ACL_TYPE_ACCESS);
+	struct inode	*inode = fhp->fh_dentry->d_inode;
+	mode_t		mode = inode->i_mode;
+
 	if (!inode)
 		return 0;
-	*p++ = htonl(nfs_ftypes[type >> 12]);
-	*p++ = htonl((u32) inode->i_mode);
+  
+ 	if (IS_ERR(acl))
+ 		return 0;
+ 	else if (acl) {
+ 		int error = posix_acl_masq_nfs_mode(acl, &mode);
+ 		posix_acl_release(acl);
+		if (error)
+			return 0;
+	}
+
+	*p++ = htonl(nfs_ftypes[(mode & S_IFMT) >> 12]);
+	*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));
 
-	if (S_ISLNK(type) && inode->i_size > NFS_MAXPATHLEN) {
+	if (S_ISLNK(mode) && inode->i_size > NFS_MAXPATHLEN) {
 		*p++ = htonl(NFS_MAXPATHLEN);
 	} else {
 		*p++ = htonl((u32) inode->i_size);
 	}
 	*p++ = htonl((u32) inode->i_blksize);
-	if (S_ISCHR(type) || S_ISBLK(type))
+	if (S_ISCHR(mode) || S_ISBLK(mode))
 		*p++ = htonl((u32) inode->i_rdev);
 	else
 		*p++ = htonl(0xffffffff);
@@ -334,7 +347,7 @@
 nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd_attrstat *resp)
 {
-	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
+	if (!(p = encode_fattr(rqstp, p, &resp->fh)))
 		return 0;
 	return xdr_ressize_check(rqstp, p);
 }
@@ -344,7 +357,7 @@
 					struct nfsd_diropres *resp)
 {
 	if (!(p = encode_fh(p, &resp->fh))
-	 || !(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
+	 || !(p = encode_fattr(rqstp, p, &resp->fh)))
 		return 0;
 	return xdr_ressize_check(rqstp, p);
 }
@@ -362,7 +375,7 @@
 nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
 					struct nfsd_readres *resp)
 {
-	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
+	if (!(p = encode_fattr(rqstp, p, &resp->fh)))
 		return 0;
 	*p++ = htonl(resp->count);
 	p += XDR_QUADLEN(resp->count);
diff -Nur linux-2.2.20ea/fs/nfsd/vfs.c linux-2.2.20acl/fs/nfsd/vfs.c
--- linux-2.2.20ea/fs/nfsd/vfs.c	Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/vfs.c	Fri May 17 15:32:39 2002
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
+#include <linux/smp_lock.h>
 #include <linux/fs.h>
 #include <linux/major.h>
 #include <linux/proc_fs.h>
@@ -1799,3 +1800,22 @@
 		nfsd_nservers = 0;
 	}
 }
+
+#ifdef CONFIG_FS_POSIX_ACL
+posix_acl_t *
+nfsd_get_posix_acl(struct svc_fh *fhp, int type)
+{
+	struct inode *inode = fhp->fh_dentry->d_inode;
+	posix_acl_t *acl = NULL;
+
+	if (inode && IS_POSIX_ACL(inode) && inode->i_op && inode->i_op->get_posix_acl) {
+		if (!fhp->fh_locked)
+			fh_lock(fhp);  /* unlocking is done automatically */
+		lock_kernel();  /* goes away in 2.5 */
+		acl = inode->i_op->get_posix_acl(inode, type);
+		unlock_kernel();  /* goes away in 2.5 */
+	}
+
+	return acl;
+}
+#endif
diff -Nur linux-2.2.20ea/fs/posix_acl.c linux-2.2.20acl/fs/posix_acl.c
--- linux-2.2.20ea/fs/posix_acl.c	Thu Jan  1 01:00:00 1970
+++ linux-2.2.20acl/fs/posix_acl.c	Fri May 17 15:38:17 2002
@@ -0,0 +1,495 @@
+/*
+ * linux/fs/posix_acl.c
+ *
+ *  Copyright (C) 2001 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/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,0)
+MODULE_LICENSE("GPL");
+#endif
+
+EXPORT_SYMBOL(posix_acl_alloc);
+EXPORT_SYMBOL(posix_acl_dup);
+EXPORT_SYMBOL(posix_acl_clone);
+EXPORT_SYMBOL(posix_acl_release);
+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);
+
+EXPORT_SYMBOL(get_posix_acl);
+EXPORT_SYMBOL(set_posix_acl);
+
+/*
+ * Allocate a new ACL with the specified number of entries.
+ */
+posix_acl_t *
+posix_acl_alloc(int count)
+{
+	const size_t size = sizeof(posix_acl_t) +
+	                    count * sizeof(posix_acl_entry_t);
+	posix_acl_t *acl = kmalloc(size, GFP_KERNEL);
+	if (acl) {
+		atomic_set(&acl->a_refcount, 1);
+		acl->a_count = count;
+	}
+	return acl;
+}
+
+/*
+ * Duplicate an ACL handle.
+ */
+posix_acl_t *
+posix_acl_dup(posix_acl_t *acl)
+{
+	if (acl)
+		atomic_inc(&acl->a_refcount);
+	return acl;
+}
+
+/*
+ * Clone an ACL.
+ */
+posix_acl_t *
+posix_acl_clone(const posix_acl_t *acl)
+{
+	posix_acl_t *clone = NULL;
+
+	if (acl) {
+		int size = sizeof(posix_acl_t) + acl->a_count *
+		           sizeof(posix_acl_entry_t);
+		clone = kmalloc(size, GFP_KERNEL);
+		if (clone) {
+			memcpy(clone, acl, size);
+			atomic_set(&clone->a_refcount, 1);
+		}
+	}
+	return clone;
+}
+
+/*
+ * Free an ACL handle.
+ */
+void
+posix_acl_release(posix_acl_t *acl)
+{
+	if (acl && atomic_dec_and_test(&acl->a_refcount))
+		kfree(acl);
+}
+
+/*
+ * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
+ */
+int
+posix_acl_valid(const posix_acl_t *acl)
+{
+	const posix_acl_entry_t *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 posix_acl_t *acl, mode_t *mode_p)
+{
+	const posix_acl_entry_t *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.
+ */
+posix_acl_t *
+posix_acl_from_mode(mode_t mode)
+{
+	posix_acl_t *acl = posix_acl_alloc(3);
+	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 posix_acl_t *acl, int want)
+{
+	const posix_acl_entry_t *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(posix_acl_t *acl, mode_t *mode_p)
+{
+	posix_acl_entry_t *pa, *pe;
+	posix_acl_entry_t *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(posix_acl_t *acl, mode_t mode)
+{
+	posix_acl_entry_t *group_obj = NULL, *mask_obj = NULL;
+	posix_acl_entry_t *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(posix_acl_t *acl, mode_t *mode_p)
+{
+	posix_acl_entry_t *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;
+}
+
+/*
+ * Get the POSIX ACL of an inode.
+ */
+posix_acl_t *
+get_posix_acl(struct inode *inode, int type)
+{
+	posix_acl_t *acl;
+
+	if (!inode->i_op || !inode->i_op->get_posix_acl)
+		return ERR_PTR(-ENOTSUP);
+
+	down(&inode->i_sem);
+	lock_kernel();  /* goes away in 2.5.x */
+	acl = inode->i_op->get_posix_acl(inode, type);
+	unlock_kernel();  /* goes away in 2.5.x */
+	up(&inode->i_sem);
+
+	return acl;
+}
+
+/*
+ * Set the POSIX ACL of an inode.
+ */
+int
+set_posix_acl(struct inode *inode, int type, posix_acl_t *acl)
+{
+	int error;
+
+	if (!inode->i_op || !inode->i_op->set_posix_acl)
+		return -ENOTSUP;
+
+	down(&inode->i_sem);
+	lock_kernel();  /* goes away in 2.5.x */
+	error = inode->i_op->set_posix_acl(inode, type, acl);
+	unlock_kernel();  /* goes away in 2.5.x */
+	up(&inode->i_sem);
+
+	return error;
+}
diff -Nur linux-2.2.20ea/fs/xattr.c linux-2.2.20acl/fs/xattr.c
--- linux-2.2.20ea/fs/xattr.c	Mon Mar 18 02:34:34 2002
+++ linux-2.2.20acl/fs/xattr.c	Sat Mar  9 17:29:12 2002
@@ -84,9 +84,9 @@
 
 	error = -EOPNOTSUPP;
 	if (d->d_inode->i_op && d->d_inode->i_op->setxattr) {
-		down(&d->d_inode->i_sem);
+		lock_kernel();
 		error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags);
-		up(&d->d_inode->i_sem);
+		unlock_kernel();
 	}
 
 	xattr_free(kvalue, size);
@@ -169,9 +169,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);
-		up(&d->d_inode->i_sem);
+		unlock_kernel();
 	}
 
 	if (kvalue && error > 0)
@@ -250,9 +250,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);
-		up(&d->d_inode->i_sem);
+		unlock_kernel();
 	}
 
 	if (klist && error > 0)
@@ -333,9 +333,9 @@
 
 	error = -EOPNOTSUPP;
 	if (d->d_inode->i_op && d->d_inode->i_op->removexattr) {
-		down(&d->d_inode->i_sem);
+		lock_kernel();
 		error = d->d_inode->i_op->removexattr(d, kname);
-		up(&d->d_inode->i_sem);
+		unlock_kernel();
 	}
 	return error;
 }
diff -Nur linux-2.2.20ea/include/asm-sparc64/unistd.h linux-2.2.20acl/include/asm-sparc64/unistd.h
--- linux-2.2.20ea/include/asm-sparc64/unistd.h	Wed Apr 24 11:24:22 2002
+++ linux-2.2.20acl/include/asm-sparc64/unistd.h	Sun Mar 25 18:37:40 2001
@@ -184,24 +184,24 @@
 /* #define __NR_exportfs        166    SunOS Specific                              */
 #define __NR_mount              167 /* Common                                      */
 #define __NR_ustat              168 /* Common                                      */
- #define __NR_setxattr          169 /* SunOS Specific                              */
-#define __NR_lsetxattr          170 /* SunOS Specific                              */
-#define __NR_fsetxattr          171 /* SunOS Specific                              */
-#define __NR_getxattr           172 /* SunOS Specific                              */
-#define __NR_lgetxattr          173 /* SunOS Specific                              */
+/* #define __NR_semsys          169    SunOS Specific                              */
+/* #define __NR_msgsys          170    SunOS Specific                              */
+/* #define __NR_shmsys          171    SunOS Specific                              */
+/* #define __NR_auditsys        172    SunOS Specific                              */
+/* #define __NR_rfssys          173    SunOS Specific                              */
 #define __NR_getdents           174 /* Common                                      */
 #define __NR_setsid             175 /* Common                                      */
 #define __NR_fchdir             176 /* Common                                      */
-#define __NR_fgetxattr          177 /* SunOS Specific                              */
-#define __NR_listxattr          178 /* SunOS Specific                              */
-#define __NR_llistxattr         179 /* SunOS Specific                              */
-#define __NR_flistxattr         180 /* SunOS Specific                              */
-#define __NR_removexattr        181 /* SunOS Specific                              */
-#define __NR_lremovexattr       182 /* SunOS Specific                              */
+/* #define __NR_fchroot         177    SunOS Specific                              */
+/* #define __NR_vpixsys         178    SunOS Specific                              */
+/* #define __NR_aioread         179    SunOS Specific                              */
+/* #define __NR_aiowrite        180    SunOS Specific                              */
+/* #define __NR_aiowait         181    SunOS Specific                              */
+/* #define __NR_aiocancel       182    SunOS Specific                              */
 #define __NR_sigpending         183 /* Common                                      */
 #define __NR_query_module	184 /* Linux Specific				   */
 #define __NR_setpgid            185 /* Common                                      */
-#define __NR_fremovexattr       186 /* SunOS Specific                              */
+/* #define __NR_pathconf        186    SunOS Specific                              */
 /* #define __NR_fpathconf       187    SunOS Specific                              */
 /* #define __NR_sysconf         188    SunOS Specific                              */
 #define __NR_uname              189 /* Linux Specific                              */
diff -Nur linux-2.2.20ea/include/linux/ext2_acl.h linux-2.2.20acl/include/linux/ext2_acl.h
--- linux-2.2.20ea/include/linux/ext2_acl.h	Thu Jan  1 01:00:00 1970
+++ linux-2.2.20acl/include/linux/ext2_acl.h	Wed Apr 24 10:30:40 2002
@@ -0,0 +1,105 @@
+/*
+  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 posix_acl_t *ext2_get_acl (struct inode *, int);
+extern int ext2_set_acl (struct inode *, int, posix_acl_t *);
+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
+#  define ext2_get_acl	NULL
+#  define ext2_set_acl	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;
+	return 0;
+}
+
+static inline int
+init_ext2_acl(void)
+{
+	return 0;
+}
+
+static inline void
+exit_ext2_acl(void)
+{
+}
+
+# endif
+#endif
+
diff -Nur linux-2.2.20ea/include/linux/ext2_fs.h linux-2.2.20acl/include/linux/ext2_fs.h
--- linux-2.2.20ea/include/linux/ext2_fs.h	Wed Mar 20 02:09:09 2002
+++ linux-2.2.20acl/include/linux/ext2_fs.h	Wed Mar 20 02:09:30 2002
@@ -509,9 +509,6 @@
 # define ATTRIB_NORET  __attribute__((noreturn))
 # define NORET_AND     noreturn,
 
-/* acl.c */
-extern int ext2_permission (struct inode *, int);
-
 /* balloc.c */
 extern int ext2_group_sparse(int group);
 extern int ext2_new_block (const struct inode *, unsigned long,
@@ -541,7 +538,7 @@
 extern int ext2_sync_file (struct file *, struct dentry *);
 
 /* ialloc.c */
-extern struct inode * ext2_new_inode (const struct inode *, int, int *);
+extern struct inode * ext2_new_inode (struct inode *, int, 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 *);
diff -Nur linux-2.2.20ea/include/linux/ext2_fs_i.h linux-2.2.20acl/include/linux/ext2_fs_i.h
--- linux-2.2.20ea/include/linux/ext2_fs_i.h	Sun Mar 25 18:31:03 2001
+++ linux-2.2.20acl/include/linux/ext2_fs_i.h	Sat Mar  9 19:20:57 2002
@@ -36,6 +36,10 @@
 	__u32	i_prealloc_block;
 	__u32	i_prealloc_count;
 	__u32	i_high_size;
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+	posix_acl_t	*i_acl;
+	posix_acl_t	*i_default_acl;
+#endif
 	int	i_new_inode:1;	/* Is a freshly allocated inode */
 };
 
diff -Nur linux-2.2.20ea/include/linux/ext2_xattr.h linux-2.2.20acl/include/linux/ext2_xattr.h
--- linux-2.2.20ea/include/linux/ext2_xattr.h	Fri Apr  5 10:07:28 2002
+++ linux-2.2.20acl/include/linux/ext2_xattr.h	Fri Apr  5 10:06:10 2002
@@ -19,6 +19,8 @@
 /* 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
 
 struct ext2_xattr_header {
 	__u32	h_magic;	/* magic number for identification */
diff -Nur linux-2.2.20ea/include/linux/ext3_acl.h linux-2.2.20acl/include/linux/ext3_acl.h
--- linux-2.2.20ea/include/linux/ext3_acl.h	Thu Jan  1 01:00:00 1970
+++ linux-2.2.20acl/include/linux/ext3_acl.h	Fri Apr  5 10:06:10 2002
@@ -0,0 +1,107 @@
+/*
+  File: linux/ext3_acl.h
+
+  (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+*/
+
+#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 posix_acl_t *ext3_get_acl (struct inode *, int);
+extern int ext3_set_acl (struct inode *, int, posix_acl_t *);
+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;
+	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 -Nur linux-2.2.20ea/include/linux/ext3_xattr.h linux-2.2.20acl/include/linux/ext3_xattr.h
--- linux-2.2.20ea/include/linux/ext3_xattr.h	Fri Apr  5 10:07:33 2002
+++ linux-2.2.20acl/include/linux/ext3_xattr.h	Fri Apr  5 10:06:10 2002
@@ -19,6 +19,8 @@
 /* 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
 
 struct ext3_xattr_header {
 	__u32	h_magic;	/* magic number for identification */
diff -Nur linux-2.2.20ea/include/linux/fs.h linux-2.2.20acl/include/linux/fs.h
--- linux-2.2.20ea/include/linux/fs.h	Sun Mar 24 23:49:51 2002
+++ linux-2.2.20acl/include/linux/fs.h	Wed Apr 24 10:29:29 2002
@@ -24,6 +24,8 @@
 #include <asm/cache.h>
 #include <linux/stddef.h>	/* just in case the #define NULL previously in here was needed */
 
+#include <linux/posix_acl.h>
+
 struct poll_table_struct;
 
 
@@ -108,12 +110,14 @@
  * These are the super block s_xattr_flags
  */
 #define XATTR_MNT_FLAG_USER		1	/* Extended user attributes */
+#define XATTR_MNT_FLAG_POSIX_ACL	2	/* Access Control Lists */
 
 #define __IS_XATTR_FLG(inode,flg) \
 	((inode)->i_sb && \
 	 (inode)->i_sb->s_xattr_flags & (XATTR_MNT_FLAG_ ## flg))
 
 #define IS_XATTR_USER(inode)	__IS_XATTR_FLG(inode, USER)
+#define IS_POSIX_ACL(inode)	__IS_XATTR_FLG(inode, POSIX_ACL)
 /*
  * Flags that can be altered by MS_REMOUNT
  */
@@ -660,6 +664,8 @@
 	ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
 	ssize_t (*listxattr) (struct dentry *, char *, size_t);
 	int (*removexattr) (struct dentry *, const char *);
+	posix_acl_t *(*get_posix_acl) (struct inode *, int);
+	int (*set_posix_acl) (struct inode *, int, posix_acl_t *);
 };
 
 struct super_operations {
diff -Nur linux-2.2.20ea/include/linux/nfsd/nfsd.h linux-2.2.20acl/include/linux/nfsd/nfsd.h
--- linux-2.2.20ea/include/linux/nfsd/nfsd.h	Sun Mar 24 23:52:34 2002
+++ linux-2.2.20acl/include/linux/nfsd/nfsd.h	Fri May 17 15:34:24 2002
@@ -14,6 +14,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>
@@ -129,6 +130,15 @@
 int		nfsd_notify_change(struct inode *, struct iattr *);
 int		nfsd_permission(struct svc_export *, struct dentry *, int);
 
+#ifdef CONFIG_FS_POSIX_ACL
+posix_acl_t *	nfsd_get_posix_acl(struct svc_fh *, int);
+#else
+static inline posix_acl_t *
+nfsd_get_posix_acl(struct svc_fh *fhp, int acl_type) {
+	return 0;
+}
+#endif
+
 /* nfsd/nfsctl.c */
 void		nfsd_modcount(struct inode *, int);
 
@@ -185,6 +195,12 @@
  * Time of server startup
  */
 extern struct timeval	nfssvc_boot;
+
+/* nfs_permission_mode module/kernel parameter */
+#define NFS_FILE_MODE_PERMISSION_BITS_UNCHANGED 0
+#define NFS_FILE_MODE_PERMISSION_BITS_SECURE 1
+
+extern int nfs_permission_mode;
 
 /*
  * The number of nfsd threads.
diff -Nur linux-2.2.20ea/include/linux/posix_acl.h linux-2.2.20acl/include/linux/posix_acl.h
--- linux-2.2.20ea/include/linux/posix_acl.h	Thu Jan  1 01:00:00 1970
+++ linux-2.2.20acl/include/linux/posix_acl.h	Wed Apr 24 10:10:59 2002
@@ -0,0 +1,69 @@
+/*
+  File: linux/posix_acl.h
+
+  (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+*/
+
+
+#ifndef __LINUX_POSIX_ACL_H
+#define __LINUX_POSIX_ACL_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 posix_acl_entry_t */
+#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)
+
+#ifdef __KERNEL__
+
+typedef struct {
+	short			e_tag;
+	unsigned short		e_perm;
+	unsigned int		e_id;
+} posix_acl_entry_t;
+
+typedef struct {
+	atomic_t		a_refcount;
+	unsigned int		a_count;
+	posix_acl_entry_t	a_entries[0];
+} posix_acl_t;
+
+#define FOREACH_ACL_ENTRY(pa, acl, pe) \
+	for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; pa<pe; pa++)
+
+/* pxacl.c */
+
+extern posix_acl_t *posix_acl_alloc(int);
+extern posix_acl_t *posix_acl_dup(posix_acl_t *);
+extern posix_acl_t *posix_acl_clone(const posix_acl_t *);
+extern void posix_acl_release(posix_acl_t *);
+extern int posix_acl_valid(const posix_acl_t *);
+extern int posix_acl_permission(struct inode *, const posix_acl_t *, int);
+extern posix_acl_t *posix_acl_from_mode(mode_t);
+extern int posix_acl_equiv_mode(const posix_acl_t *, mode_t *);
+extern int posix_acl_create_masq(posix_acl_t *, mode_t *);
+extern int posix_acl_chmod_masq(posix_acl_t *, mode_t);
+extern int posix_acl_masq_nfs_mode(posix_acl_t *, mode_t *);
+
+extern posix_acl_t *get_posix_acl(struct inode *, int);
+extern int set_posix_acl(struct inode *, int, posix_acl_t *);
+#endif  /* __KERNEL__ */
+
+
+#endif  /* __LINUX_POSIX_ACL_H */
diff -Nur linux-2.2.20ea/include/linux/xattr_acl.h linux-2.2.20acl/include/linux/xattr_acl.h
--- linux-2.2.20ea/include/linux/xattr_acl.h	Thu Jan  1 01:00:00 1970
+++ linux-2.2.20acl/include/linux/xattr_acl.h	Wed Apr 24 10:30:40 2002
@@ -0,0 +1,45 @@
+/*
+  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);
+}
+
+#endif /* _LINUX_XATTR_ACL_H */
diff -Nur linux-2.2.20ea/unistd.h linux-2.2.20acl/unistd.h
--- linux-2.2.20ea/unistd.h	Thu Jan  1 01:00:00 1970
+++ linux-2.2.20acl/unistd.h	Wed Apr 24 11:29:31 2002
@@ -0,0 +1,450 @@
+/* $Id: unistd.h,v 1.28.2.2 2001/01/11 19:12:11 davem Exp $ */
+#ifndef _SPARC64_UNISTD_H
+#define _SPARC64_UNISTD_H
+
+/*
+ * System calls under the Sparc.
+ *
+ * Don't be scared by the ugly clobbers, it is the only way I can
+ * think of right now to force the arguments into fixed registers
+ * before the trap into the system call with gcc 'asm' statements.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * SunOS compatibility based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ */
+
+#define __NR_exit                 1 /* Common                                      */
+#define __NR_fork                 2 /* Common                                      */
+#define __NR_read                 3 /* Common                                      */
+#define __NR_write                4 /* Common                                      */
+#define __NR_open                 5 /* Common                                      */
+#define __NR_close                6 /* Common                                      */
+#define __NR_wait4                7 /* Common                                      */
+#define __NR_creat                8 /* Common                                      */
+#define __NR_link                 9 /* Common                                      */
+#define __NR_unlink              10 /* Common                                      */
+#define __NR_execv               11 /* SunOS Specific                              */
+#define __NR_chdir               12 /* Common                                      */
+#define __NR_chown		 13 /* Common					   */
+#define __NR_mknod               14 /* Common                                      */
+#define __NR_chmod               15 /* Common                                      */
+#define __NR_lchown              16 /* Common                                      */
+#define __NR_brk                 17 /* Common                                      */
+#define __NR_perfctr             18 /* Performance counter operations              */
+#define __NR_lseek               19 /* Common                                      */
+#define __NR_getpid              20 /* Common                                      */
+#define __NR_capget		 21 /* Linux Specific				   */
+#define __NR_capset		 22 /* Linux Specific				   */
+#define __NR_setuid              23 /* Implemented via setreuid in SunOS           */
+#define __NR_getuid              24 /* Common                                      */
+/* #define __NR_time alias	 25    ENOSYS under SunOS			   */
+#define __NR_ptrace              26 /* Common                                      */
+#define __NR_alarm               27 /* Implemented via setitimer in SunOS          */
+#define __NR_sigaltstack	 28 /* Common					   */
+#define __NR_pause               29 /* Is sigblock(0)->sigpause() in SunOS         */
+#define __NR_utime               30 /* Implemented via utimes() under SunOS        */
+/* #define __NR_stty             31    Implemented via ioctl() under SunOS         */
+/* #define __NR_gtty             32    Implemented via ioctl() under SunOS         */
+#define __NR_access              33 /* Common                                      */
+#define __NR_nice                34 /* Implemented via get/setpriority() in SunOS  */
+/* #define __NR_ftime            35    Implemented via gettimeofday() in SunOS     */
+#define __NR_sync                36 /* Common                                      */
+#define __NR_kill                37 /* Common                                      */
+#define __NR_stat                38 /* Common                                      */
+#define __NR_sendfile		 39 /* Linux Specific				   */
+#define __NR_lstat               40 /* Common                                      */
+#define __NR_dup                 41 /* Common                                      */
+#define __NR_pipe                42 /* Common                                      */
+#define __NR_times               43 /* Implemented via getrusage() in SunOS        */
+/* #define __NR_getuid32         44    Linux Sparc32 Specific                      */
+#define __NR_umount2             45 /* Linux Specific                              */
+#define __NR_setgid              46 /* Implemented via setregid() in SunOS         */
+#define __NR_getgid              47 /* Common                                      */
+#define __NR_signal              48 /* Implemented via sigvec() in SunOS           */
+#define __NR_geteuid             49 /* SunOS calls getuid()                        */
+#define __NR_getegid             50 /* SunOS calls getgid()                        */
+#define __NR_acct                51 /* Common                                      */
+#define __NR_memory_ordering	 52 /* Linux Specific				   */
+/* #define __NR_mctl             53    SunOS specific                              */
+#define __NR_ioctl               54 /* Common                                      */
+#define __NR_reboot              55 /* Common                                      */
+/* #define __NR_ni_syscall       56    ENOSYS under SunOS                          */
+#define __NR_symlink             57 /* Common                                      */
+#define __NR_readlink            58 /* Common                                      */
+#define __NR_execve              59 /* Common                                      */
+#define __NR_umask               60 /* Common                                      */
+#define __NR_chroot              61 /* Common                                      */
+#define __NR_fstat               62 /* Common                                      */
+/* #define __NR_ni_syscall       63    ENOSYS under SunOS                          */
+#define __NR_getpagesize         64 /* Common                                      */
+#define __NR_msync               65 /* Common in newer 1.3.x revs...               */
+#define __NR_vfork               66 /* Common                                      */
+#define __NR_pread               67 /* Linux Specific                              */
+#define __NR_pwrite              68 /* Linux Specific                              */
+/* #define __NR_sbrk             69    SunOS Specific                              */
+/* #define __NR_sstk             70    SunOS Specific                              */
+#define __NR_mmap                71 /* Common                                      */
+/* #define __NR_vadvise          72    SunOS Specific                              */
+#define __NR_munmap              73 /* Common                                      */
+#define __NR_mprotect            74 /* Common                                      */
+/* #define __NR_madvise          75    SunOS Specific                              */
+#define __NR_vhangup             76 /* Common                                      */
+/* #define __NR_ni_syscall       77    ENOSYS under SunOS                          */
+/* #define __NR_mincore          78    SunOS Specific                              */
+#define __NR_getgroups           79 /* Common                                      */
+#define __NR_setgroups           80 /* Common                                      */
+#define __NR_getpgrp             81 /* Common                                      */
+/* #define __NR_setpgrp          82    setpgid, same difference...                 */
+#define __NR_setitimer           83 /* Common                                      */
+/* #define __NR_ni_syscall       84    ENOSYS under SunOS                          */
+#define __NR_swapon              85 /* Common                                      */
+#define __NR_getitimer           86 /* Common                                      */
+/* #define __NR_gethostname      87    SunOS Specific                              */
+#define __NR_sethostname         88 /* Common                                      */
+/* #define __NR_getdtablesize    89    SunOS Specific                              */
+#define __NR_dup2                90 /* Common                                      */
+/* #define __NR_getdopt          91    SunOS Specific                              */
+#define __NR_fcntl               92 /* Common                                      */
+#define __NR_select              93 /* Common                                      */
+/* #define __NR_setdopt          94    SunOS Specific                              */
+#define __NR_fsync               95 /* Common                                      */
+#define __NR_setpriority         96 /* Common                                      */
+#define __NR_socket              97 /* Common                                      */
+#define __NR_connect             98 /* Common                                      */
+#define __NR_accept              99 /* Common                                      */
+#define __NR_getpriority        100 /* Common                                      */
+#define __NR_rt_sigreturn       101 /* Linux Specific                              */
+#define __NR_rt_sigaction       102 /* Linux Specific                              */
+#define __NR_rt_sigprocmask     103 /* Linux Specific                              */
+#define __NR_rt_sigpending      104 /* Linux Specific                              */
+#define __NR_rt_sigtimedwait    105 /* Linux Specific                              */
+#define __NR_rt_sigqueueinfo    106 /* Linux Specific                              */
+#define __NR_rt_sigsuspend      107 /* Linux Specific                              */
+/* #define __NR_sigvec          108    SunOS Specific                              */
+/* #define __NR_sigblock        109    SunOS Specific                              */
+/* #define __NR_sigsetmask      110    SunOS Specific                              */
+/* #define __NR_sigpause        111    SunOS Specific                              */
+/* #define __NR_sigstack        112    SunOS Specific                              */
+#define __NR_recvmsg            113 /* Common                                      */
+#define __NR_sendmsg            114 /* Common                                      */
+/* #define __NR_vtrace          115    SunOS Specific                              */
+#define __NR_gettimeofday       116 /* Common                                      */
+#define __NR_getrusage          117 /* Common                                      */
+#define __NR_getsockopt         118 /* Common                                      */
+#define __NR_getcwd		119 /* Linux Specific				   */
+#define __NR_readv              120 /* Common                                      */
+#define __NR_writev             121 /* Common                                      */
+#define __NR_settimeofday       122 /* Common                                      */
+#define __NR_fchown             123 /* Common                                      */
+#define __NR_fchmod             124 /* Common                                      */
+#define __NR_recvfrom           125 /* Common                                      */
+#define __NR_setreuid           126 /* Common                                      */
+#define __NR_setregid           127 /* Common                                      */
+#define __NR_rename             128 /* Common                                      */
+#define __NR_truncate           129 /* Common                                      */
+#define __NR_ftruncate          130 /* Common                                      */
+#define __NR_flock              131 /* Common                                      */
+/* #define __NR_ni_syscall      132    ENOSYS under SunOS                          */
+#define __NR_sendto             133 /* Common                                      */
+#define __NR_shutdown           134 /* Common                                      */
+#define __NR_socketpair         135 /* Common                                      */
+#define __NR_mkdir              136 /* Common                                      */
+#define __NR_rmdir              137 /* Common                                      */
+#define __NR_utimes             138 /* SunOS Specific                              */
+/* #define __NR_ni_syscall      139    ENOSYS under SunOS                          */
+/* #define __NR_adjtime         140    SunOS Specific                              */
+#define __NR_getpeername        141 /* Common                                      */
+/* #define __NR_gethostid       142    SunOS Specific                              */
+/* #define __NR_ni_syscall      143    ENOSYS under SunOS                          */
+#define __NR_getrlimit          144 /* Common                                      */
+#define __NR_setrlimit          145 /* Common                                      */
+/* #define __NR_killpg          146    SunOS Specific                              */
+#define __NR_prctl		147 /* ENOSYS under SunOS                          */
+#define __NR_pciconfig_read	148 /* ENOSYS under SunOS                          */
+#define __NR_pciconfig_write	149 /* ENOSYS under SunOS                          */
+#define __NR_getsockname        150 /* Common                                      */
+/* #define __NR_getmsg          151    SunOS Specific                              */
+/* #define __NR_putmsg          152    SunOS Specific                              */
+#define __NR_poll               153 /* Common                                      */
+/* #define __NR_getdents64      154    Linux Sparc32 Specific                      */
+/* #define __NR_fcntl64         155    Linux Sparc32 Specific                      */
+/* #define __NR_getdirentries   156    SunOS Specific                              */
+#define __NR_statfs             157 /* Common                                      */
+#define __NR_fstatfs            158 /* Common                                      */
+#define __NR_umount             159 /* Common                                      */
+/* #define __NR_async_daemon    160    SunOS Specific                              */
+/* #define __NR_getfh           161    SunOS Specific                              */
+#define __NR_getdomainname      162 /* SunOS Specific                              */
+#define __NR_setdomainname      163 /* Common                                      */
+#define __NR_utrap_install	164 /* SYSV ABI/v9 required			   */
+#define __NR_quotactl           165 /* Common                                      */
+/* #define __NR_exportfs        166    SunOS Specific                              */
+#define __NR_mount              167 /* Common                                      */
+#define __NR_ustat              168 /* Common                                      */
+ #define __NR_setxattr          169 /* SunOS Specific                              */
+#define __NR_lsetxattr          170 /* SunOS Specific                              */
+#define __NR_fsetxattr          171 /* SunOS Specific                              */
+#define __NR_getxattr           172 /* SunOS Specific                              */
+#define __NR_lgetxattr          173 /* SunOS Specific                              */
+#define __NR_getdents           174 /* Common                                      */
+#define __NR_setsid             175 /* Common                                      */
+#define __NR_fchdir             176 /* Common                                      */
+#define __NR_fgetxattr          177 /* SunOS Specific                              */
+#define __NR_listxattr          178 /* SunOS Specific                              */
+#define __NR_llistxattr         179 /* SunOS Specific                              */
+#define __NR_flistxattr         180 /* SunOS Specific                              */
+#define __NR_removexattr        181 /* SunOS Specific                              */
+#define __NR_lremovexattr       182 /* SunOS Specific                              */
+#define __NR_sigpending         183 /* Common                                      */
+#define __NR_query_module	184 /* Linux Specific				   */
+#define __NR_setpgid            185 /* Common                                      */
+#define __NR_fremovexattr       186 /* SunOS Specific                              */
+/* #define __NR_fpathconf       187    SunOS Specific                              */
+/* #define __NR_sysconf         188    SunOS Specific                              */
+#define __NR_uname              189 /* Linux Specific                              */
+#define __NR_init_module        190 /* Linux Specific                              */
+#define __NR_personality        191 /* Linux Specific                              */
+/* #define __NR_prof            192    Linux Specific                              */
+/* #define __NR_break           193    Linux Specific                              */
+/* #define __NR_lock            194    Linux Specific                              */
+/* #define __NR_mpx             195    Linux Specific                              */
+/* #define __NR_ulimit          196    Linux Specific                              */
+#define __NR_getppid            197 /* Linux Specific                              */
+#define __NR_sigaction          198 /* Linux Specific                              */
+#define __NR_sgetmask           199 /* Linux Specific                              */
+#define __NR_ssetmask           200 /* Linux Specific                              */
+#define __NR_sigsuspend         201 /* Linux Specific                              */
+#define __NR_oldlstat           202 /* Linux Specific                              */
+#define __NR_uselib             203 /* Linux Specific                              */
+#define __NR_readdir            204 /* Linux Specific                              */
+/* #define __NR_ioperm          205    Linux Specific - i386 specific, unused      */
+#define __NR_socketcall         206 /* Linux Specific                              */
+#define __NR_syslog             207 /* Linux Specific                              */
+/* #define __NR_olduname        208    Linux Specific                              */
+/* #define __NR_iopl            209    Linux Specific - i386 specific, unused      */
+#define __NR_idle               210 /* Linux Specific                              */
+/* #define __NR_vm86            211    Linux Specific - i386 specific, unused      */
+#define __NR_waitpid            212 /* Linux Specific                              */
+#define __NR_swapoff            213 /* Linux Specific                              */
+#define __NR_sysinfo            214 /* Linux Specific                              */
+#define __NR_ipc                215 /* Linux Specific                              */
+#define __NR_sigreturn          216 /* Linux Specific                              */
+#define __NR_clone              217 /* Linux Specific                              */
+/* #define __NR_modify_ldt      218    Linux Specific - i386 specific, unused      */
+#define __NR_adjtimex           219 /* Linux Specific                              */
+#define __NR_sigprocmask        220 /* Linux Specific                              */
+#define __NR_create_module      221 /* Linux Specific                              */
+#define __NR_delete_module      222 /* Linux Specific                              */
+#define __NR_get_kernel_syms    223 /* Linux Specific                              */
+#define __NR_getpgid            224 /* Linux Specific                              */
+#define __NR_bdflush            225 /* Linux Specific                              */
+#define __NR_sysfs              226 /* Linux Specific                              */
+#define __NR_afs_syscall        227 /* Linux Specific                              */
+#define __NR_setfsuid           228 /* Linux Specific                              */
+#define __NR_setfsgid           229 /* Linux Specific                              */
+#define __NR__newselect         230 /* Linux Specific                              */
+#define __NR_time               231 /* Linux Specific                              */
+/* #define __NR_oldstat         232    Linux Specific                              */
+#define __NR_stime              233 /* Linux Specific                              */
+/* #define __NR_oldfstat        234    Linux Specific                              */
+/* #define __NR_phys            235    Linux Specific                              */
+#define __NR__llseek            236 /* Linux Specific                              */
+#define __NR_mlock              237
+#define __NR_munlock            238
+#define __NR_mlockall           239
+#define __NR_munlockall         240
+#define __NR_sched_setparam     241
+#define __NR_sched_getparam     242
+#define __NR_sched_setscheduler 243
+#define __NR_sched_getscheduler 244
+#define __NR_sched_yield        245
+#define __NR_sched_get_priority_max 246
+#define __NR_sched_get_priority_min 247
+#define __NR_sched_rr_get_interval  248
+#define __NR_nanosleep          249
+#define __NR_mremap             250
+#define __NR__sysctl            251
+#define __NR_getsid             252
+#define __NR_fdatasync          253
+#define __NR_nfsservctl         254
+#define __NR_aplib              255
+
+#define _syscall0(type,name) \
+type name(void) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "t 0x6d\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
+		      : "=r" (__res)\
+		      : "0" (__NR_##name) \
+		      : "g1", "o0", "cc"); \
+if (__res >= 0) \
+    return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall1(type,name,type1,arg1) \
+type name(type1 arg1) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "t 0x6d\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)) \
+		      : "0" (__NR_##name),"1" ((long)(arg1)) \
+		      : "g1", "o0", "cc"); \
+if (__res >= 0) \
+	return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+type name(type1 arg1,type2 arg2) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "t 0x6d\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)) \
+		      : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)) \
+		      : "g1", "o0", "o1", "cc"); \
+if (__res >= 0) \
+	return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name(type1 arg1,type2 arg2,type3 arg3) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "mov %3, %%o2\n\t" \
+		      "t 0x6d\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
+		        "=r" ((long)(arg3)) \
+		      : "0" (__NR_##name), "1" ((long)(arg1)), "2" ((long)(arg2)), \
+		        "3" ((long)(arg3)) \
+		      : "g1", "o0", "o1", "o2", "cc"); \
+if (__res>=0) \
+	return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+		      "mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "mov %3, %%o2\n\t" \
+		      "mov %4, %%o3\n\t" \
+		      "t 0x6d\n\t" \
+		      "sub %%g0,%%o0, %0\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
+		      : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
+		        "=r" ((long)(arg3)), "=r" ((long)(arg4)) \
+		      : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)), \
+		        "3" ((long)(arg3)),"4" ((long)(arg4)) \
+		      : "g1", "o0", "o1", "o2", "o3", "cc"); \
+if (__res>=0) \
+	return (type) __res; \
+errno = -__res; \
+return -1; \
+} 
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+	  type5,arg5) \
+type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
+{ \
+      long __res; \
+\
+__asm__ __volatile__ ("mov %1, %%o0\n\t" \
+		      "mov %2, %%o1\n\t" \
+		      "mov %3, %%o2\n\t" \
+		      "mov %4, %%o3\n\t" \
+		      "mov %5, %%o4\n\t" \
+		      "mov %6, %%g1\n\t" \
+		      "t 0x6d\n\t" \
+		      "sub %%g0, %%o0, %0\n\t" \
+		      "movcc %%xcc, %%o0, %0\n\t" \
+		      : "=r" (__res) \
+		      : "r" ((long)(arg1)),"r" ((long)(arg2)), \
+		        "r" ((long)(arg3)),"r" ((long)(arg4)),"r" ((long)(arg5)), \
+		        "i" (__NR_##name)  \
+		      : "g1", "o0", "o1", "o2", "o3", "o4", "cc"); \
+if (__res>=0) \
+	return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+#ifdef __KERNEL_SYSCALLS__
+
+/*
+ * we need this inline - forking from kernel space will result
+ * in NO COPY ON WRITE (!!!), until an execve is executed. This
+ * is no problem, but for the stack. This is handled by not letting
+ * main() use the stack at all after fork(). Thus, no function
+ * calls - which means inline code for fork too, as otherwise we
+ * would use the stack upon exit from 'fork()'.
+ *
+ * Actually only pause and fork are needed inline, so that there
+ * won't be any messing with the stack from main(), but we define
+ * some others too.
+ */
+#define __NR__exit __NR_exit
+static __inline__ _syscall0(int,idle)
+static __inline__ _syscall0(int,pause)
+static __inline__ _syscall0(int,sync)
+static __inline__ _syscall0(pid_t,setsid)
+static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
+static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static __inline__ _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
+static __inline__ _syscall1(int,dup,int,fd)
+static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp)
+static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode)
+static __inline__ _syscall1(int,close,int,fd)
+static __inline__ _syscall1(int,_exit,int,exitcode)
+static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static __inline__ _syscall1(int,delete_module,const char *,name)
+
+static __inline__ pid_t wait(int * wait_stat)
+{
+	return waitpid(-1,wait_stat,0);
+}
+
+extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+
+#endif /* __KERNEL_SYSCALLS__ */
+
+#ifdef __KERNEL__
+/* sysconf options, for SunOS compatibility */
+#define   _SC_ARG_MAX             1
+#define   _SC_CHILD_MAX           2
+#define   _SC_CLK_TCK             3
+#define   _SC_NGROUPS_MAX         4
+#define   _SC_OPEN_MAX            5
+#define   _SC_JOB_CONTROL         6
+#define   _SC_SAVED_IDS           7
+#define   _SC_VERSION             8
+#endif
+
+#endif /* _SPARC64_UNISTD_H */
diff -Nur linux-2.2.20ea/xattr.c linux-2.2.20acl/xattr.c
--- linux-2.2.20ea/xattr.c	Wed Mar 20 01:56:10 2002
+++ linux-2.2.20acl/xattr.c	Thu Jan  1 01:00:00 1970
@@ -1,1259 +0,0 @@
-/*
- * linux/fs/ext2/xattr.c
- *
- * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
- *
- * 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 that allocated outside of
- * any inode. The i_file_acl field is them 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.
- *
- *
- * 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
- * ----------------
- * The VFS already holds the BKL and the inode->i_sem semaphore when any of
- * the xattr inode operations is called, so we are guaranteed that only one
- * processes accesses extended attributes of an inode at any time.
- *
- * For writing we also grab the ext2_xattr_sem semaphore. This ensures that
- * only a single process is modifying an extended attribute block, even
- * if the block is shared among inodes.
- *
- * Note for porting to 2.5
- * -----------------------
- * The BLK will no longer be held in the xattr inode operations.
- */
-
-#include <linux/fs.h>
-#include <linux/locks.h>
-#include <linux/slab.h>
-#include <linux/ext2_fs.h>
-#include <linux/ext2_xattr.h>
-#include <linux/mbcache.h>
-#include <linux/quotaops.h>
-#include <asm/semaphore.h>
-#include <linux/compatmac.h>
-
-#include <linux/semaphore_debug.h>
-
-#define EXT2_EA_USER "user."
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
-# define mark_buffer_dirty(bh) mark_buffer_dirty(bh, 1)
-#endif
-
-#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
-
-/*
- * If a file system does not share extended attributes among inodes,
- * we should not need the ext2_xattr_sem semaphore. However, the
- * filesystem may still contain shared blocks, so we always take
- * the lock.
- */
-
-DECLARE_MUTEX(ext2_xattr_sem);
-
-static inline void
-ext2_xattr_lock(void)
-{
-	down(&ext2_xattr_sem);
-}
-
-static inline void
-ext2_xattr_unlock(void)
-{
-	up(&ext2_xattr_sem);
-}
-
-static inline int
-ext2_xattr_new_block(struct inode *inode, int * errp, int force)
-{
-	struct super_block *sb = inode->i_sb;
-	int goal = le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block) +
-		EXT2_I(inode)->i_block_group * EXT2_BLOCKS_PER_GROUP(sb);
-
-	/* How can we enforce the allocation? */
-	int block = ext2_new_block(inode, goal, 0, 0, errp);
-#ifdef OLD_QUOTAS
-	if (!*errp)
-		inode->i_blocks += inode->i_sb->s_blocksize >> 9;
-#endif
-	return block;
-}
-
-/*
- * Force the allocation of a block in quotas: We know that we will
- * later free a block, so effectively the quotas won't change.
- */
-static inline int
-ext2_xattr_quota_alloc(struct inode *inode, int force)
-{
-	/* How can we enforce the allocation? */
-#ifdef OLD_QUOTAS
-	int error = DQUOT_ALLOC_BLOCK(inode->i_sb, inode, 1);
-	if (!error)
-		inode->i_blocks += inode->i_sb->s_blocksize >> 9;
-#else
-	int error = DQUOT_ALLOC_BLOCK(inode, 1);
-#endif
-	return error;
-}
-
-#ifdef OLD_QUOTAS
-
-static inline void
-ext2_xattr_quota_free(struct inode *inode)
-{
-	DQUOT_FREE_BLOCK(inode->i_sb, inode, 1);
-	inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
-}
-
-void
-ext2_xattr_free_block(struct inode * inode, unsigned long block)
-{
-	ext2_free_blocks(inode, block, 1);
-	inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
-}
-
-#else
-# define ext2_xattr_quota_free(inode) \
-	DQUOT_FREE_BLOCK(inode, 1)
-# define ext2_xattr_free_block(inode, block) \
-	ext2_free_blocks(inode, block, 1)
-#endif
-
-
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-static int
-ext2_get_user_xattr(struct inode *inode, const char *name,
-		    void *buffer, size_t size)
-{
-	int error;
-
-	if (!IS_XATTR_USER(inode))
-		return -ENOTSUP;
-	error = permission(inode, MAY_READ);
-	if (error)
-		return error;
-
-	return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name, buffer, size);
-}
-
-static int
-ext2_set_user_xattr(struct inode *inode, const char *name,
-		    void *value, size_t size, int flags)
-{
-	int error;
-
-	if (!IS_XATTR_USER(inode))
-		return -ENOTSUP;
-	error = permission(inode, MAY_WRITE);
-	if (error)
-		return error;
-  
-	return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name,
-		value, size, flags);
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
-
-static inline struct buffer_head *
-sb_bread(struct super_block *sb, int block)
-{
-	return bread(sb->s_dev, block, sb->s_blocksize);
-}
-
-static inline struct buffer_head *
-sb_getblk(struct super_block *sb, int block)
-{
-	return getblk(sb->s_dev, block, sb->s_blocksize);
-}
-
-#endif
-
-/*
- * Decode the extended attribute name, and translate it into
- * the name index + name suffix.
- */
-static int
-ext2_xattr_name(const char **name)
-{
-	if (*name) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-		if (!strncmp(*name, EXT2_EA_USER, sizeof(EXT2_EA_USER)-1)) {
-			if (!strcmp(*name+sizeof(EXT2_EA_USER)-1, ""))
-				return -EINVAL;
-			*name += 5;
-			return EXT2_XATTR_INDEX_USER;
-		} else
-#endif
-		return -ENOTSUP;
-	}
-	return 0;
-}
-
-/*
- * Inode operation getxattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-ssize_t
-ext2_getxattr(struct dentry *dentry, const char *name,
-	      void *buffer, size_t size)
-{
-	struct inode *inode = dentry->d_inode;
-	int name_index;
-
-	assert_down(&dentry->d_inode->i_sem);
-
-	name_index = ext2_xattr_name(&name);
-	switch(name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-		case EXT2_XATTR_INDEX_USER:
-			return ext2_get_user_xattr(inode, name, buffer, size);
-#endif
-		default:
-			if (name_index >= 0)
-				name_index = -EINVAL;
-			return name_index;
-	}
-}
-
-/*
- * Inode operation listxattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-ssize_t
-ext2_listxattr(struct dentry *dentry, char *buffer, size_t size)
-{
-	assert_down(&dentry->d_inode->i_sem);
-
-	return ext2_xattr_list(dentry->d_inode, buffer, size);
-}
-
-/*
- * Inode operation setxattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-int
-ext2_setxattr(struct dentry *dentry, const char *name,
-	      void *value, size_t size, int flags)
-{
-	struct inode *inode = dentry->d_inode;
-	int name_index;
-
-	assert_down(&dentry->d_inode->i_sem);
-
-	if (size == 0)
-		value = "";  /* empty EA, do not remove */
-
-	name_index = ext2_xattr_name(&name);
-	switch(name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-		case EXT2_XATTR_INDEX_USER:
-			return ext2_set_user_xattr(inode, name,
-				value, size, flags);
-#endif
-		default:
-			if (name_index >= 0)
-				name_index = -EINVAL;
-			return name_index;
-	}
-}
-
-/*
- * Inode operation removexattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-int
-ext2_removexattr(struct dentry *dentry, const char *name)
-{
-	struct inode *inode = dentry->d_inode;
-	int name_index;
-
-	assert_down(&dentry->d_inode->i_sem);
-
-	name_index = ext2_xattr_name(&name);
-	switch(name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-		case EXT2_XATTR_INDEX_USER:
-			return ext2_set_user_xattr(inode, name,
-				NULL, 0, XATTR_REPLACE);
-#endif
-		default:
-			if (name_index >= 0)
-				name_index = -EINVAL;
-			return name_index;
-	}
-}
-
-/*
- * 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;
-	unsigned int block, size;
-	char *end;
-	int name_len, error;
-
-	ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
-		  name_index, name, buffer, (long)buffer_size);
-
-	if (!IS_XATTR(inode))
-		return -ENOTSUP;
-	if (name == NULL)
-		return -EINVAL;
-	if (!EXT2_I(inode)->i_file_acl)
-		return -ENOATTR;
-	block = EXT2_I(inode)->i_file_acl;
-	ea_idebug(inode, "reading block %d", block);
-	bh = sb_bread(inode->i_sb, block);
-	if (!bh)
-		return -EIO;
-	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, block);
-		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 = -ENOATTR;
-	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);
-
-	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;
-	unsigned int block, size = 0;
-	char *buf, *end;
-	int error;
-
-	ea_idebug(inode, "buffer=%p, buffer_size=%ld",
-		  buffer, (long)buffer_size);
-
-	if (!IS_XATTR(inode))
-		return -ENOTSUP;
-	if (!EXT2_I(inode)->i_file_acl)
-		return 0;
-	block = EXT2_I(inode)->i_file_acl;
-	ea_idebug(inode, "reading block %d", block);
-	bh = sb_bread(inode->i_sb, block);
-	if (!bh)
-		return -EIO;
-	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, block);
-		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_entry *next =
-			EXT2_XATTR_NEXT(entry);
-		if ((char *)next >= end)
-			goto bad_block;
-		switch(entry->e_name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-			case EXT2_XATTR_INDEX_USER:
-				if (!IS_XATTR_USER(inode))
-					continue;
-				size += 5 + entry->e_name_len;
-				break;
-#endif
-			default:
-				/* skip unrecognized name index */
-				continue;
-		}
-		size++;
-	}
-
-	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)) {
-		char *nsp = "", *name;
-		int nsp_len, name_len;
-
-		switch(entry->e_name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-			case EXT2_XATTR_INDEX_USER:
-				if (!IS_XATTR_USER(inode))
-					continue;
-				nsp = EXT2_EA_USER;
-				nsp_len = sizeof(EXT2_EA_USER)-1;
-				name = entry->e_name;
-				name_len = entry->e_name_len;
-				break;
-#endif
-			default:
-				/* skip unrecognized name index */
-				continue;
-		}
-
-		memcpy(buf, nsp, nsp_len);
-		buf += nsp_len;
-		memcpy(buf, name, name_len);
-		buf += name_len;
-		memset(buf++, '\0', 1);
-	}
-	error = size;
-
-cleanup:
-	brelse(bh);
-
-	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);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
-	EXT2_SB(sb)->s_feature_compat |= EXT2_FEATURE_COMPAT_EXT_ATTR;
-#endif
-	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,
-	       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;
-	unsigned int name_len;
-	int min_offs = sb->s_blocksize, not_found = 1, free, 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_XATTR(inode))
-		return -ENOTSUP;
-	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;
-	ext2_xattr_lock();
-
-	if (EXT2_I(inode)->i_file_acl) {
-		/* The inode already has an extended attribute block. */
-		int block = EXT2_I(inode)->i_file_acl;
-
-		bh = sb_bread(sb, block);
-		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, block);
-			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) {
-				int 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) {
-				int 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 = -ENOATTR;
-		if (flags & XATTR_REPLACE)
-			goto cleanup;
-		error = 0;
-		if (value == NULL)
-			goto cleanup;
-		else
-			free -= EXT2_XATTR_LEN(name_len);
-	} else {
-		/* Request to create an existing attribute? */
-		error = -EEXIST;
-		if (flags & XATTR_CREATE)
-			goto cleanup;
-		if (!here->e_value_block && here->e_value_size) {
-			unsigned int 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_SIZE(value_len);
-	error = -ENOSPC;
-	if (free < 0)
-		goto cleanup;
-
-	/* Here we know that we can set the new attribute. */
-
-	if (header) {
-		if (header->h_refcount == cpu_to_le32(1)) {
-			ea_bdebug(bh, "modifying in-place");
-			ext2_xattr_cache_remove(bh);
-		} else {
-			int offset;
-
-			ea_bdebug(bh, "cloning");
-			header = kmalloc(bh->b_size, GFP_KERNEL);
-			error = -ENOMEM;
-			if (header == NULL)
-				goto cleanup;
-			memcpy(header, HDR(bh), bh->b_size);
-			header->h_refcount = cpu_to_le32(1);
-			offset = (char *)header - bh->b_data;
-			here = ENTRY((char *)here + offset);
-			last = ENTRY((char *)last + offset);
-		}
-	} else {
-		/* Allocate a buffer where we construct the new block. */
-		header = kmalloc(sb->s_blocksize, GFP_KERNEL);
-		error = -ENOMEM;
-		if (header == NULL)
-			goto cleanup;
-		memset(header, '\0', sb->s_blocksize);
-		end = (char *)header + sb->s_blocksize;
-		header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC);
-		header->h_blocks = header->h_refcount = cpu_to_le32(1);
-		last = here = ENTRY(header+1);
-	}
-
-	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 {
-		/* Remove the old value. */
-		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));
-			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 this attribute. */
-			if (EXT2_XATTR_NEXT(ENTRY(header+1)) == last) {
-				/* This block is now empty. */
-				error = ext2_xattr_set2(inode, bh, NULL);
-				goto cleanup;
-			} else {
-				/* 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);
-		}
-	}
-	ext2_xattr_rehash(header, here);
-
-	error = ext2_xattr_set2(inode, bh, header);
-
-cleanup:
-	brelse(bh);
-	if (!(bh && header == HDR(bh)))
-		kfree(header);
-	ext2_xattr_unlock();
-
-	return error;
-}
-
-/*
- * Second half of ext2_xattr_set(): Update the file system.
- */
-static int
-ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
-		struct ext2_xattr_header *header)
-{
-	struct super_block *sb = inode->i_sb;
-	struct buffer_head *new_bh = NULL;
-	int error;
-
-	if (header) {
-		new_bh = ext2_xattr_cache_find(inode, header);
-		if (new_bh) {
-			/*
-			 * We found an identical block in the cache.
-			 * The old block will be released after updating
-			 * the inode.
-			 */
-			ea_bdebug(old_bh, "reusing block %ld",
-				new_bh->b_blocknr);
-			
-			error = -EDQUOT;
-			if (ext2_xattr_quota_alloc(inode, 1))
-				goto cleanup;
-			
-			HDR(new_bh)->h_refcount = cpu_to_le32(
-				le32_to_cpu(HDR(new_bh)->h_refcount) + 1);
-			ea_bdebug(new_bh, "refcount now=%d",
-				le32_to_cpu(HDR(new_bh)->h_refcount));
-		} else if (old_bh && header == HDR(old_bh)) {
-			/* Keep this block. */
-			new_bh = old_bh;
-			ext2_xattr_cache_insert(new_bh);
-		} else {
-			/* We need to allocate a new block */
-			int force = EXT2_I(inode)->i_file_acl != 0;
-			int block = ext2_xattr_new_block(inode, &error, force);
-			if (error)
-				goto cleanup;
-			ea_idebug(inode, "creating block %d", block);
-
-			new_bh = sb_getblk(sb, block);
-			if (!new_bh) {
-				ext2_xattr_free_block(inode, block);
-				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 not still using it,
-		 * we now release the old block.
-		*/
-		unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount);
-
-		if (refcount == 1) {
-			/* Free the old block. */
-			ea_bdebug(old_bh, "freeing");
-			ext2_xattr_free_block(inode, old_bh->b_blocknr);
-			mark_buffer_clean(old_bh);
-		} else {
-			/* Decrement the refcount only. */
-			refcount--;
-			HDR(old_bh)->h_refcount = cpu_to_le32(refcount);
-			ext2_xattr_quota_free(inode);
-			mark_buffer_dirty(old_bh);
-			ea_bdebug(old_bh, "refcount now=%d", refcount);
-		}
-	}
-
-cleanup:
-	if (old_bh != new_bh)
-		brelse(new_bh);
-
-	return error;
-}
-
-/*
- * ext2_xattr_drop_inode()
- *
- * Free extended attribute resources associated with this inode. This
- * is called immediately before an inode is freed.
- */
-void
-ext2_xattr_drop_inode(struct inode *inode)
-{
-	struct buffer_head *bh;
-	unsigned int block = EXT2_I(inode)->i_file_acl;
-
-	if (!block)
-		return;
-	ext2_xattr_lock();
-
-	bh = sb_bread(inode->i_sb, block);
-	if (!bh) {
-		ext2_error(inode->i_sb, "ext2_xattr_drop_inode",
-			"inode %ld: block %d read error", inode->i_ino, block);
-		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_drop_inode",
-			"inode %ld: bad block %d", inode->i_ino, block);
-		goto cleanup;
-	}
-	ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1);
-	if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
-		ext2_xattr_cache_remove(bh);
-		ext2_xattr_free_block(inode, block);
-		bforget(bh);
-		bh = NULL;
-	} 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);
-		}
-		ext2_xattr_quota_free(inode);
-	}
-	EXT2_I(inode)->i_file_acl = 0;
-
-cleanup:
-	brelse(bh);
-	ext2_xattr_unlock();
-}
-
-/*
- * 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 pointer 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 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);
-			return 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 ext2_xattr_init(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 ext2_xattr_done(void)
-{
-	mb_cache_destroy(ext2_xattr_cache);
-}
-
-#else  /* CONFIG_EXT2_FS_XATTR_SHARING */
-
-int ext2_xattr_init(void)
-{
-	return 0;
-}
-
-void ext2_xattr_done(void)
-{
-}
-
-#endif  /* CONFIG_EXT2_FS_XATTR_SHARING */
