]> git.hungrycats.org Git - linux/commitdiff
[PATCH] reiserfs: ACL support
authorAndrew Morton <akpm@osdl.org>
Mon, 10 May 2004 06:59:25 +0000 (23:59 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Mon, 10 May 2004 06:59:25 +0000 (23:59 -0700)
From: Chris Mason <mason@suse.com>

From: jeffm@suse.com

reiserfs acl support

13 files changed:
fs/Kconfig
fs/reiserfs/Makefile
fs/reiserfs/file.c
fs/reiserfs/inode.c
fs/reiserfs/namei.c
fs/reiserfs/super.c
fs/reiserfs/xattr.c
fs/reiserfs/xattr_acl.c [new file with mode: 0644]
fs/reiserfs/xattr_user.c
include/linux/reiserfs_acl.h [new file with mode: 0644]
include/linux/reiserfs_fs_i.h
include/linux/reiserfs_fs_sb.h
include/linux/reiserfs_xattr.h

index d3ba7b5a376b346acb6e0106298daf183b6199c3..1f41463cedf03e93591256aed11f0b59614cf595 100644 (file)
@@ -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
index a72c0c1aca0ed5a9053c73f55c47f2f1982c4630..984f8c3211cde3a4aa9a86def8df8d614e30717f 100644 (file)
@@ -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,
index 847a11a2ccc2401edd28824ed1f12711b5734840..a8c3f48264a8b2d0c220483a402e15b44e43ead2 100644 (file)
@@ -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>
index 5b2e6a0918069c9de1a620ef954f508f5002bc54..593f543db21c0982e10e090946143bf0227f1204 100644 (file)
@@ -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 * inode, struct path * path)
     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_transaction_handle *th,
     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_transaction_handle *th,
        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 *dentry, struct iattr *attr) {
     }
 
 
+    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 ;
index 397a92fe51e9715bb4a228b3b0dccdf0a9466de6..e628813848c03cdbf8c1f88c02db49501e4fe41d 100644 (file)
@@ -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 * dir, struct dentry *dentry, int mode,
     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 * dir, struct dentry *dentry, int mode,
     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 * dir, struct dentry *dentry, int mode,
     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 * dir, struct dentry *dentry, int mode,
     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 * dir, struct dentry *dentry, int mode)
     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 * dir, struct dentry *dentry, int mode)
     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 * dir, struct dentry *dentry, int mode)
                                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 inode * parent_dir,
     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), 
index 4cd8a8d56f46b2f1b41cf385773f5a7df9414be2..5aa25c9a8e2751f508e628c633efe4cfabdbca7b 100644 (file)
@@ -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_cache_t * cachep, unsigned long flags)
            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 inode * inode) {
     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 (struct super_block * s, char * options, /* st
        {"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 super_block * s, int * mount_flags, char * a
   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 */
index 18494450627c731c502256da0a551cb2b3ea048d..e80fb90f8a20759fcae47d09c385f3e7116cca32 100644 (file)
@@ -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,
  * 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 *s, int mount_flags)
   } 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 *s, int mount_flags)
           /* 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, int mask, struct nameidata *nd)
         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, int mask, struct nameidata *nd)
        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, int mask, struct nameidata *nd)
                        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 --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
new file mode 100644 (file)
index 0000000..cded542
--- /dev/null
@@ -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,
+};
index ad5ee806ad55da37830a9df6434b6e14090c226e..38779f3fd314ad48735314a79c3221eb73d61766 100644 (file)
@@ -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 char *name, void *buffer, size_t size)
     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 char *name, const void *buffer,
         (!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 char *name)
         (!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 --git a/include/linux/reiserfs_acl.h b/include/linux/reiserfs_acl.h
new file mode 100644 (file)
index 0000000..acfde2d
--- /dev/null
@@ -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
index 3ee4eb78c4f1e0d775cc8d061b4be53b075046af..67bc69db28b467d35168488edce631d34b5db44e 100644 (file)
@@ -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;
 };
 
index dd9a3988de3e9e41779fc652ae3a3ebc94077788..38708778dd5e09380b32c7e2a17aac1c8988b520 100644 (file)
@@ -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;
index b13cc8d280db3119619f8c26fe61a4c7d0e4591b..4abc58013cd651b84bd497d3665bfa6c6a9d04ee 100644 (file)
@@ -42,6 +42,7 @@ int reiserfs_delete_xattrs (struct inode *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_block *sb)
 
 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__ */