11/11 Ext2/3 Updates: Extended attributes, ACL, etc.

11/11 Ext2/3 Updates: Extended attributes, ACL, etc.

Post by ty.. » Wed, 30 Oct 2002 19:00:17



Port of (bugfixed) 0.8.50 acl-ext2 to 2.5

This patch adds ACL support to the ext2 filesystem.

fs/Config.help          |   11
fs/Config.in            |    1
fs/ext2/Makefile        |    4
fs/ext2/acl.c           |  573 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext2/acl.h           |   88 +++++++
fs/ext2/ext2.h          |    5
fs/ext2/file.c          |    3
fs/ext2/ialloc.c        |   37 +--
fs/ext2/inode.c         |   22 +
fs/ext2/namei.c         |   10
fs/ext2/super.c         |   43 +++
fs/ext2/xattr.c         |   21 +
include/linux/ext2_fs.h |    1
13 files changed, 797 insertions(+), 22 deletions(-)

diff -Nru a/fs/Config.help b/fs/Config.help
--- a/fs/Config.help    Tue Oct 29 09:56:10 2002
+++ b/fs/Config.help    Tue Oct 29 09:56:10 2002
@@ -128,7 +128,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 ext2.
+
   If unsure, say N.
+
+CONFIG_EXT2_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_EXT3_FS
   This is the journaling version of the Second extended file system
diff -Nru a/fs/Config.in b/fs/Config.in
--- a/fs/Config.in      Tue Oct 29 09:56:10 2002
+++ b/fs/Config.in      Tue Oct 29 09:56:10 2002
@@ -98,6 +98,7 @@

 tristate 'Second extended fs support' CONFIG_EXT2_FS
 dep_mbool '  Ext2 extended attributes' CONFIG_EXT2_FS_XATTR $CONFIG_EXT2_FS
+dep_mbool '  Ext2 POSIX Access Control Lists' CONFIG_EXT2_FS_POSIX_ACL $CONFIG_EXT2_FS_XATTR

 tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS

diff -Nru a/fs/ext2/Makefile b/fs/ext2/Makefile
--- a/fs/ext2/Makefile  Tue Oct 29 09:56:10 2002
+++ b/fs/ext2/Makefile  Tue Oct 29 09:56:10 2002
@@ -13,4 +13,8 @@
 ext2-objs += xattr.o xattr_user.o
 endif

+ifeq ($(CONFIG_EXT2_FS_POSIX_ACL),y)
+ext2-objs += acl.o
+endif
+
 include $(TOPDIR)/Rules.make
diff -Nru a/fs/ext2/acl.c b/fs/ext2/acl.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/fs/ext2/acl.c     Tue Oct 29 09:56:10 2002
@@ -0,0 +1,573 @@
+/*
+ * linux/fs/ext2/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 "ext2.h"
+#include "xattr.h"
+#include "acl.h"
+
+/*
+ * Convert from filesystem to in-memory representation.
+ */
+static struct posix_acl *
+ext2_acl_from_disk(const void *value, size_t size)
+{
+       const char *end = (char *)value + size;
+       int n, count;
+       struct posix_acl *acl;
+
+       if (!value)
+               return NULL;
+       if (size < sizeof(ext2_acl_header))
+                return ERR_PTR(-EINVAL);
+       if (((ext2_acl_header *)value)->a_version !=
+           cpu_to_le32(EXT2_ACL_VERSION))
+               return ERR_PTR(-EINVAL);
+       value = (char *)value + sizeof(ext2_acl_header);
+       count = ext2_acl_count(size);
+       if (count < 0)
+               return ERR_PTR(-EINVAL);
+       if (count == 0)
+               return NULL;
+       acl = posix_acl_alloc(count, GFP_KERNEL);
+       if (!acl)
+               return ERR_PTR(-ENOMEM);
+       for (n=0; n < count; n++) {
+               ext2_acl_entry *entry =
+                       (ext2_acl_entry *)value;
+               if ((char *)value + sizeof(ext2_acl_entry_short) > end)
+                       goto fail;
+               acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
+               acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
+               switch(acl->a_entries[n].e_tag) {
+                       case ACL_USER_OBJ:
+                       case ACL_GROUP_OBJ:
+                       case ACL_MASK:
+                       case ACL_OTHER:
+                               value = (char *)value +
+                                       sizeof(ext2_acl_entry_short);
+                               acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
+                               break;
+
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               value = (char *)value + sizeof(ext2_acl_entry);
+                               if ((char *)value > end)
+                                       goto fail;
+                               acl->a_entries[n].e_id =
+                                       le32_to_cpu(entry->e_id);
+                               break;
+
+                       default:
+                               goto fail;
+               }
+       }
+       if (value != end)
+               goto fail;
+       return acl;
+
+fail:
+       posix_acl_release(acl);
+       return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Convert from in-memory to filesystem representation.
+ */
+static void *
+ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
+{
+       ext2_acl_header *ext_acl;
+       char *e;
+       int n;
+
+       *size = ext2_acl_size(acl->a_count);
+       ext_acl = (ext2_acl_header *)kmalloc(sizeof(ext2_acl_header) +
+               acl->a_count * sizeof(ext2_acl_entry), GFP_KERNEL);
+       if (!ext_acl)
+               return ERR_PTR(-ENOMEM);
+       ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
+       e = (char *)ext_acl + sizeof(ext2_acl_header);
+       for (n=0; n < acl->a_count; n++) {
+               ext2_acl_entry *entry = (ext2_acl_entry *)e;
+               entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);
+               entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
+               switch(acl->a_entries[n].e_tag) {
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               entry->e_id =
+                                       cpu_to_le32(acl->a_entries[n].e_id);
+                               e += sizeof(ext2_acl_entry);
+                               break;
+
+                       case ACL_USER_OBJ:
+                       case ACL_GROUP_OBJ:
+                       case ACL_MASK:
+                       case ACL_OTHER:
+                               e += sizeof(ext2_acl_entry_short);
+                               break;
+
+                       default:
+                               goto fail;
+               }
+       }
+       return (char *)ext_acl;
+
+fail:
+       kfree(ext_acl);
+       return ERR_PTR(-EINVAL);
+}
+
+/*
+ * inode->i_sem: down
+ */
+static struct posix_acl *
+ext2_get_acl(struct inode *inode, int type)
+{
+       int name_index;
+       char *value;
+       struct posix_acl *acl, **p_acl;
+       const size_t size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES);
+       int retval;
+
+       if (!test_opt(inode->i_sb, POSIX_ACL))
+               return 0;
+
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       p_acl = &EXT2_I(inode)->i_acl;
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       p_acl = &EXT2_I(inode)->i_default_acl;
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
+                       break;
+
+               default:
+                       return ERR_PTR(-EINVAL);
+       }
+       if (*p_acl != EXT2_ACL_NOT_CACHED)
+               return posix_acl_dup(*p_acl);
+       value = kmalloc(size, GFP_KERNEL);
+       if (!value)
+               return ERR_PTR(-ENOMEM);
+
+       retval = ext2_xattr_get(inode, name_index, "", value, size);
+
+       if (retval == -ENODATA || retval == -ENOSYS)
+               *p_acl = acl = NULL;
+       else if (retval < 0)
+               acl = ERR_PTR(retval);
+       else {
+               acl = ext2_acl_from_disk(value, retval);
+               if (!IS_ERR(acl))
+                       *p_acl = posix_acl_dup(acl);
+       }
+       kfree(value);
+       return acl;
+}
+
+/*
+ * inode->i_sem: down
+ */
+static int
+ext2_set_acl(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;
+       if (!test_opt(inode->i_sb, POSIX_ACL))
+               return 0;
+
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
+                       p_acl = &EXT2_I(inode)->i_acl;
+                       if (acl) {
+                               mode_t mode = inode->i_mode;
+                               error = posix_acl_equiv_mode(acl, &mode);
+                               if (error < 0)
+                                       return error;
+                               else {
+                                       inode->i_mode = mode;
+                                       mark_inode_dirty(inode);
+                                       if (error == 0)
+                                               acl = NULL;
+                               }
+                       }
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
+                       p_acl = &EXT2_I(inode)->i_default_acl;
+                       if (!S_ISDIR(inode->i_mode))
+                               return acl ? -EACCES : 0;
+                       break;
+
+               default:
+                       return -EINVAL;
+       }
+       if (acl) {
+               if (acl->a_count > EXT2_ACL_MAX_ENTRIES)
+                       return -EINVAL;
+               value = ext2_acl_to_disk(acl, &size);
+               if (IS_ERR(value))
+                       return (int)PTR_ERR(value);
+       }
+
+       error = ext2_xattr_set(inode, name_index, "", value, size, 0);
+
+       if (value)
+               kfree(value);
+       if (!error) {
+               if (*p_acl && *p_acl != EXT2_ACL_NOT_CACHED)
+                       posix_acl_release(*p_acl);
+               *p_acl = posix_acl_dup(acl);
+       }
+       return error;
+}
+
+static int
+__ext2_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 (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED) {
+                       struct posix_acl *acl;
+
+                       if (lock) {
+                               down(&inode->i_sem);
+                               acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+                               up(&inode->i_sem);
+                       } else
+                               acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+
+                       if (IS_ERR(acl))
+                               return PTR_ERR(acl);
+                       posix_acl_release(acl);
+                       if (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED)
+                               return -EIO;
+               }
+               if (EXT2_I(inode)->i_acl) {
+                       int error = posix_acl_permission(inode,
+                               EXT2_I(inode)->i_acl, mask);
+                       if (error == -EACCES)
+                               goto check_capabilities;
+                       return error;
+               } else
+                       goto check_groups;
+       } else {
+check_groups:
+               if (in_group_p(inode->i_gid))
+                       mode >>= 3;
+       }
+       if ((mode & mask & S_IRWXO) == mask)
+               return 0;
+
+check_capabilities:
+       /* Allowed to override Discretionary Access Control? */
+       if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
+               if (capable(CAP_DAC_OVERRIDE))
+                       return 0;
+       /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */
+       if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) ||
+           (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
+               return 0;
+       return -EACCES;
+}
+
+/*
+ * Inode operation permission().
+ *
+ * inode->i_sem: up
+ * BKL held [before 2.5.x]
+ */
+int
+ext2_permission(struct inode *inode, int mask)
+{
+       return __ext2_permission(inode, mask, 1);
+}
+
+/*
+ * Used internally if i_sem is already down.
+ */
+int
+ext2_permission_locked(struct inode *inode, int mask)
+{
+       return __ext2_permission(inode, mask, 0);
+}
+
+/*
+ * Initialize the ACLs of a new inode. ...

read more »

 
 
 

11/11 Ext2/3 Updates: Extended attributes, ACL, etc.

Post by ty.. » Fri, 01 Nov 2002 10:50:11


Port of (bugfixed) 0.8.50 acl-ext2 to 2.5

This patch adds ACL support to the ext2 filesystem.

 fs/Kconfig              |   12 +
 fs/ext2/Makefile        |    4
 fs/ext2/acl.c           |  573 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext2/acl.h           |   88 +++++++
 fs/ext2/ext2.h          |    5
 fs/ext2/file.c          |    3
 fs/ext2/ialloc.c        |   37 +--
 fs/ext2/inode.c         |   22 +
 fs/ext2/namei.c         |   10
 fs/ext2/super.c         |   43 +++
 fs/ext2/xattr.c         |   21 +
 include/linux/ext2_fs.h |    1
 12 files changed, 797 insertions(+), 22 deletions(-)

diff -Nru a/fs/Kconfig b/fs/Kconfig
--- a/fs/Kconfig        Thu Oct 31 02:39:46 2002
+++ b/fs/Kconfig        Thu Oct 31 02:39:46 2002
@@ -970,6 +970,18 @@

          If unsure, say N.

+config EXT2_FS_POSIX_ACL
+       bool "Ext2 POSIX Access Control Lists"
+       depends on EXT2_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 SYSV_FS
        tristate "System V/Xenix/V7/Coherent file system support"
        ---help---
diff -Nru a/fs/ext2/Makefile b/fs/ext2/Makefile
--- a/fs/ext2/Makefile  Thu Oct 31 02:39:46 2002
+++ b/fs/ext2/Makefile  Thu Oct 31 02:39:46 2002
@@ -13,4 +13,8 @@
 ext2-objs += xattr.o xattr_user.o
 endif

+ifeq ($(CONFIG_EXT2_FS_POSIX_ACL),y)
+ext2-objs += acl.o
+endif
+
 include $(TOPDIR)/Rules.make
diff -Nru a/fs/ext2/acl.c b/fs/ext2/acl.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/fs/ext2/acl.c     Thu Oct 31 02:39:46 2002
@@ -0,0 +1,573 @@
+/*
+ * linux/fs/ext2/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 "ext2.h"
+#include "xattr.h"
+#include "acl.h"
+
+/*
+ * Convert from filesystem to in-memory representation.
+ */
+static struct posix_acl *
+ext2_acl_from_disk(const void *value, size_t size)
+{
+       const char *end = (char *)value + size;
+       int n, count;
+       struct posix_acl *acl;
+
+       if (!value)
+               return NULL;
+       if (size < sizeof(ext2_acl_header))
+                return ERR_PTR(-EINVAL);
+       if (((ext2_acl_header *)value)->a_version !=
+           cpu_to_le32(EXT2_ACL_VERSION))
+               return ERR_PTR(-EINVAL);
+       value = (char *)value + sizeof(ext2_acl_header);
+       count = ext2_acl_count(size);
+       if (count < 0)
+               return ERR_PTR(-EINVAL);
+       if (count == 0)
+               return NULL;
+       acl = posix_acl_alloc(count, GFP_KERNEL);
+       if (!acl)
+               return ERR_PTR(-ENOMEM);
+       for (n=0; n < count; n++) {
+               ext2_acl_entry *entry =
+                       (ext2_acl_entry *)value;
+               if ((char *)value + sizeof(ext2_acl_entry_short) > end)
+                       goto fail;
+               acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
+               acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
+               switch(acl->a_entries[n].e_tag) {
+                       case ACL_USER_OBJ:
+                       case ACL_GROUP_OBJ:
+                       case ACL_MASK:
+                       case ACL_OTHER:
+                               value = (char *)value +
+                                       sizeof(ext2_acl_entry_short);
+                               acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
+                               break;
+
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               value = (char *)value + sizeof(ext2_acl_entry);
+                               if ((char *)value > end)
+                                       goto fail;
+                               acl->a_entries[n].e_id =
+                                       le32_to_cpu(entry->e_id);
+                               break;
+
+                       default:
+                               goto fail;
+               }
+       }
+       if (value != end)
+               goto fail;
+       return acl;
+
+fail:
+       posix_acl_release(acl);
+       return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Convert from in-memory to filesystem representation.
+ */
+static void *
+ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
+{
+       ext2_acl_header *ext_acl;
+       char *e;
+       int n;
+
+       *size = ext2_acl_size(acl->a_count);
+       ext_acl = (ext2_acl_header *)kmalloc(sizeof(ext2_acl_header) +
+               acl->a_count * sizeof(ext2_acl_entry), GFP_KERNEL);
+       if (!ext_acl)
+               return ERR_PTR(-ENOMEM);
+       ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
+       e = (char *)ext_acl + sizeof(ext2_acl_header);
+       for (n=0; n < acl->a_count; n++) {
+               ext2_acl_entry *entry = (ext2_acl_entry *)e;
+               entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);
+               entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
+               switch(acl->a_entries[n].e_tag) {
+                       case ACL_USER:
+                       case ACL_GROUP:
+                               entry->e_id =
+                                       cpu_to_le32(acl->a_entries[n].e_id);
+                               e += sizeof(ext2_acl_entry);
+                               break;
+
+                       case ACL_USER_OBJ:
+                       case ACL_GROUP_OBJ:
+                       case ACL_MASK:
+                       case ACL_OTHER:
+                               e += sizeof(ext2_acl_entry_short);
+                               break;
+
+                       default:
+                               goto fail;
+               }
+       }
+       return (char *)ext_acl;
+
+fail:
+       kfree(ext_acl);
+       return ERR_PTR(-EINVAL);
+}
+
+/*
+ * inode->i_sem: down
+ */
+static struct posix_acl *
+ext2_get_acl(struct inode *inode, int type)
+{
+       int name_index;
+       char *value;
+       struct posix_acl *acl, **p_acl;
+       const size_t size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES);
+       int retval;
+
+       if (!test_opt(inode->i_sb, POSIX_ACL))
+               return 0;
+
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       p_acl = &EXT2_I(inode)->i_acl;
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       p_acl = &EXT2_I(inode)->i_default_acl;
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
+                       break;
+
+               default:
+                       return ERR_PTR(-EINVAL);
+       }
+       if (*p_acl != EXT2_ACL_NOT_CACHED)
+               return posix_acl_dup(*p_acl);
+       value = kmalloc(size, GFP_KERNEL);
+       if (!value)
+               return ERR_PTR(-ENOMEM);
+
+       retval = ext2_xattr_get(inode, name_index, "", value, size);
+
+       if (retval == -ENODATA || retval == -ENOSYS)
+               *p_acl = acl = NULL;
+       else if (retval < 0)
+               acl = ERR_PTR(retval);
+       else {
+               acl = ext2_acl_from_disk(value, retval);
+               if (!IS_ERR(acl))
+                       *p_acl = posix_acl_dup(acl);
+       }
+       kfree(value);
+       return acl;
+}
+
+/*
+ * inode->i_sem: down
+ */
+static int
+ext2_set_acl(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;
+       if (!test_opt(inode->i_sb, POSIX_ACL))
+               return 0;
+
+       switch(type) {
+               case ACL_TYPE_ACCESS:
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
+                       p_acl = &EXT2_I(inode)->i_acl;
+                       if (acl) {
+                               mode_t mode = inode->i_mode;
+                               error = posix_acl_equiv_mode(acl, &mode);
+                               if (error < 0)
+                                       return error;
+                               else {
+                                       inode->i_mode = mode;
+                                       mark_inode_dirty(inode);
+                                       if (error == 0)
+                                               acl = NULL;
+                               }
+                       }
+                       break;
+
+               case ACL_TYPE_DEFAULT:
+                       name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
+                       p_acl = &EXT2_I(inode)->i_default_acl;
+                       if (!S_ISDIR(inode->i_mode))
+                               return acl ? -EACCES : 0;
+                       break;
+
+               default:
+                       return -EINVAL;
+       }
+       if (acl) {
+               if (acl->a_count > EXT2_ACL_MAX_ENTRIES)
+                       return -EINVAL;
+               value = ext2_acl_to_disk(acl, &size);
+               if (IS_ERR(value))
+                       return (int)PTR_ERR(value);
+       }
+
+       error = ext2_xattr_set(inode, name_index, "", value, size, 0);
+
+       if (value)
+               kfree(value);
+       if (!error) {
+               if (*p_acl && *p_acl != EXT2_ACL_NOT_CACHED)
+                       posix_acl_release(*p_acl);
+               *p_acl = posix_acl_dup(acl);
+       }
+       return error;
+}
+
+static int
+__ext2_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 (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED) {
+                       struct posix_acl *acl;
+
+                       if (lock) {
+                               down(&inode->i_sem);
+                               acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+                               up(&inode->i_sem);
+                       } else
+                               acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+
+                       if (IS_ERR(acl))
+                               return PTR_ERR(acl);
+                       posix_acl_release(acl);
+                       if (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED)
+                               return -EIO;
+               }
+               if (EXT2_I(inode)->i_acl) {
+                       int error = posix_acl_permission(inode,
+                               EXT2_I(inode)->i_acl, mask);
+                       if (error == -EACCES)
+                               goto check_capabilities;
+                       return error;
+               } else
+                       goto check_groups;
+       } else {
+check_groups:
+               if (in_group_p(inode->i_gid))
+                       mode >>= 3;
+       }
+       if ((mode & mask & S_IRWXO) == mask)
+               return 0;
+
+check_capabilities:
+       /* Allowed to override Discretionary Access Control? */
+       if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
+               if (capable(CAP_DAC_OVERRIDE))
+                       return 0;
+       /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */
+       if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) ||
+           (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
+               return 0;
+       return -EACCES;
+}
+
+/*
+ * Inode operation permission().
+ *
+ * inode->i_sem: up
+ * BKL held [before 2.5.x]
+ */
+int
+ext2_permission(struct inode *inode, int mask)
+{
+       return __ext2_permission(inode, mask, 1);
+}
+
+/*
+ * Used internally if i_sem is already down.
+ */
+int
+ext2_permission_locked(struct inode *inode, int mask)
+{
+       return __ext2_permission(inode, mask, 0);
+}
+
+/*
+ * Initialize the ACLs of a new inode. Called from ext2_new_inode.
+ *
+ * dir->i_sem: down
+ * inode->i_sem: up (access to inode is still exclusive)
+ * BKL held [before 2.5.x]
+ */
+int
+ext2_init_acl(struct inode *inode, struct inode *dir)
+{
+       struct posix_acl *acl = NULL;
+       int error = 0;
+
+       if (!S_ISLNK(inode->i_mode)) {
+               if (test_opt(dir->i_sb, POSIX_ACL)) {
+                       acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT);
+                       if (IS_ERR(acl))
+                               return PTR_ERR(acl);
+               }
+               if (!acl)
+                       inode->i_mode &= ~current->fs->umask;
+       }
+       if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
+              
...

read more »