Add POSIX Access Control Lists to ext2/3

Add POSIX Access Control Lists to ext2/3

Post by ty.. » Thu, 17 Oct 2002 01:00:18



This patch adds ACL support to the ext3 filesystem.

                                        - Ted

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
#
# fs/Config.help            |   11
# fs/Config.in              |    1
# fs/ext3/Makefile          |    4
# fs/ext3/acl.c             |  608 ++++++++++++++++++++++++++++++++++++++++++++++
# fs/ext3/acl.h             |   94 +++++++
# fs/ext3/file.c            |    4
# fs/ext3/ialloc.c          |   32 +-
# fs/ext3/inode.c           |   36 ++
# fs/ext3/namei.c           |   15 -
# fs/ext3/super.c           |   39 ++
# fs/ext3/xattr.c           |   23 +
# fs/ext3/xattr_user.c      |    5
# include/linux/ext3_fs.h   |    1
# include/linux/ext3_fs_i.h |    4
# 14 files changed, 850 insertions(+), 27 deletions(-)
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/10/15      ty...@snap.thunk.org   1.857
# Port of (bugfixed) 0.8.50 acl-ext3 to 2.5.
#
# This patch adds ACL support to the ext3 filesystem.
# --------------------------------------------
#
diff -Nru a/fs/Config.help b/fs/Config.help
--- a/fs/Config.help    Tue Oct 15 16:59:54 2002
+++ b/fs/Config.help    Tue Oct 15 16:59:54 2002
@@ -179,7 +179,18 @@
   the kernel or by users (see the attr(5) manual page, or visit
   <http://acl.bestbits.at/> for details).

+  You need this for POSIX ACL support on ext3.
+
   If unsure, say N.
+
+CONFIG_EXT3_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 don't know what Access Control Lists are, say N.

 CONFIG_JBD
   This is a generic journaling layer for block devices.  It is
diff -Nru a/fs/Config.in b/fs/Config.in
--- a/fs/Config.in      Tue Oct 15 16:59:54 2002
+++ b/fs/Config.in      Tue Oct 15 16:59:54 2002
@@ -30,6 +30,7 @@

 tristate 'Ext3 journalling file system support' CONFIG_EXT3_FS
 dep_mbool '  Ext3 extended attributes' CONFIG_EXT3_FS_XATTR $CONFIG_EXT3_FS
+dep_mbool '  Ext3 POSIX Access Control Lists' CONFIG_EXT3_FS_POSIX_ACL $CONFIG_EXT3_FS_XATTR $CONFIG_FS_POSIX_ACL
 # CONFIG_JBD could be its own option (even modular), but until there are
 # other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS
 # dep_tristate '  Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS
diff -Nru a/fs/ext3/Makefile b/fs/ext3/Makefile
--- a/fs/ext3/Makefile  Tue Oct 15 16:59:54 2002
+++ b/fs/ext3/Makefile  Tue Oct 15 16:59:54 2002
@@ -13,4 +13,8 @@
 ext3-objs += xattr.o xattr_user.o
 endif

+ifeq ($(CONFIG_EXT3_FS_POSIX_ACL),y)
+ext3-objs += acl.o
+endif
+
 include $(TOPDIR)/Rules.make
diff -Nru a/fs/ext3/acl.c b/fs/ext3/acl.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/fs/ext3/acl.c     Tue Oct 15 16:59:54 2002
@@ -0,0 +1,608 @@
+/*
+ * linux/fs/ext3/acl.c
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbac...@computer.org>
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/ext3_fs.h>
+#include "xattr.h"
+#include "acl.h"
+
+/*
+ * Convert from filesystem to in-memory representation.
+ */
+static struct posix_acl *
+ext3_acl_from_disk(const void *value, size_t size)
+{
+       const char *end = (char *)value + size;
+       int n, count;
+       struct posix_acl *acl;
+
+       if (!value)
+               return NULL;
+       if (size < sizeof(ext3_acl_header))
+                return ERR_PTR(-EINVAL);
+       if (((ext3_acl_header *)value)->a_version !=
+           cpu_to_le32(EXT3_ACL_VERSION))
+               return ERR_PTR(-EINVAL);
+       value = (char *)value + sizeof(ext3_acl_header);
+       count = ext3_acl_count(size);
+       if (count < 0)
+               return ERR_PTR(-EINVAL);
+       if (count == 0)
+               return NULL;
+       acl = posix_acl_alloc(count, GFP_KERNEL);
+       if (!acl)
+               return ERR_PTR(-ENOMEM);
+       for (n=0; n < count; n++) {
+               ext3_acl_entry *entry =
+                       (ext3_acl_entry *)value;
+               if ((char *)value + sizeof(ext3_acl_entry_short) > end)
+                       goto fail;
+               acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
+               acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
+               switch(acl->a_entries[n].e_tag) {
+                       case ACL_USER_OBJ:
+                       case ACL_GROUP_OBJ:
+                       case ACL_MASK:
+                       case ACL_OTHER:
+                               value = (char *)value +
+                                       sizeof(ext3_acl_entry_short);
+                               acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
+                               break;
+
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               value = (char *)value + sizeof(ext3_acl_entry);
+                               if ((char *)value > end)
+                                       goto fail;
+                               acl->a_entries[n].e_id =
+                                       le32_to_cpu(entry->e_id);
+                               break;
+
+                       default:
+                               goto fail;
+               }
+       }
+       if (value != end)
+               goto fail;
+       return acl;
+
+fail:
+       posix_acl_release(acl);
+       return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Convert from in-memory to filesystem representation.
+ */
+static void *
+ext3_acl_to_disk(const struct posix_acl *acl, size_t *size)
+{
+       ext3_acl_header *ext_acl;
+       char *e;
+       int n;
+
+       *size = ext3_acl_size(acl->a_count);
+       ext_acl = (ext3_acl_header *)kmalloc(sizeof(ext3_acl_header) +
+               acl->a_count * sizeof(ext3_acl_entry), GFP_KERNEL);
+       if (!ext_acl)
+               return ERR_PTR(-ENOMEM);
+       ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION);
+       e = (char *)ext_acl + sizeof(ext3_acl_header);
+       for (n=0; n < acl->a_count; n++) {
+               ext3_acl_entry *entry = (ext3_acl_entry *)e;
+               entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);
+               entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
+               switch(acl->a_entries[n].e_tag) {
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               entry->e_id =
+                                       cpu_to_le32(acl->a_entries[n].e_id);
+                               e += sizeof(ext3_acl_entry);
+                               break;
+
+                       case ACL_USER_OBJ:
+                       case ACL_GROUP_OBJ:
+                       case ACL_MASK:
+                       case ACL_OTHER:
+                               e += sizeof(ext3_acl_entry_short);
+                               break;
+
+                       default:
+                               goto fail;
+               }
+       }
+       return (char *)ext_acl;
+
+fail:
+       kfree(ext_acl);
+       return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Inode operation get_posix_acl().
+ *
+ * inode->i_sem: down
+ */
+struct posix_acl *
+ext3_get_acl(struct inode *inode, int type)
+{
+       int name_index;
+       char *value;
+       struct posix_acl *acl, **p_acl;
+       const size_t size = ext3_acl_size(EXT3_ACL_MAX_ENTRIES);
+       int retval;
+
+       if (!test_opt(inode->i_sb, POSIX_ACL))
+               return 0;
+
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       p_acl = &EXT3_I(inode)->i_acl;
+                       name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       p_acl = &EXT3_I(inode)->i_default_acl;
+                       name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;
+                       break;
+
+               default:
+                       return ERR_PTR(-EINVAL);
+       }
+       if (*p_acl != EXT3_ACL_NOT_CACHED)
+               return posix_acl_dup(*p_acl);
+       value = kmalloc(size, GFP_KERNEL);
+       if (!value)
+               return ERR_PTR(-ENOMEM);
+
+       retval = ext3_xattr_get(inode, name_index, "", value, size);
+
+       if (retval == -ENODATA || retval == -ENOSYS)
+               *p_acl = acl = NULL;
+       else if (retval < 0)
+               acl = ERR_PTR(retval);
+       else {
+               acl = ext3_acl_from_disk(value, retval);
+               if (!IS_ERR(acl))
+                       *p_acl = posix_acl_dup(acl);
+       }
+       kfree(value);
+       return acl;
+}
+
+/*
+ * Set the access or default ACL of an inode.
+ *
+ * inode->i_sem: down unless called from ext3_new_inode
+ */
+static int
+ext3_do_set_acl(handle_t *handle, struct inode *inode, int type,
+               struct posix_acl *acl)
+{
+       int name_index;
+       void *value = NULL;
+       struct posix_acl **p_acl;
+       size_t size;
+       int error;
+
+       if (S_ISLNK(inode->i_mode))
+               return -EOPNOTSUPP;
+
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
+                       p_acl = &EXT3_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;
+                                       ext3_mark_inode_dirty(handle, inode);
+                                       if (error == 0)
+                                               acl = NULL;
+                               }
+                       }
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;
+                       p_acl = &EXT3_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 > EXT3_ACL_MAX_ENTRIES)
+                       return -EINVAL;
+               value = ext3_acl_to_disk(acl, &size);
+               if (IS_ERR(value))
+                       return (int)PTR_ERR(value);
+       }
+
+       error = ext3_xattr_set(handle, inode, name_index, "", value, size, 0);
+
+       if (value)
+               kfree(value);
+       if (!error) {
+               if (*p_acl && *p_acl != EXT3_ACL_NOT_CACHED)
+                       posix_acl_release(*p_acl);
+               *p_acl = posix_acl_dup(acl);
+       }
+       return error;
+}
+
+/*
+ * Inode operation set_posix_acl().
+ *
+ * inode->i_sem: down
+ */
+
+int
+ext3_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+       handle_t *handle;
+       int error;
+      
+       if (!test_opt(inode->i_sb, POSIX_ACL))
+               return 0;
+
+       handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS);
+       if (IS_ERR(handle))
+               return PTR_ERR(handle);
+       error = ext3_do_set_acl(handle, inode, type, acl);
+       ext3_journal_stop(handle, inode);
+
+       return error;
+}
+
+static int
+__ext3_permission(struct inode *inode, int mask, int lock)
+{
+       int mode = inode->i_mode;
+
+       /* Nobody gets write access to a read-only fs */
+       if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
+           (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+               return -EROFS;
+       /* Nobody gets write access to an immutable file */
+       if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
+           return -EACCES;
+       if (current->fsuid == inode->i_uid) {
+               mode >>= 6;
+       } else if (test_opt(inode->i_sb, POSIX_ACL)) {
+               /* ACL can't contain additional permissions if
+                  the ACL_MASK entry is 0 */
+               if (!(mode & S_IRWXG))
+                       goto check_groups;
+               if (EXT3_I(inode)->i_acl == EXT3_ACL_NOT_CACHED) {
+                       struct posix_acl *acl;
+
+                       if (lock) {
+                               down(&inode->i_sem);
+                               acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
+                               up(&inode->i_sem);
+                       } else
+                               acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
+
+                       if (IS_ERR(acl))
+                               return PTR_ERR(acl);
+                       posix_acl_release(acl);
+              
...

read more »

 
 
 

Add POSIX Access Control Lists to ext2/3

Post by Andreas Gruenbache » Thu, 17 Oct 2002 02:00:12



Quote:> This patch adds ACL support to the ext3 filesystem.

On Tuesday 15 October 2002 23:22, Stephen C. Tweedie wrote
[about the problems RedHat had with the patch]:

Quote:> The main problems were in standards conformance --- permissions on
> normal files not using ACLs were sometimes handled incorrectly with
> the ACL patches applied.  We also saw error-handling problems where
> non-fatal conditions (eg. truncate() to an illegal file size) were
> treated as fatal, potentially taking the whole fs offline.

The standard conformance is already fixed ("goto check_group"). This is the
fix to the error handling.

acl-ext3-fix-tree.diff is the fix relative to the 0.8.51 bestbits patch;
acl-ext3-inode.c.diff includes all changes from 2.4.19. I make a 0.8.52 now.

--Andreas.

  acl-ext3-fix-tree.diff
1K Download

  acl-ext3-inode.c.diff
2K Download