From: Chris Mason <mason@suse.com>

From: jeffm@suse.com

reiserfs acl support


---

 25-akpm/fs/Kconfig                     |   16 
 25-akpm/fs/reiserfs/Makefile           |    4 
 25-akpm/fs/reiserfs/file.c             |    1 
 25-akpm/fs/reiserfs/inode.c            |   23 +
 25-akpm/fs/reiserfs/namei.c            |   33 +
 25-akpm/fs/reiserfs/super.c            |   25 +
 25-akpm/fs/reiserfs/xattr.c            |   82 ++++
 25-akpm/fs/reiserfs/xattr_acl.c        |  552 +++++++++++++++++++++++++++++++++
 25-akpm/fs/reiserfs/xattr_user.c       |   10 
 25-akpm/include/linux/reiserfs_acl.h   |   91 +++++
 25-akpm/include/linux/reiserfs_fs_i.h  |    3 
 25-akpm/include/linux/reiserfs_fs_sb.h |    4 
 25-akpm/include/linux/reiserfs_xattr.h |    7 
 13 files changed, 836 insertions(+), 15 deletions(-)

diff -puN fs/Kconfig~reiserfs-acl-02 fs/Kconfig
--- 25/fs/Kconfig~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/Kconfig	Fri Apr 23 14:36:49 2004
@@ -254,6 +254,18 @@ config REISERFS_FS_XATTR
 
 	  If unsure, say N.
 
+config REISERFS_FS_POSIX_ACL
+	bool "ReiserFS POSIX Access Control Lists"
+	depends on REISERFS_FS_XATTR
+	help
+	  Posix Access Control Lists (ACLs) support permissions for users and
+	  groups beyond the owner/group/world scheme.
+
+	  To learn more about Access Control Lists, visit the Posix ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+
+	  If you don't know what Access Control Lists are, say N
+
 config JFS_FS
 	tristate "JFS filesystem support"
 	select NLS
@@ -292,13 +304,13 @@ config JFS_STATISTICS
 	  to be made available to the user in the /proc/fs/jfs/ directory.
 
 config FS_POSIX_ACL
-# Posix ACL utility routines (for now, only ext2/ext3/jfs)
+# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs)
 #
 # NOTE: you can implement Posix ACLs without these helpers (XFS does).
 # 	Never use this symbol for ifdefs.
 #
 	bool
-	depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL
+	depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL || REISERFS_FS_POSIX_ACL
 	default y
 
 config XFS_FS
diff -puN fs/reiserfs/file.c~reiserfs-acl-02 fs/reiserfs/file.c
--- 25/fs/reiserfs/file.c~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/reiserfs/file.c	Fri Apr 23 14:36:49 2004
@@ -5,6 +5,7 @@
 
 #include <linux/time.h>
 #include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_acl.h>
 #include <linux/reiserfs_xattr.h>
 #include <linux/smp_lock.h>
 #include <asm/uaccess.h>
diff -puN fs/reiserfs/inode.c~reiserfs-acl-02 fs/reiserfs/inode.c
--- 25/fs/reiserfs/inode.c~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/reiserfs/inode.c	Fri Apr 23 14:36:49 2004
@@ -5,6 +5,7 @@
 #include <linux/config.h>
 #include <linux/time.h>
 #include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_acl.h>
 #include <linux/reiserfs_xattr.h>
 #include <linux/smp_lock.h>
 #include <linux/pagemap.h>
@@ -976,6 +977,8 @@ static void init_inode (struct inode * i
     REISERFS_I(inode)->i_prealloc_count = 0;
     REISERFS_I(inode)->i_trans_id = 0;
     REISERFS_I(inode)->i_jl = NULL;
+    REISERFS_I(inode)->i_acl_access = NULL;
+    REISERFS_I(inode)->i_acl_default = NULL;
 
     if (stat_data_v1 (ih)) {
 	struct stat_data_v1 * sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih);
@@ -1637,6 +1640,8 @@ int reiserfs_new_inode (struct reiserfs_
     REISERFS_I(inode)->i_attrs =
 	REISERFS_I(dir)->i_attrs & REISERFS_INHERIT_MASK;
     sd_attrs_to_i_attrs( REISERFS_I(inode) -> i_attrs, inode );
+    REISERFS_I(inode)->i_acl_access = NULL;
+    REISERFS_I(inode)->i_acl_default = NULL;
 
     if (old_format_only (sb))
 	make_le_item_head (&ih, 0, KEY_FORMAT_3_5, SD_OFFSET, TYPE_STAT_DATA, SD_V1_SIZE, MAX_US_INT);
@@ -1721,6 +1726,19 @@ int reiserfs_new_inode (struct reiserfs_
 	goto out_inserted_sd;
     }
 
+    /* XXX CHECK THIS */
+    if (reiserfs_posixacl (inode->i_sb)) {
+        retval = reiserfs_inherit_default_acl (dir, dentry, inode);
+        if (retval) {
+            err = retval;
+            reiserfs_check_path(&path_to_key) ;
+            journal_end(th, th->t_super, th->t_blocks_allocated);
+            goto out_inserted_sd;
+        }
+    } else if (inode->i_sb->s_flags & MS_POSIXACL) {
+        reiserfs_warning ("ACLs aren't enabled in the fs, but vfs thinks they are!\n");
+    }
+
     insert_inode_hash (inode);
     reiserfs_update_sd(th, inode);
     reiserfs_check_path(&path_to_key) ;
@@ -2565,6 +2583,11 @@ int reiserfs_setattr(struct dentry *dent
     }
 
 
+    if (!error && reiserfs_posixacl (inode->i_sb)) {
+        if (attr->ia_valid & ATTR_MODE)
+            error = reiserfs_acl_chmod (inode);
+    }
+
 out:
     reiserfs_write_unlock(inode->i_sb);
     return error ;
diff -puN fs/reiserfs/Makefile~reiserfs-acl-02 fs/reiserfs/Makefile
--- 25/fs/reiserfs/Makefile~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/reiserfs/Makefile	Fri Apr 23 14:36:49 2004
@@ -13,6 +13,10 @@ ifeq ($(CONFIG_REISERFS_FS_XATTR),y)
 reiserfs-objs += xattr.o xattr_user.o
 endif
 
+ifeq ($(CONFIG_REISERFS_FS_POSIX_ACL),y)
+reiserfs-objs += xattr_acl.o
+endif
+
 # gcc -O2 (the kernel default)  is overaggressive on ppc32 when many inline
 # functions are used.  This causes the compiler to advance the stack
 # pointer out of the available stack space, corrupting kernel space,
diff -puN fs/reiserfs/namei.c~reiserfs-acl-02 fs/reiserfs/namei.c
--- 25/fs/reiserfs/namei.c~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/reiserfs/namei.c	Fri Apr 23 14:36:49 2004
@@ -15,6 +15,7 @@
 #include <linux/time.h>
 #include <linux/bitops.h>
 #include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_acl.h>
 #include <linux/reiserfs_xattr.h>
 #include <linux/smp_lock.h>
 
@@ -579,6 +580,7 @@ static int reiserfs_create (struct inode
     struct inode * inode;
     int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 ;
     struct reiserfs_transaction_handle th ;
+    int locked;
 
     if (!(inode = new_inode(dir->i_sb))) {
 	return -ENOMEM ;
@@ -587,9 +589,19 @@ static int reiserfs_create (struct inode
     if (retval)
         return retval;
 
+    locked = reiserfs_cache_default_acl (dir);
+
     reiserfs_write_lock(dir->i_sb);
+
+    if (locked)
+        reiserfs_write_lock_xattrs (dir->i_sb);
+
     journal_begin(&th, dir->i_sb, jbegin_count) ;
     retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode);
+
+    if (locked)
+        reiserfs_write_unlock_xattrs (dir->i_sb);
+
     if (retval) {
         goto out_failed;
     }
@@ -625,6 +637,7 @@ static int reiserfs_mknod (struct inode 
     struct inode * inode;
     struct reiserfs_transaction_handle th ;
     int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; 
+    int locked;
 
     if (!new_valid_dev(rdev))
 	return -EINVAL;
@@ -636,10 +649,20 @@ static int reiserfs_mknod (struct inode 
     if (retval)
         return retval;
 
+    locked = reiserfs_cache_default_acl (dir);
+
     reiserfs_write_lock(dir->i_sb);
+
+    if (locked)
+        reiserfs_write_lock_xattrs (dir->i_sb);
+
     journal_begin(&th, dir->i_sb, jbegin_count) ;
 
     retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode);
+
+    if (locked)
+        reiserfs_write_unlock_xattrs (dir->i_sb);
+
     if (retval) {
         goto out_failed;
     }
@@ -678,6 +701,7 @@ static int reiserfs_mkdir (struct inode 
     struct inode * inode;
     struct reiserfs_transaction_handle th ;
     int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; 
+    int locked;
 
 #ifdef DISPLACE_NEW_PACKING_LOCALITIES
     /* set flag that new packing locality created and new blocks for the content     * of that directory are not displaced yet */
@@ -691,7 +715,11 @@ static int reiserfs_mkdir (struct inode 
     if (retval)
         return retval;
 
+    locked = reiserfs_cache_default_acl (dir);
+
     reiserfs_write_lock(dir->i_sb);
+    if (locked)
+        reiserfs_write_lock_xattrs (dir->i_sb);
     journal_begin(&th, dir->i_sb, jbegin_count) ;
 
     /* inc the link count now, so another writer doesn't overflow it while
@@ -703,6 +731,9 @@ static int reiserfs_mkdir (struct inode 
 				old_format_only (dir->i_sb) ? 
 				EMPTY_DIR_SIZE_V1 : EMPTY_DIR_SIZE,
 				dentry, inode);
+    if (locked)
+        reiserfs_write_unlock_xattrs (dir->i_sb);
+
     if (retval) {
 	dir->i_nlink-- ;
 	goto out_failed;
@@ -945,6 +976,8 @@ static int reiserfs_symlink (struct inod
     memcpy (name, symname, strlen (symname));
     padd_item (name, item_len, strlen (symname));
 
+    /* We would inherit the default ACL here, but symlinks don't get ACLs */
+
     journal_begin(&th, parent_dir->i_sb, jbegin_count) ;
 
     retval = reiserfs_new_inode (&th, parent_dir, mode, name, strlen (symname), 
diff -puN fs/reiserfs/super.c~reiserfs-acl-02 fs/reiserfs/super.c
--- 25/fs/reiserfs/super.c~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/reiserfs/super.c	Fri Apr 23 14:36:49 2004
@@ -17,6 +17,7 @@
 #include <linux/time.h>
 #include <asm/uaccess.h>
 #include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_acl.h>
 #include <linux/reiserfs_xattr.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
@@ -433,6 +434,8 @@ static void init_once(void * foo, kmem_c
 	    SLAB_CTOR_CONSTRUCTOR) {
 		INIT_LIST_HEAD(&ei->i_prealloc_list) ;
 		inode_init_once(&ei->vfs_inode);
+		ei->i_acl_access = NULL;
+		ei->i_acl_default = NULL;
 	}
 }
  
@@ -473,6 +476,22 @@ static void reiserfs_dirty_inode (struct
     reiserfs_write_unlock(inode->i_sb);
 }
 
+static void reiserfs_clear_inode (struct inode *inode)
+{
+    struct posix_acl *acl;
+
+    acl = REISERFS_I(inode)->i_acl_access;
+    if (acl && !IS_ERR (acl))
+        posix_acl_release (acl);
+    REISERFS_I(inode)->i_acl_access = NULL;
+
+    acl = REISERFS_I(inode)->i_acl_default;
+    if (acl && !IS_ERR (acl))
+        posix_acl_release (acl);
+    REISERFS_I(inode)->i_acl_default = NULL;
+}
+
+
 struct super_operations reiserfs_sops = 
 {
   .alloc_inode = reiserfs_alloc_inode,
@@ -480,6 +499,7 @@ struct super_operations reiserfs_sops = 
   .write_inode = reiserfs_write_inode,
   .dirty_inode = reiserfs_dirty_inode,
   .delete_inode = reiserfs_delete_inode,
+  .clear_inode  = reiserfs_clear_inode,
   .put_super = reiserfs_put_super,
   .write_super = reiserfs_write_super,
   .write_super_lockfs = reiserfs_write_super_lockfs,
@@ -682,6 +702,10 @@ static int reiserfs_parse_options (struc
 	{"noattrs", 0, 0, 0, 1<<REISERFS_ATTRS},
 	{"user_xattr", 0, 0, 1<<REISERFS_XATTRS_USER, 0},
 	{"nouser_xattr", 0, 0, 0, 1<<REISERFS_XATTRS_USER},
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+	{"acl", 0, 0, 1<<REISERFS_POSIXACL, 0},
+	{"noacl", 0, 0, 0, 1<<REISERFS_POSIXACL},
+#endif
 	{"nolog", 0, 0, 0, 0}, /* This is unsupported */
 	{"replayonly", 0, 0, 1<<REPLAYONLY, 0},
 	{"block-allocator", 'a', balloc, 0, 0},
@@ -827,6 +851,7 @@ static int reiserfs_remount (struct supe
   safe_mask |= 1 << REISERFS_TEST4;
   safe_mask |= 1 << REISERFS_ATTRS;
   safe_mask |= 1 << REISERFS_XATTRS_USER;
+  safe_mask |= 1 << REISERFS_POSIXACL;
 
   /* Update the bitmask, taking care to keep
    * the bits we're not allowed to change here */
diff -puN /dev/null fs/reiserfs/xattr_acl.c
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/fs/reiserfs/xattr_acl.c	Fri Apr 23 14:36:49 2004
@@ -0,0 +1,552 @@
+#include <linux/fs.h>
+#include <linux/posix_acl.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/errno.h>
+#include <linux/pagemap.h>
+#include <linux/xattr.h>
+#include <linux/xattr_acl.h>
+#include <linux/reiserfs_xattr.h>
+#include <linux/reiserfs_acl.h>
+#include <asm/uaccess.h>
+
+static int
+xattr_set_acl(struct inode *inode, int type, const void *value, size_t size)
+{
+	struct posix_acl *acl;
+	int error;
+
+	if (!reiserfs_posixacl(inode->i_sb))
+		return -EOPNOTSUPP;
+	if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+		return -EPERM;
+
+	if (value) {
+		acl = posix_acl_from_xattr(value, size);
+		if (IS_ERR(acl)) {
+			return PTR_ERR(acl);
+		} else if (acl) {
+			error = posix_acl_valid(acl);
+			if (error)
+				goto release_and_out;
+		}
+	} else
+		acl = NULL;
+
+	error = reiserfs_set_acl (inode, type, acl);
+
+release_and_out:
+	posix_acl_release(acl);
+	return error;
+}
+
+
+static int
+xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
+{
+	struct posix_acl *acl;
+	int error;
+
+	if (!reiserfs_posixacl(inode->i_sb))
+		return -EOPNOTSUPP;
+
+	acl = reiserfs_get_acl (inode, type);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl == NULL)
+		return -ENODATA;
+	error = posix_acl_to_xattr(acl, buffer, size);
+	posix_acl_release(acl);
+
+	return error;
+}
+
+
+/*
+ * Convert from filesystem to in-memory representation.
+ */
+static struct posix_acl *
+posix_acl_from_disk(const void *value, size_t size)
+{
+	const char *end = (char *)value + size;
+	int n, count;
+	struct posix_acl *acl;
+
+	if (!value)
+		return NULL;
+	if (size < sizeof(reiserfs_acl_header))
+		 return ERR_PTR(-EINVAL);
+	if (((reiserfs_acl_header *)value)->a_version !=
+	    cpu_to_le32(REISERFS_ACL_VERSION))
+		return ERR_PTR(-EINVAL);
+	value = (char *)value + sizeof(reiserfs_acl_header);
+	count = reiserfs_acl_count(size);
+	if (count < 0)
+		return ERR_PTR(-EINVAL);
+	if (count == 0)
+		return NULL;
+	acl = posix_acl_alloc(count, GFP_NOFS);
+	if (!acl)
+		return ERR_PTR(-ENOMEM);
+	for (n=0; n < count; n++) {
+		reiserfs_acl_entry *entry =
+			(reiserfs_acl_entry *)value;
+		if ((char *)value + sizeof(reiserfs_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(reiserfs_acl_entry_short);
+				acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
+				break;
+
+			case ACL_USER:
+			case ACL_GROUP:
+				value = (char *)value + sizeof(reiserfs_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 *
+posix_acl_to_disk(const struct posix_acl *acl, size_t *size)
+{
+	reiserfs_acl_header *ext_acl;
+	char *e;
+	int n;
+
+	*size = reiserfs_acl_size(acl->a_count);
+	ext_acl = (reiserfs_acl_header *)kmalloc(sizeof(reiserfs_acl_header) +
+		acl->a_count * sizeof(reiserfs_acl_entry), GFP_NOFS);
+	if (!ext_acl)
+		return ERR_PTR(-ENOMEM);
+	ext_acl->a_version = cpu_to_le32(REISERFS_ACL_VERSION);
+	e = (char *)ext_acl + sizeof(reiserfs_acl_header);
+	for (n=0; n < acl->a_count; n++) {
+		reiserfs_acl_entry *entry = (reiserfs_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(reiserfs_acl_entry);
+				break;
+
+			case ACL_USER_OBJ:
+			case ACL_GROUP_OBJ:
+			case ACL_MASK:
+			case ACL_OTHER:
+				e += sizeof(reiserfs_acl_entry_short);
+				break;
+
+			default:
+				goto fail;
+		}
+	}
+	return (char *)ext_acl;
+
+fail:
+	kfree(ext_acl);
+	return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Inode operation get_posix_acl().
+ *
+ * inode->i_sem: down
+ * BKL held [before 2.5.x]
+ */
+struct posix_acl *
+reiserfs_get_acl(struct inode *inode, int type)
+{
+	char *name, *value;
+	struct posix_acl *acl, **p_acl;
+	size_t size;
+	int retval;
+        struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
+
+        switch (type) {
+            case ACL_TYPE_ACCESS:
+                name = XATTR_NAME_ACL_ACCESS;
+                p_acl = &reiserfs_i->i_acl_access;
+                break;
+            case ACL_TYPE_DEFAULT:
+                name = XATTR_NAME_ACL_DEFAULT;
+                p_acl = &reiserfs_i->i_acl_default;
+                break;
+            default:
+                return ERR_PTR (-EINVAL);
+        }
+
+        if (IS_ERR (*p_acl)) {
+            if (PTR_ERR (*p_acl) == -ENODATA)
+                return NULL;
+        } else if (*p_acl != NULL)
+            return posix_acl_dup (*p_acl);
+
+        size = reiserfs_xattr_get (inode, name, NULL, 0);
+        if ((int)size < 0) {
+            if (size == -ENODATA || size == -ENOSYS) {
+		*p_acl = ERR_PTR (-ENODATA);
+		return NULL;
+            }
+            return ERR_PTR (size);
+        }
+
+        value = kmalloc (size, GFP_NOFS);
+        if (!value)
+            return ERR_PTR (-ENOMEM);
+
+	retval = reiserfs_xattr_get(inode, name, value, size);
+	if (retval == -ENODATA || retval == -ENOSYS) {
+		/* This shouldn't actually happen as it should have
+		   been caught above.. but just in case */
+		acl = NULL;
+		*p_acl = ERR_PTR (-ENODATA);
+        } else if (retval < 0) {
+		acl = ERR_PTR(retval);
+	} else {
+		acl = posix_acl_from_disk(value, retval);
+		*p_acl = posix_acl_dup (acl);
+        }
+
+	kfree(value);
+	return acl;
+}
+
+/*
+ * Inode operation set_posix_acl().
+ *
+ * inode->i_sem: down
+ * BKL held [before 2.5.x]
+ */
+int
+reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+        char *name;
+	void *value = NULL;
+	struct posix_acl **p_acl;
+	size_t size;
+	int error;
+        struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+
+        switch (type) {
+            case ACL_TYPE_ACCESS:
+                name = XATTR_NAME_ACL_ACCESS;
+                p_acl = &reiserfs_i->i_acl_access;
+                if (acl) {
+                    mode_t mode = inode->i_mode;
+                    error = posix_acl_equiv_mode (acl, &mode);
+                    if (error < 0)
+                        return error;
+                    else {
+                        inode->i_mode = mode;
+                        if (error == 0)
+                            acl = NULL;
+                    }
+                }
+                break;
+            case ACL_TYPE_DEFAULT:
+                name = XATTR_NAME_ACL_DEFAULT;
+                p_acl = &reiserfs_i->i_acl_default;
+                if (!S_ISDIR (inode->i_mode))
+                    return acl ? -EACCES : 0;
+                break;
+            default:
+                return -EINVAL;
+        }
+
+ 	if (acl) {
+            value = posix_acl_to_disk(acl, &size);
+            if (IS_ERR(value))
+                return (int)PTR_ERR(value);
+            error = reiserfs_xattr_set(inode, name, value, size, 0);
+	} else {
+            error = reiserfs_xattr_del (inode, name);
+            if (error == -ENODATA)
+                error = 0;
+        }
+
+	if (value)
+		kfree(value);
+
+        if (!error) {
+            /* Release the old one */
+            if (!IS_ERR (*p_acl) && *p_acl)
+                posix_acl_release (*p_acl);
+
+            if (acl == NULL)
+                *p_acl = ERR_PTR (-ENODATA);
+            else
+                *p_acl = posix_acl_dup (acl);
+        }
+
+	return error;
+}
+
+/* dir->i_sem: down,
+ * inode is new and not released into the wild yet */
+int
+reiserfs_inherit_default_acl (struct inode *dir, struct dentry *dentry, struct inode *inode)
+{
+    struct posix_acl *acl;
+    int err = 0;
+
+    /* ACLs only get applied to files and directories */
+    if (S_ISLNK (inode->i_mode))
+        return 0;
+
+    /* ACLs can only be used on "new" objects, so if it's an old object
+     * there is nothing to inherit from */
+    if (get_inode_sd_version (dir) == STAT_DATA_V1)
+        goto apply_umask;
+
+    /* Don't apply ACLs to objects in the .reiserfs_priv tree.. This
+     * would be useless since permissions are ignored, and a pain because
+     * it introduces locking cycles */
+    if (is_reiserfs_priv_object (dir)) {
+        REISERFS_I(inode)->i_flags |= i_priv_object;
+        goto apply_umask;
+    }
+
+    acl = reiserfs_get_acl (dir, ACL_TYPE_DEFAULT);
+    if (IS_ERR (acl)) {
+        if (PTR_ERR (acl) == -ENODATA)
+            goto apply_umask;
+        return PTR_ERR (acl);
+    }
+
+    if (acl) {
+        struct posix_acl *acl_copy;
+        mode_t mode = inode->i_mode;
+        int need_acl;
+
+        /* Copy the default ACL to the default ACL of a new directory */
+        if (S_ISDIR (inode->i_mode)) {
+            err = reiserfs_set_acl (inode, ACL_TYPE_DEFAULT, acl);
+            if (err)
+                goto cleanup;
+        }
+
+        /* Now we reconcile the new ACL and the mode,
+           potentially modifying both */
+        acl_copy = posix_acl_clone (acl, GFP_NOFS);
+        if (!acl_copy) {
+            err = -ENOMEM;
+            goto cleanup;
+        }
+
+
+        need_acl = posix_acl_create_masq (acl_copy, &mode);
+        if (need_acl >= 0) {
+            if (mode != inode->i_mode) {
+                inode->i_mode = mode;
+            }
+
+            /* If we need an ACL.. */
+            if (need_acl > 0) {
+                err = reiserfs_set_acl (inode, ACL_TYPE_ACCESS, acl_copy);
+                if (err)
+                    goto cleanup_copy;
+            }
+        }
+cleanup_copy:
+        posix_acl_release (acl_copy);
+cleanup:
+        posix_acl_release (acl);
+    } else {
+apply_umask:
+        /* no ACL, apply umask */
+        inode->i_mode &= ~current->fs->umask;
+    }
+
+    return err;
+}
+
+/* Looks up and caches the result of the default ACL.
+ * We do this so that we don't need to carry the xattr_sem into
+ * reiserfs_new_inode if we don't need to */
+int
+reiserfs_cache_default_acl (struct inode *inode)
+{
+    int ret = 0;
+    if (reiserfs_posixacl (inode->i_sb) &&
+        !is_reiserfs_priv_object (inode)) {
+        struct posix_acl *acl;
+        reiserfs_read_lock_xattrs (inode->i_sb);
+        acl = reiserfs_get_acl (inode, ACL_TYPE_DEFAULT);
+        reiserfs_read_unlock_xattrs (inode->i_sb);
+        ret = acl ? 1 : 0;
+        posix_acl_release (acl);
+    }
+
+    return ret;
+}
+
+int
+reiserfs_acl_chmod (struct inode *inode)
+{
+        struct posix_acl *acl, *clone;
+        int error;
+
+        if (S_ISLNK(inode->i_mode))
+                return -EOPNOTSUPP;
+
+	if (get_inode_sd_version (inode) == STAT_DATA_V1 ||
+	    !reiserfs_posixacl(inode->i_sb))
+        {
+	    return 0;
+	}
+
+        reiserfs_read_lock_xattrs (inode->i_sb);
+        acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS);
+        reiserfs_read_unlock_xattrs (inode->i_sb);
+        if (!acl)
+                return 0;
+        if (IS_ERR(acl))
+                return PTR_ERR(acl);
+        clone = posix_acl_clone(acl, GFP_NOFS);
+        posix_acl_release(acl);
+        if (!clone)
+                return -ENOMEM;
+        error = posix_acl_chmod_masq(clone, inode->i_mode);
+        if (!error) {
+                reiserfs_write_lock_xattrs (inode->i_sb);
+                error = reiserfs_set_acl(inode, ACL_TYPE_ACCESS, clone);
+                reiserfs_write_unlock_xattrs (inode->i_sb);
+        }
+        posix_acl_release(clone);
+        return error;
+}
+
+static int
+posix_acl_access_get(struct inode *inode, const char *name,
+			  void *buffer, size_t size)
+{
+	if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1)
+		return -EINVAL;
+	return xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
+}
+
+static int
+posix_acl_access_set(struct inode *inode, const char *name,
+			  const void *value, size_t size, int flags)
+{
+	if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1)
+		return -EINVAL;
+	return xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
+}
+
+static int
+posix_acl_access_del (struct inode *inode, const char *name)
+{
+    struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
+    struct posix_acl **acl = &reiserfs_i->i_acl_access;
+    if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1)
+	return -EINVAL;
+    if (!IS_ERR (*acl) && *acl) {
+        posix_acl_release (*acl);
+        *acl = ERR_PTR (-ENODATA);
+    }
+
+    return 0;
+}
+
+static int
+posix_acl_access_list (struct inode *inode, const char *name, int namelen, char *out)
+{
+    int len = namelen;
+    if (!reiserfs_posixacl (inode->i_sb))
+        return 0;
+    if (out)
+        memcpy (out, name, len);
+
+    return len;
+}
+
+struct reiserfs_xattr_handler posix_acl_access_handler = {
+    prefix: XATTR_NAME_ACL_ACCESS,
+    get: posix_acl_access_get,
+    set: posix_acl_access_set,
+    del: posix_acl_access_del,
+    list: posix_acl_access_list,
+};
+
+static int
+posix_acl_default_get (struct inode *inode, const char *name,
+			   void *buffer, size_t size)
+{
+	if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1)
+		return -EINVAL;
+	return xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
+}
+
+static int
+posix_acl_default_set(struct inode *inode, const char *name,
+			   const void *value, size_t size, int flags)
+{
+	if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1)
+		return -EINVAL;
+	return xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
+}
+
+static int
+posix_acl_default_del (struct inode *inode, const char *name)
+{
+    struct reiserfs_inode_info *reiserfs_i = REISERFS_I(inode);
+    struct posix_acl **acl = &reiserfs_i->i_acl_default;
+    if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1)
+	return -EINVAL;
+    if (!IS_ERR (*acl) && *acl) {
+        posix_acl_release (*acl);
+        *acl = ERR_PTR (-ENODATA);
+    }
+
+    return 0;
+}
+
+static int
+posix_acl_default_list (struct inode *inode, const char *name, int namelen, char *out)
+{
+    int len = namelen;
+    if (!reiserfs_posixacl (inode->i_sb))
+        return 0;
+    if (out)
+        memcpy (out, name, len);
+
+    return len;
+}
+
+struct reiserfs_xattr_handler posix_acl_default_handler = {
+    prefix: XATTR_NAME_ACL_DEFAULT,
+    get: posix_acl_default_get,
+    set: posix_acl_default_set,
+    del: posix_acl_default_del,
+    list: posix_acl_default_list,
+};
diff -puN fs/reiserfs/xattr.c~reiserfs-acl-02 fs/reiserfs/xattr.c
--- 25/fs/reiserfs/xattr.c~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/reiserfs/xattr.c	Fri Apr 23 14:36:49 2004
@@ -6,7 +6,7 @@
  */
 
 /*
- * In order to implement EAs in a clean, backwards compatible manner,
+ * In order to implement EA/ACLs in a clean, backwards compatible manner,
  * they are implemented as files in a "private" directory.
  * Each EA is in it's own file, with the directory layout like so (/ is assumed
  * to be relative to fs root). Inside the /.reiserfs_priv/xattrs directory,
@@ -15,12 +15,18 @@
  * named with the name of the extended attribute.
  *
  * So, for objectid 12648430, we could have:
+ * /.reiserfs_priv/xattrs/C0FFEE.0/system.posix_acl_access
+ * /.reiserfs_priv/xattrs/C0FFEE.0/system.posix_acl_default
  * /.reiserfs_priv/xattrs/C0FFEE.0/user.Content-Type
  * .. or similar.
  *
  * The file contents are the text of the EA. The size is known based on the
  * stat data describing the file.
  *
+ * In the case of system.posix_acl_access and system.posix_acl_default, since
+ * these are special cases for filesystem ACLs, they are interpreted by the
+ * kernel, in addition, they are negatively and positively cached and attached
+ * to the inode so that unnecessary lookups are avoided.
  */
 
 #include <linux/reiserfs_fs.h>
@@ -32,6 +38,7 @@
 #include <linux/pagemap.h>
 #include <linux/xattr.h>
 #include <linux/reiserfs_xattr.h>
+#include <linux/reiserfs_acl.h>
 #include <linux/mbcache.h>
 #include <asm/uaccess.h>
 #include <asm/checksum.h>
@@ -1169,6 +1176,10 @@ reiserfs_xattr_register_handlers (void)
 
     /* Add the handlers */
     list_add_tail (&user_handler.handlers, &xattr_handlers);
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+    list_add_tail (&posix_acl_access_handler.handlers, &xattr_handlers);
+    list_add_tail (&posix_acl_default_handler.handlers, &xattr_handlers);
+#endif
 
     /* Run initializers, if available */
     list_for_each (p, &xattr_handlers) {
@@ -1231,7 +1242,7 @@ reiserfs_xattr_init (struct super_block 
   } else if (reiserfs_xattrs_optional (s)) {
     /* Old format filesystem, but optional xattrs have been enabled
      * at mount time. Error out. */
-    reiserfs_warning ("reiserfs: xattrs not supported on pre v3.6 "
+    reiserfs_warning ("reiserfs: xattrs/ACLs not supported on pre v3.6 "
                       "format filesystem. Failing mount.\n");
     err = -EOPNOTSUPP;
     goto error;
@@ -1276,7 +1287,7 @@ reiserfs_xattr_init (struct super_block 
           /* If we're read-only it just means that the dir hasn't been
            * created. Not an error -- just no xattrs on the fs. We'll
            * check again if we go read-write */
-          reiserfs_warning ("reiserfs: xattrs enabled and couldn't "
+          reiserfs_warning ("reiserfs: xattrs/ACLs enabled and couldn't "
                             "find/create .reiserfs_priv. Failing mount.\n");
           err = -EOPNOTSUPP;
       }
@@ -1288,12 +1299,20 @@ error:
     if (err) {
           clear_bit (REISERFS_XATTRS, &(REISERFS_SB(s)->s_mount_opt));
           clear_bit (REISERFS_XATTRS_USER, &(REISERFS_SB(s)->s_mount_opt));
+          clear_bit (REISERFS_POSIXACL, &(REISERFS_SB(s)->s_mount_opt));
     }
+
+    /* The super_block MS_POSIXACL must mirror the (no)acl mount option. */
+    s->s_flags = s->s_flags & ~MS_POSIXACL;
+    if (reiserfs_posixacl (s))
+	s->s_flags |= MS_POSIXACL;
+
     return err;
 }
 
-int
-reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd)
+static int
+__reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd,
+                       int need_lock)
 {
 	umode_t			mode = inode->i_mode;
 
@@ -1317,10 +1336,45 @@ reiserfs_permission (struct inode *inode
         if (is_reiserfs_priv_object (inode))
 		return 0;
 
-	if (current->fsuid == inode->i_uid)
+	if (current->fsuid == inode->i_uid) {
 		mode >>= 6;
-	else if (in_group_p(inode->i_gid))
-		mode >>= 3;
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+	} else if (reiserfs_posixacl(inode->i_sb) &&
+                   get_inode_sd_version (inode) != STAT_DATA_V1) {
+                struct posix_acl *acl;
+
+		/* ACL can't contain additional permissions if
+		   the ACL_MASK entry is 0 */
+		if (!(mode & S_IRWXG))
+			goto check_groups;
+
+                if (need_lock)
+                    reiserfs_read_lock_xattrs (inode->i_sb);
+                acl = reiserfs_get_acl (inode, ACL_TYPE_ACCESS);
+                if (need_lock)
+                    reiserfs_read_unlock_xattrs (inode->i_sb);
+                if (IS_ERR (acl)) {
+                    if (PTR_ERR (acl) == -ENODATA)
+                        goto check_groups;
+                    return PTR_ERR (acl);
+                }
+
+                if (acl) {
+                    int err = posix_acl_permission (inode, acl, mask);
+                    posix_acl_release (acl);
+                    if (err == -EACCES) {
+                        goto check_capabilities;
+                    }
+                    return err;
+		} else {
+			goto check_groups;
+                }
+#endif
+	} else {
+check_groups:
+		if (in_group_p(inode->i_gid))
+			mode >>= 3;
+	}
 
 	/*
 	 * If the DACs are ok we don't need any capability check.
@@ -1328,6 +1382,7 @@ reiserfs_permission (struct inode *inode
 	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
 		return 0;
 
+check_capabilities:
 	/*
 	 * Read/write DACs are always overridable.
 	 * Executable DACs are overridable if at least one exec bit is set.
@@ -1344,5 +1399,16 @@ reiserfs_permission (struct inode *inode
 			return 0;
 
 	return -EACCES;
+}
 
+int
+reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd)
+{
+    return __reiserfs_permission (inode, mask, nd, 1);
+}
+
+int
+reiserfs_permission_locked (struct inode *inode, int mask, struct nameidata *nd)
+{
+    return __reiserfs_permission (inode, mask, nd, 0);
 }
diff -puN fs/reiserfs/xattr_user.c~reiserfs-acl-02 fs/reiserfs/xattr_user.c
--- 25/fs/reiserfs/xattr_user.c~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/fs/reiserfs/xattr_user.c	Fri Apr 23 14:36:49 2004
@@ -6,6 +6,10 @@
 #include <linux/reiserfs_xattr.h>
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+# include <linux/reiserfs_acl.h>
+#endif
+
 #define XATTR_USER_PREFIX "user."
 
 static int
@@ -20,7 +24,7 @@ user_get (struct inode *inode, const cha
     if (!reiserfs_xattrs_user (inode->i_sb))
         return -EOPNOTSUPP;
 
-    error = permission (inode, MAY_READ, NULL);
+    error = reiserfs_permission_locked (inode, MAY_READ, NULL);
     if (error)
         return error;
 
@@ -44,7 +48,7 @@ user_set (struct inode *inode, const cha
         (!S_ISDIR (inode->i_mode) || inode->i_mode & S_ISVTX))
         return -EPERM;
 
-    error = permission (inode, MAY_WRITE, NULL);
+    error = reiserfs_permission_locked (inode, MAY_WRITE, NULL);
     if (error)
         return error;
 
@@ -66,7 +70,7 @@ user_del (struct inode *inode, const cha
         (!S_ISDIR (inode->i_mode) || inode->i_mode & S_ISVTX))
         return -EPERM;
 
-    error = permission (inode, MAY_WRITE, NULL);
+    error = reiserfs_permission_locked (inode, MAY_WRITE, NULL);
     if (error)
         return error;
 
diff -puN /dev/null include/linux/reiserfs_acl.h
--- /dev/null	Thu Apr 11 07:25:15 2002
+++ 25-akpm/include/linux/reiserfs_acl.h	Fri Apr 23 14:36:49 2004
@@ -0,0 +1,91 @@
+#include <linux/init.h>
+#include <linux/posix_acl.h>
+#include <linux/xattr_acl.h>
+
+#define REISERFS_ACL_VERSION	0x0001
+
+typedef struct {
+	__u16		e_tag;
+	__u16		e_perm;
+	__u32		e_id;
+} reiserfs_acl_entry;
+
+typedef struct {
+	__u16		e_tag;
+	__u16		e_perm;
+} reiserfs_acl_entry_short;
+
+typedef struct {
+	__u32		a_version;
+} reiserfs_acl_header;
+
+static inline size_t reiserfs_acl_size(int count)
+{
+	if (count <= 4) {
+		return sizeof(reiserfs_acl_header) +
+		       count * sizeof(reiserfs_acl_entry_short);
+	} else {
+		return sizeof(reiserfs_acl_header) +
+		       4 * sizeof(reiserfs_acl_entry_short) +
+		       (count - 4) * sizeof(reiserfs_acl_entry);
+	}
+}
+
+static inline int reiserfs_acl_count(size_t size)
+{
+	ssize_t s;
+	size -= sizeof(reiserfs_acl_header);
+	s = size - 4 * sizeof(reiserfs_acl_entry_short);
+	if (s < 0) {
+		if (size % sizeof(reiserfs_acl_entry_short))
+			return -1;
+		return size / sizeof(reiserfs_acl_entry_short);
+	} else {
+		if (s % sizeof(reiserfs_acl_entry))
+			return -1;
+		return s / sizeof(reiserfs_acl_entry) + 4;
+	}
+}
+
+
+#ifdef CONFIG_REISERFS_FS_POSIX_ACL
+struct posix_acl * reiserfs_get_acl(struct inode *inode, int type);
+int reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl);
+int reiserfs_acl_chmod (struct inode *inode);
+int reiserfs_inherit_default_acl (struct inode *dir, struct dentry *dentry, struct inode *inode);
+int reiserfs_cache_default_acl (struct inode *dir);
+extern int reiserfs_xattr_posix_acl_init (void) __init;
+extern int reiserfs_xattr_posix_acl_exit (void);
+extern struct reiserfs_xattr_handler posix_acl_default_handler;
+extern struct reiserfs_xattr_handler posix_acl_access_handler;
+#else
+
+#define reiserfs_set_acl NULL
+#define reiserfs_get_acl NULL
+#define reiserfs_cache_default_acl(inode) 0
+
+static inline int
+reiserfs_xattr_posix_acl_init (void)
+{
+    return 0;
+}
+
+static inline int
+reiserfs_xattr_posix_acl_exit (void)
+{
+    return 0;
+}
+
+static inline int
+reiserfs_acl_chmod (struct inode *inode)
+{
+    return 0;
+}
+
+static inline int
+reiserfs_inherit_default_acl (const struct inode *dir, struct dentry *dentry, struct inode *inode)
+{
+    return 0;
+}
+
+#endif
diff -puN include/linux/reiserfs_fs_i.h~reiserfs-acl-02 include/linux/reiserfs_fs_i.h
--- 25/include/linux/reiserfs_fs_i.h~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/include/linux/reiserfs_fs_i.h	Fri Apr 23 14:36:49 2004
@@ -52,6 +52,9 @@ struct reiserfs_inode_info {
     ** flushed */
     unsigned long i_trans_id ;
     struct reiserfs_journal_list *i_jl;
+
+    struct posix_acl *i_acl_access;
+    struct posix_acl *i_acl_default;
     struct inode vfs_inode;
 };
 
diff -puN include/linux/reiserfs_fs_sb.h~reiserfs-acl-02 include/linux/reiserfs_fs_sb.h
--- 25/include/linux/reiserfs_fs_sb.h~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/include/linux/reiserfs_fs_sb.h	Fri Apr 23 14:36:49 2004
@@ -443,6 +443,7 @@ enum reiserfs_mount_options {
     REISERFS_ATTRS,
     REISERFS_XATTRS,
     REISERFS_XATTRS_USER,
+    REISERFS_POSIXACL,
 
     REISERFS_TEST1,
     REISERFS_TEST2,
@@ -470,7 +471,8 @@ enum reiserfs_mount_options {
 #define reiserfs_data_writeback(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_DATA_WRITEBACK))
 #define reiserfs_xattrs(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_XATTRS))
 #define reiserfs_xattrs_user(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_XATTRS_USER))
-#define reiserfs_xattrs_optional(s) reiserfs_xattrs_user(s)
+#define reiserfs_posixacl(s) (REISERFS_SB(s)->s_mount_opt & (1 << REISERFS_POSIXACL))
+#define reiserfs_xattrs_optional(s) (reiserfs_xattrs_user(s) || reiserfs_posixacl(s))
 
 void reiserfs_file_buffer (struct buffer_head * bh, int list);
 extern struct file_system_type reiserfs_fs_type;
diff -puN include/linux/reiserfs_xattr.h~reiserfs-acl-02 include/linux/reiserfs_xattr.h
--- 25/include/linux/reiserfs_xattr.h~reiserfs-acl-02	Fri Apr 23 14:36:49 2004
+++ 25-akpm/include/linux/reiserfs_xattr.h	Fri Apr 23 14:36:49 2004
@@ -42,6 +42,7 @@ int reiserfs_delete_xattrs (struct inode
 int reiserfs_chown_xattrs (struct inode *inode, struct iattr *attrs);
 int reiserfs_xattr_init (struct super_block *sb, int mount_flags);
 int reiserfs_permission (struct inode *inode, int mask, struct nameidata *nd);
+int reiserfs_permission_locked (struct inode *inode, int mask, struct nameidata *nd);
 
 int reiserfs_xattr_del (struct inode *, const char *);
 int reiserfs_xattr_get (const struct inode *, const char *, void *, size_t);
@@ -94,7 +95,11 @@ reiserfs_read_unlock_xattrs(struct super
 
 static inline int reiserfs_delete_xattrs (struct inode *inode) { return 0; };
 static inline int reiserfs_chown_xattrs (struct inode *inode, struct iattr *attrs) { return 0; };
-static inline int reiserfs_xattr_init (struct super_block *sb, int mount_flags) { return 0; };
+static inline int reiserfs_xattr_init (struct super_block *sb, int mount_flags)
+{
+    sb->s_flags = (sb->s_flags & ~MS_POSIXACL); /* to be sure */
+    return 0;
+};
 #endif
 
 #endif  /* __KERNEL__ */

_