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 »