Linux Access Control Lists -- Kernel Patch
17 May 2002, 15:42:46
This patch is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This patch is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this patch; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
After extracting the linux-2.2.20.tar.gz package and applying the extended
attributes patch, apply this patch as follows:
cd linux
patch -p1 < ../linux-2.2.20acl-0.8.27.patch
diff -Nur linux-2.2.20ea/Documentation/Configure.help linux-2.2.20acl/Documentation/Configure.help
--- linux-2.2.20ea/Documentation/Configure.help Sun Mar 24 21:33:25 2002
+++ linux-2.2.20acl/Documentation/Configure.help Sun Mar 24 22:05:14 2002
@@ -8262,6 +8262,20 @@
The module will be called isp16.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
+Posix Access Control Lists
+CONFIG_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 .
+
+ If you plan to use Access Control Lists, you may also need the
+ getfacl and setfacl utilities, along with some additional patches
+ from the website.
+
+ If you don't know what Access Control Lists are, say N.
+
Quota support
CONFIG_QUOTA
If you say Y here, you will be able to set per user limits for disk
--- linux-2.2.20ea/fs/Config.in Tue Mar 26 00:05:13 2002
+++ linux-2.2.20acl/fs/Config.in Mon Mar 25 23:04:03 2002
@@ -4,6 +4,8 @@
mainmenu_option next_comment
comment 'Filesystems'
+bool 'POSIX Access Control Lists' CONFIG_FS_POSIX_ACL
+
bool 'Quota support' CONFIG_QUOTA
tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
@@ -48,15 +50,19 @@
fi
fi
tristate 'ROM filesystem support' CONFIG_ROMFS_FS
+
tristate 'Second extended fs support' CONFIG_EXT2_FS
dep_mbool ' Ext2 extended attributes' CONFIG_EXT2_FS_XATTR $CONFIG_EXT2_FS
dep_bool ' Ext2 extended attribute block sharing' \
CONFIG_EXT2_FS_XATTR_SHARING $CONFIG_EXT2_FS_XATTR
dep_bool ' Ext2 extended user attributes' \
CONFIG_EXT2_FS_XATTR_USER $CONFIG_EXT2_FS_XATTR
+dep_bool ' Ext2 POSIX Access Control Lists' \
+ CONFIG_EXT2_FS_POSIX_ACL $CONFIG_FS_POSIX_ACL
if [ "$CONFIG_EXT2_FS_XATTR_SHARING" = "y" ]; then
define_tristate CONFIG_FS_MBCACHE $CONFIG_EXT2_FS
fi
+
tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
tristate 'UFS filesystem support' CONFIG_UFS_FS
if [ "$CONFIG_UFS_FS" != "n" ]; then
diff -Nur linux-2.2.20ea/fs/Makefile linux-2.2.20acl/fs/Makefile
--- linux-2.2.20ea/fs/Makefile Sun Mar 24 21:41:25 2002
+++ linux-2.2.20acl/fs/Makefile Sun Mar 24 21:41:39 2002
@@ -27,6 +27,13 @@
MX_OBJS += mbcache.o
endif
+ifeq ($(CONFIG_FS_POSIX_ACL),y)
+OX_OBJS += posix_acl.o
+endif
+ifeq ($(CONFIG_FS_POSIX_ACL),m)
+MX_OBJS += posix_acl.o
+endif
+
ifeq ($(CONFIG_QUOTA),y)
O_OBJS += dquot.o
else
diff -Nur linux-2.2.20ea/fs/ext2/Makefile linux-2.2.20acl/fs/ext2/Makefile
--- linux-2.2.20ea/fs/ext2/Makefile Tue Mar 26 00:04:03 2002
+++ linux-2.2.20acl/fs/ext2/Makefile Mon Mar 25 23:17:03 2002
@@ -8,7 +8,7 @@
# Note 2! The CFLAGS definitions are now in the main makefile...
O_TARGET := ext2.o
-O_OBJS := acl.o balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+O_OBJS := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
ioctl.o namei.o super.o symlink.o truncate.o
M_OBJS := $(O_TARGET)
@@ -18,6 +18,10 @@
ifeq ($(CONFIG_EXT2_FS_XATTR_USER),y)
O_OBJS += xattr_user.o
+endif
+
+ifeq ($(CONFIG_EXT2_FS_POSIX_ACL),y)
+O_OBJS += acl.o
endif
include $(TOPDIR)/Rules.make
diff -Nur linux-2.2.20ea/fs/ext2/acl.c linux-2.2.20acl/fs/ext2/acl.c
--- linux-2.2.20ea/fs/ext2/acl.c Tue Mar 26 00:52:03 2002
+++ linux-2.2.20acl/fs/ext2/acl.c Fri May 17 15:39:39 2002
@@ -1,61 +1,653 @@
/*
* linux/fs/ext2/acl.c
*
- * Copyright (C) 1993, 1994, 1995
- * Remy Card (card@masi.ibp.fr)
- * Laboratoire MASI - Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
+ * Copyright (C) 2001 by Andreas Gruenbacher,
*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
/*
- * This file will contain the Access Control Lists management for the
- * second extended file system.
+ * Convert from filesystem to in-memory representation.
*/
+static posix_acl_t *
+ext2_acl_from_disk(const void *value, size_t size)
+{
+ const char *end = (char *)value + size;
+ int n, count;
+ posix_acl_t *acl;
-#include
-#include
-#include
-#include
-#include
+ 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);
+ 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 posix_acl_t *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);
+}
/*
- * ext2_permission ()
+ * Convert from extended attribute to in-memory representation.
+ */
+static posix_acl_t *
+ext2_acl_from_xattr(const void *value, size_t size)
+{
+ xattr_acl_header *header = (xattr_acl_header *)value;
+ xattr_acl_entry *entry = (xattr_acl_entry *)(header+1), *end;
+ int count;
+ posix_acl_t *acl;
+ posix_acl_entry_t *acl_e;
+
+ if (!value)
+ return NULL;
+ if (size < sizeof(xattr_acl_header))
+ return ERR_PTR(-EINVAL);
+ if (header->a_version != cpu_to_le32(XATTR_ACL_VERSION))
+ return ERR_PTR(-EINVAL);
+
+ count = xattr_acl_count(size);
+ if (count < 0)
+ return ERR_PTR(-EINVAL);
+ if (count == 0)
+ return NULL;
+
+ acl = posix_acl_alloc(count);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ acl_e = acl->a_entries;
+
+ for (end = entry + count; entry != end; acl_e++, entry++) {
+ acl_e->e_tag = le16_to_cpu(entry->e_tag);
+ acl_e->e_perm = le16_to_cpu(entry->e_perm);
+
+ switch(acl_e->e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ acl_e->e_id = ACL_UNDEFINED_ID;
+ break;
+
+ case ACL_USER:
+ case ACL_GROUP:
+ acl_e->e_id = le32_to_cpu(entry->e_id);
+ break;
+
+ default:
+ goto fail;
+ }
+ }
+ return acl;
+
+fail:
+ posix_acl_release(acl);
+ return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Convert from in-memory to extended attribute representation.
+ */
+static int
+ext2_acl_to_xattr(const posix_acl_t *acl, void *buffer, size_t size)
+{
+ xattr_acl_header *ext_acl = (xattr_acl_header *)buffer;
+ xattr_acl_entry *ext_entry = ext_acl->a_entries;
+ int real_size, n;
+
+ real_size = xattr_acl_size(acl->a_count);
+ if (!buffer)
+ return real_size;
+ if (real_size > size)
+ return -ERANGE;
+
+ ext_acl->a_version = cpu_to_le32(XATTR_ACL_VERSION);
+
+ for (n=0; n < acl->a_count; n++, ext_entry++) {
+ ext_entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
+ ext_entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
+ ext_entry->e_id = cpu_to_le32(acl->a_entries[n].e_id);
+ }
+ return real_size;
+}
+
+/*
+ * Inode operation get_posix_acl().
+ *
+ * inode->i_sem: down
+ * BKL held [before 2.5.x]
+ */
+posix_acl_t *
+ext2_get_acl(struct inode *inode, int type)
+{
+ int name_index;
+ char *value;
+ posix_acl_t *acl, **p_acl;
+ const size_t size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES);
+ int retval;
+
+ 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 == -ENOATTR || 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 operation set_posix_acl().
*
- * Check for access rights
+ * inode->i_sem: down
+ * BKL held [before 2.5.x]
*/
-int ext2_permission (struct inode * inode, int mask)
+int
+ext2_set_acl(struct inode *inode, int type, posix_acl_t *acl)
{
- unsigned short mode = inode->i_mode;
+ int name_index;
+ void *value = NULL;
+ posix_acl_t **p_acl;
+ size_t size;
+ int error;
- /*
- * Nobody gets write access to a file on a readonly-fs
- */
- if ((mask & S_IWOTH) &&
- (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
- IS_RDONLY(inode))
+ if (S_ISLNK(inode->i_mode))
+ return -ENOTSUP;
+
+ 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;
+}
+
+/*
+ * Inode operation permission().
+ *
+ * inode->i_sem: up
+ * BKL held [before 2.5.x]
+ */
+int
+ext2_permission(struct inode *inode, int mask)
+{
+ 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 & S_IWOTH) && IS_IMMUTABLE(inode))
- return -EACCES;
-
- /*
- * If no ACL, checks using the file mode
- */
- else if (current->fsuid == inode->i_uid)
+ /* 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 (in_group_p (inode->i_gid))
- mode >>= 3;
- /*
- * Access is always granted for root. We now check last,
- * though, for BSD process accounting correctness
- */
- if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE))
- return 0;
- if ((mask == S_IROTH) ||
- (S_ISDIR(inode->i_mode) && !(mask & ~(S_IROTH | S_IXOTH))))
- if (capable(CAP_DAC_READ_SEARCH))
- return 0;
+ } else if (IS_POSIX_ACL(inode)) {
+ /* ACL can't contain additional permissions if
+ the ACL_MASK entry is 0 */
+ if (!(mode & S_IRWXG))
+ goto check_mode;
+ if (EXT2_I(inode)->i_acl == EXT2_ACL_NOT_CACHED) {
+ posix_acl_t *acl;
+
+ down(&inode->i_sem);
+ acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+ up(&inode->i_sem);
+
+ 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;
+ }
+check_mode:
+ if ((mode & mask & S_IRWXO) == mask)
+ return 0;
+
+check_capabilities:
+ /* Allowed to override Discretionary Access Control? */
+ 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(mode) && !(mask & ~(MAY_READ | MAY_EXEC)))))
+ return 0;
return -EACCES;
+}
+
+/*
+ * Determine whether a process has a valid fs_struct (kernel daemons
+ * like knfsd don't have an fs_struct).
+ */
+static inline int
+has_fs_struct(struct task_struct *task)
+{
+ return (task->fs != init_task.fs);
+}
+
+/*
+ * 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)
+{
+ posix_acl_t *acl = NULL;
+ int error = 0;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ if (IS_POSIX_ACL(dir)) {
+ acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ }
+ if (!acl && has_fs_struct(current)) {
+ inode->i_mode &= ~current->fs->umask;
+ mark_inode_dirty(inode);
+ }
+ }
+ if (IS_POSIX_ACL(inode) && acl) {
+ posix_acl_t *clone;
+ mode_t mode;
+
+ if (S_ISDIR(inode->i_mode)) {
+ error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
+ if (error)
+ goto cleanup;
+ }
+ clone = posix_acl_clone(acl);
+ error = -ENOMEM;
+ if (!clone)
+ goto cleanup;
+ mode = inode->i_mode;
+ error = posix_acl_create_masq(clone, &mode);
+ if (error >= 0) {
+ inode->i_mode = mode;
+ mark_inode_dirty(inode);
+ if (error > 0) {
+ /* This is an extended ACL */
+ error = ext2_set_acl(inode,
+ ACL_TYPE_ACCESS, clone);
+ }
+ }
+ posix_acl_release(clone);
+ }
+cleanup:
+ posix_acl_release(acl);
+ return error;
+}
+
+/*
+ * Does chmod for an inode that may have an Access Control List. The
+ * inode->i_mode field must be updated to the desired value by the caller
+ * before calling this function.
+ * Returns 0 on success, or a negative error number.
+ *
+ * We change the ACL rather than storing some ACL entries in the file
+ * mode permission bits (which would be more efficient), because that
+ * would break once additional permissions (like ACL_APPEND, ACL_DELETE
+ * for directories) are added. There are no more bits available in the
+ * file mode.
+ *
+ * inode->i_sem: down
+ * BKL held [before 2.5.x]
+ */
+int
+ext2_acl_chmod(struct inode *inode)
+{
+ posix_acl_t *acl, *clone;
+ int error;
+
+ if (!IS_POSIX_ACL(inode))
+ return 0;
+ if (S_ISLNK(inode->i_mode))
+ return -ENOTSUP;
+ acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
+ if (IS_ERR(acl) || !acl)
+ return PTR_ERR(acl);
+ clone = posix_acl_clone(acl);
+ posix_acl_release(acl);
+ if (!clone)
+ return -ENOMEM;
+ error = posix_acl_chmod_masq(clone, inode->i_mode);
+ if (!error)
+ error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone);
+ posix_acl_release(clone);
+ return error;
+}
+
+/*
+ * Extended attribut handlers
+ */
+static size_t
+ext2_xattr_list_acl_access(char *list, struct inode *inode,
+ const char *name, int name_len)
+{
+ const size_t len = sizeof(XATTR_NAME_ACL_ACCESS)-1;
+
+ if (!IS_POSIX_ACL(inode))
+ return 0;
+ if (list)
+ memcpy(list, XATTR_NAME_ACL_ACCESS, len);
+ return len;
+}
+
+static size_t
+ext2_xattr_list_acl_default(char *list, struct inode *inode,
+ const char *name, int name_len)
+{
+ const size_t len = sizeof(XATTR_NAME_ACL_DEFAULT)-1;
+
+ if (!IS_POSIX_ACL(inode))
+ return 0;
+ if (list)
+ memcpy(list, XATTR_NAME_ACL_DEFAULT, len);
+ return len;
+}
+
+static int
+ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
+{
+ posix_acl_t *acl;
+ int error;
+
+ if (!IS_POSIX_ACL(inode))
+ return -ENOTSUP;
+
+ acl = ext2_get_acl(inode, type);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl == NULL)
+ return -ENOATTR;
+ error = ext2_acl_to_xattr(acl, buffer, size);
+ posix_acl_release(acl);
+
+ return error;
+}
+
+static int
+ext2_xattr_get_acl_access(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return ext2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
+}
+
+static int
+ext2_xattr_get_acl_default(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return ext2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
+}
+
+static int
+ext2_xattr_set_acl(struct inode *inode, int type, void *value, size_t size)
+{
+ posix_acl_t *acl;
+ int error;
+
+ if (!IS_POSIX_ACL(inode))
+ return -ENOTSUP;
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+
+ if (value) {
+ acl = ext2_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 = ext2_set_acl(inode, type, acl);
+
+release_and_out:
+ posix_acl_release(acl);
+ return error;
+}
+
+static int
+ext2_xattr_set_acl_access(struct inode *inode, const char *name,
+ void *value, size_t size, int flags)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return ext2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
+}
+
+static int
+ext2_xattr_set_acl_default(struct inode *inode, const char *name,
+ void *value, size_t size, int flags)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return ext2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
+}
+
+struct ext2_xattr_handler ext2_xattr_acl_access_handler = {
+ prefix: XATTR_NAME_ACL_ACCESS,
+ list: ext2_xattr_list_acl_access,
+ get: ext2_xattr_get_acl_access,
+ set: ext2_xattr_set_acl_access,
+};
+
+struct ext2_xattr_handler ext2_xattr_acl_default_handler = {
+ prefix: XATTR_NAME_ACL_DEFAULT,
+ list: ext2_xattr_list_acl_default,
+ get: ext2_xattr_get_acl_default,
+ set: ext2_xattr_set_acl_default,
+};
+
+void
+exit_ext2_acl(void)
+{
+ ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS,
+ &ext2_xattr_acl_access_handler);
+ ext2_xattr_unregister(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT,
+ &ext2_xattr_acl_default_handler);
+}
+
+int __init
+init_ext2_acl(void)
+{
+ int error;
+
+ error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_ACCESS,
+ &ext2_xattr_acl_access_handler);
+ if (error)
+ goto fail;
+ error = ext2_xattr_register(EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT,
+ &ext2_xattr_acl_default_handler);
+ if (error)
+ goto fail;
+ return 0;
+
+fail:
+ exit_ext2_acl();
+ return error;
}
diff -Nur linux-2.2.20ea/fs/ext2/dir.c linux-2.2.20acl/fs/ext2/dir.c
--- linux-2.2.20ea/fs/ext2/dir.c Thu Feb 28 23:47:17 2002
+++ linux-2.2.20acl/fs/ext2/dir.c Sat Mar 9 18:14:14 2002
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
@@ -82,6 +83,8 @@
ext2_getxattr, /* getxattr */
ext2_listxattr, /* listxattr */
ext2_removexattr, /* removexattr */
+ ext2_get_acl, /* get_posix_acl */
+ ext2_set_acl, /* set_posix_acl */
};
int ext2_check_dir_entry (const char * function, struct inode * dir,
diff -Nur linux-2.2.20ea/fs/ext2/file.c linux-2.2.20acl/fs/ext2/file.c
--- linux-2.2.20ea/fs/ext2/file.c Thu Feb 28 23:46:43 2002
+++ linux-2.2.20acl/fs/ext2/file.c Sat Mar 9 18:15:14 2002
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -110,6 +111,8 @@
ext2_getxattr, /* getxattr */
ext2_listxattr, /* listxattr */
ext2_removexattr, /* removexattr */
+ ext2_get_acl, /* get_posix_acl */
+ ext2_set_acl, /* set_posix_acl */
};
/*
diff -Nur linux-2.2.20ea/fs/ext2/ialloc.c linux-2.2.20acl/fs/ext2/ialloc.c
--- linux-2.2.20ea/fs/ext2/ialloc.c Fri Mar 1 00:21:04 2002
+++ linux-2.2.20acl/fs/ext2/ialloc.c Sat Mar 9 18:27:54 2002
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -300,6 +301,8 @@
ext2_getxattr, /* getxattr */
ext2_listxattr, /* listxattr */
ext2_removexattr, /* removexattr */
+ ext2_get_acl, /* get_posix_acl */
+ ext2_set_acl, /* set_posix_acl */
};
struct inode_operations ext2_blkdev_inode_operations = {
@@ -329,6 +332,8 @@
ext2_getxattr, /* getxattr */
ext2_listxattr, /* listxattr */
ext2_removexattr, /* removexattr */
+ ext2_get_acl, /* get_posix_acl */
+ ext2_set_acl, /* set_posix_acl */
};
struct inode_operations ext2_fifo_inode_operations = {
@@ -358,6 +363,8 @@
ext2_getxattr, /* getxattr */
ext2_listxattr, /* listxattr */
ext2_removexattr, /* removexattr */
+ ext2_get_acl, /* get_posix_acl */
+ ext2_set_acl, /* set_posix_acl */
};
struct inode_operations ext2_sock_inode_operations = {
@@ -387,6 +394,8 @@
ext2_getxattr, /* getxattr */
ext2_listxattr, /* listxattr */
ext2_removexattr, /* removexattr */
+ ext2_get_acl, /* get_posix_acl */
+ ext2_set_acl, /* set_posix_acl */
};
/*
@@ -399,7 +408,7 @@
* For other inodes, search forward from the parent directory\'s block
* group to find a free inode.
*/
-struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
+struct inode * ext2_new_inode (struct inode * dir, int mode, int * err)
{
struct super_block * sb;
struct buffer_head * bh;
@@ -613,15 +622,23 @@
unlock_super (sb);
if(DQUOT_ALLOC_INODE(sb, inode)) {
sb->dq_op->drop(inode);
- inode->i_nlink = 0;
- iput(inode);
*err = -EDQUOT;
- return NULL;
+ goto fail;
+ }
+ *err = ext2_init_acl(inode, dir);
+ if (*err) {
+ DQUOT_FREE_INODE(sb, inode);
+ goto fail;
}
ext2_debug ("allocating inode %lu\n", inode->i_ino);
*err = 0;
return inode;
+
+fail:
+ inode->i_nlink = 0;
+ iput(inode);
+ return NULL;
}
unsigned long ext2_count_free_inodes (struct super_block * sb)
diff -Nur linux-2.2.20ea/fs/ext2/inode.c linux-2.2.20acl/fs/ext2/inode.c
--- linux-2.2.20ea/fs/ext2/inode.c Thu Feb 28 23:57:53 2002
+++ linux-2.2.20acl/fs/ext2/inode.c Sat Mar 9 19:26:04 2002
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -589,6 +590,15 @@
inode->i_attr_flags |= ATTR_FLAG_NOATIME;
inode->i_flags |= MS_NOATIME;
}
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+ if (IS_POSIX_ACL(inode) && inode->u.ext2_i.i_file_acl) {
+ /* The filesystem is mounted with ACL support, and there
+ are extended attributes for this inode. However we do
+ not yet know whether there are actually any ACLs. */
+ inode->u.ext2_i.i_acl = EXT2_ACL_NOT_CACHED;
+ inode->u.ext2_i.i_default_acl = EXT2_ACL_NOT_CACHED;
+ }
+#endif
return;
bad_inode:
@@ -790,6 +800,10 @@
}
}
mark_inode_dirty(inode);
+
+ if (iattr->ia_valid & ATTR_MODE)
+ retval = ext2_acl_chmod(inode);
+
out:
return retval;
}
diff -Nur linux-2.2.20ea/fs/ext2/namei.c linux-2.2.20acl/fs/ext2/namei.c
--- linux-2.2.20ea/fs/ext2/namei.c Fri Mar 1 00:18:29 2002
+++ linux-2.2.20acl/fs/ext2/namei.c Sat Mar 9 18:34:07 2002
@@ -371,7 +371,6 @@
return err;
inode->i_op = &ext2_file_inode_operations;
- inode->i_mode = mode;
mark_inode_dirty(inode);
bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh) {
@@ -407,8 +406,6 @@
if (!inode)
goto out;
- inode->i_uid = current->fsuid;
- inode->i_mode = mode;
inode->i_op = NULL;
bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh)
@@ -476,13 +473,12 @@
goto out;
err = -EIO;
- inode = ext2_new_inode (dir, S_IFDIR, &err);
+ inode = ext2_new_inode (dir, S_IFDIR | mode, &err);
if (!inode)
goto out;
inode->i_op = &ext2_dir_inode_operations;
inode->i_size = inode->i_sb->s_blocksize;
- inode->i_blocks = 0;
dir_block = ext2_bread (inode, 0, 1, &err);
if (!dir_block) {
ext2_xattr_drop_inode(inode);
@@ -510,9 +506,6 @@
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
brelse (dir_block);
- inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
- if (dir->i_mode & S_ISGID)
- inode->i_mode |= S_ISGID;
mark_inode_dirty(inode);
bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh)
@@ -714,10 +707,9 @@
int i, l, err = -EIO;
char c;
- if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
+ if (!(inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO, &err))) {
return err;
}
- inode->i_mode = S_IFLNK | S_IRWXUGO;
inode->i_op = &ext2_symlink_inode_operations;
for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
symname [l]; l++)
diff -Nur linux-2.2.20ea/fs/ext2/super.c linux-2.2.20acl/fs/ext2/super.c
--- linux-2.2.20ea/fs/ext2/super.c Tue Mar 26 00:11:54 2002
+++ linux-2.2.20acl/fs/ext2/super.c Mon Mar 25 23:12:14 2002
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -131,6 +132,24 @@
return;
}
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+static void ext2_clear_inode(struct inode *inode)
+{
+ if (inode->u.ext2_i.i_acl &&
+ inode->u.ext2_i.i_acl != EXT2_ACL_NOT_CACHED) {
+ posix_acl_release(inode->u.ext2_i.i_acl);
+ inode->u.ext2_i.i_acl = NULL;
+ }
+ if (inode->u.ext2_i.i_default_acl &&
+ inode->u.ext2_i.i_default_acl != EXT2_ACL_NOT_CACHED) {
+ posix_acl_release(inode->u.ext2_i.i_default_acl);
+ inode->u.ext2_i.i_default_acl = NULL;
+ }
+}
+#else
+# define ext2_clear_inode NULL
+#endif
+
static struct super_operations ext2_sops = {
ext2_read_inode,
ext2_write_inode,
@@ -140,7 +159,8 @@
ext2_put_super,
ext2_write_super,
ext2_statfs,
- ext2_remount
+ ext2_remount,
+ ext2_clear_inode,
};
/*
@@ -172,6 +192,12 @@
*c++ = '\0';
if (!strcmp(value, "off"))
flags = 0;
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+ else if (!strcmp(value, "acl"))
+ flags |= XATTR_MNT_FLAG_POSIX_ACL;
+ else if (!strcmp(value, "noacl"))
+ flags &= ~XATTR_MNT_FLAG_POSIX_ACL;
+#endif
#ifdef CONFIG_EXT2_FS_XATTR_USER
else if (!strcmp(value, "user"))
flags |= XATTR_MNT_FLAG_USER;
@@ -437,6 +463,9 @@
sb->u.ext2_sb.s_mount_opt = 0;
set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
sb->s_xattr_flags = 0;
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+ sb->s_xattr_flags |= XATTR_MNT_FLAG_POSIX_ACL;
+#endif
#ifdef CONFIG_EXT2_FS_XATTR_USER
sb->s_xattr_flags |= XATTR_MNT_FLAG_USER;
#endif
@@ -775,6 +804,7 @@
static void exit_ext2_fs(void)
{
unregister_filesystem(&ext2_fs_type);
+ exit_ext2_acl();
exit_ext2_xattr_user();
exit_ext2_xattr();
}
@@ -784,6 +814,8 @@
int error = init_ext2_xattr();
if (!error)
error = init_ext2_xattr_user();
+ if (!error)
+ error = init_ext2_acl();
if (!error)
error = register_filesystem(&ext2_fs_type);
if (!error)
diff -Nur linux-2.2.20ea/fs/ext2/truncate.c linux-2.2.20acl/fs/ext2/truncate.c
--- linux-2.2.20ea/fs/ext2/truncate.c Thu Mar 14 21:56:42 2002
+++ linux-2.2.20acl/fs/ext2/truncate.c Thu Mar 14 21:24:00 2002
@@ -386,8 +386,12 @@
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return;
- if (ext2_inode_is_fast_symlink(inode))
- return;
+ if (S_ISLNK(inode->i_mode)) {
+ int ea_blocks = inode->u.ext2_i.i_file_acl ?
+ (inode->i_sb->s_blocksize >> 9) : 0;
+ if (inode->i_blocks - ea_blocks == 0)
+ return;
+ }
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return;
diff -Nur linux-2.2.20ea/fs/namei.c linux-2.2.20acl/fs/namei.c
--- linux-2.2.20ea/fs/namei.c Fri Nov 2 17:39:08 2001
+++ linux-2.2.20acl/fs/namei.c Sat Mar 9 18:40:20 2002
@@ -683,9 +683,6 @@
struct inode *inode;
struct dentry *dentry;
- mode &= S_IALLUGO & ~current->fs->umask;
- mode |= S_IFREG;
-
dentry = lookup_dentry(pathname, NULL, lookup_flags(flag));
if (IS_ERR(dentry))
return dentry;
@@ -730,8 +727,12 @@
if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
error = -EACCES;
else {
+ if (!IS_POSIX_ACL(dir->d_inode))
+ mode &= ~current->fs->umask;
+
DQUOT_INIT(dir->d_inode);
- error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);
+ error = dir->d_inode->i_op->create(dir->d_inode,
+ dentry, S_IFREG | (mode & S_IALLUGO));
/* Don't check for write permission, don't truncate */
acc_mode = 0;
flag &= ~O_TRUNC;
@@ -823,7 +824,6 @@
struct dentry *dir;
struct dentry *dentry, *retval;
- mode &= ~current->fs->umask;
dentry = lookup_dentry(filename, NULL, 0);
if (IS_ERR(dentry))
return dentry;
@@ -841,6 +841,9 @@
if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod)
goto exit_lock;
+ if (!IS_POSIX_ACL(dir->d_inode))
+ mode &= ~current->fs->umask;
+
DQUOT_INIT(dir->d_inode);
error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);
exit_lock:
@@ -923,9 +926,11 @@
if (!dir->d_inode->i_op || !dir->d_inode->i_op->mkdir)
goto exit_lock;
+ if (!IS_POSIX_ACL(dir->d_inode))
+ mode &= ~current->fs->umask;
+
DQUOT_INIT(dir->d_inode);
- mode &= 0777 & ~current->fs->umask;
- error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);
+ error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode & S_IALLUGO);
exit_lock:
unlock_dir(dir);
diff -Nur linux-2.2.20ea/fs/nfsd/nfs3xdr.c linux-2.2.20acl/fs/nfsd/nfs3xdr.c
--- linux-2.2.20ea/fs/nfsd/nfs3xdr.c Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/nfs3xdr.c Fri May 17 15:36:42 2002
@@ -151,21 +151,34 @@
}
static inline u32 *
-encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
{
- struct inode *inode = dentry->d_inode;
+ struct inode *inode = fhp->fh_dentry->d_inode;
+ mode_t mode = inode->i_mode;
if (!inode) {
printk("nfsd: NULL inode in %s:%d", __FILE__, __LINE__);
return NULL;
}
+ if (nfs_permission_mode == NFS_FILE_MODE_PERMISSION_BITS_SECURE) {
+ posix_acl_t *acl = nfsd_get_posix_acl(fhp, ACL_TYPE_ACCESS);
+
+ if (IS_ERR(acl))
+ return 0;
+ else if (acl) {
+ int error = posix_acl_masq_nfs_mode(acl, &mode);
+ posix_acl_release(acl);
+ if (error)
+ return 0;
+ }
+ }
- *p++ = htonl(nfs3_ftypes[(inode->i_mode & S_IFMT) >> 12]);
- *p++ = htonl((u32) inode->i_mode);
+ *p++ = htonl(nfs3_ftypes[(mode & S_IFMT) >> 12]);
+ *p++ = htonl((u32) mode);
*p++ = htonl((u32) inode->i_nlink);
*p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid));
*p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid));
- if (S_ISLNK(inode->i_mode) && inode->i_size > NFS3_MAXPATHLEN) {
+ if (S_ISLNK(mode) && inode->i_size > NFS3_MAXPATHLEN) {
p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
} else {
p = xdr_encode_hyper(p, (u64) inode->i_size);
@@ -231,11 +244,11 @@
* handle. In this case, no attributes are returned.
*/
static u32 *
-encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry)
+encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
{
- if (dentry && dentry->d_inode != NULL) {
+ if (fhp->fh_dentry && fhp->fh_dentry->d_inode != NULL) {
*p++ = xdr_one; /* attributes follow */
- return encode_fattr3(rqstp, p, dentry);
+ return encode_fattr3(rqstp, p, fhp);
}
*p++ = xdr_zero;
return p;
@@ -262,7 +275,7 @@
}
/* no pre- or post-attrs */
*p++ = xdr_zero;
- return encode_post_op_attr(rqstp, p, dentry);
+ return encode_post_op_attr(rqstp, p, fhp);
}
/*
@@ -520,7 +533,7 @@
struct nfsd3_attrstat *resp)
{
if (resp->status == 0
- && !(p = encode_fattr3(rqstp, p, resp->fh.fh_dentry)))
+ && !(p = encode_fattr3(rqstp, p, &resp->fh)))
return 0;
return xdr_ressize_check(rqstp, p);
}
@@ -542,9 +555,9 @@
{
if (resp->status == 0) {
p = encode_fh(p, &resp->fh);
- p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
}
- p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->dirfh);
return xdr_ressize_check(rqstp, p);
}
@@ -553,7 +566,7 @@
nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
struct nfsd3_accessres *resp)
{
- p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
if (resp->status == 0)
*p++ = htonl(resp->access);
return xdr_ressize_check(rqstp, p);
@@ -564,7 +577,7 @@
nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
struct nfsd3_readlinkres *resp)
{
- p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
if (resp->status == 0) {
*p++ = htonl(resp->len);
p += XDR_QUADLEN(resp->len);
@@ -577,7 +590,7 @@
nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
struct nfsd3_readres *resp)
{
- p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
if (resp->status == 0) {
*p++ = htonl(resp->count);
*p++ = htonl(resp->eof);
@@ -610,7 +623,7 @@
if (resp->status == 0) {
*p++ = xdr_one;
p = encode_fh(p, &resp->fh);
- p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
}
p = encode_wcc_data(rqstp, p, &resp->dirfh);
return xdr_ressize_check(rqstp, p);
@@ -631,7 +644,7 @@
nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
struct nfsd3_linkres *resp)
{
- p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
p = encode_wcc_data(rqstp, p, &resp->tfh);
return xdr_ressize_check(rqstp, p);
}
@@ -641,7 +654,7 @@
nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
struct nfsd3_readdirres *resp)
{
- p = encode_post_op_attr(rqstp, p, resp->fh.fh_dentry);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
if (resp->status == 0) {
/* stupid readdir cookie */
memcpy(p, resp->verf, 8); p += 2;
@@ -708,7 +721,7 @@
fh_init(&fh);
/* Disabled for now because of lock-up */
if (0 && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh) == 0) {
- p = encode_post_op_attr(cd->rqstp, p, fh.fh_dentry);
+ p = encode_post_op_attr(cd->rqstp, p, &fh);
p = encode_fh(p, &fh);
fh_put(&fh);
} else {
diff -Nur linux-2.2.20ea/fs/nfsd/nfsctl.c linux-2.2.20acl/fs/nfsd/nfsctl.c
--- linux-2.2.20ea/fs/nfsd/nfsctl.c Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/nfsctl.c Mon Apr 22 07:54:06 2002
@@ -8,6 +8,7 @@
#define NFS_GETFH_NEW
#include
+#include
#include
#include
@@ -313,13 +314,41 @@
return err;
}
+/*
+ About the nfs_permission_mode module parameter:
+
+ With Posix Access Control Lists, pre-NFS3 clients and older Linux
+ NFSv3 clients in some cases mis-interpret the file mode permission
+ bits, and either allow the remote user to read data she is not
+ permitted to, or deny the user read access that should be granted.
+ (With proper NFSv3, the access RPC is used to check access, and
+ access decisions are not implemented on the client.)
+
+ The nfs_permission_mode module parameter can either send the mode
+ permission bits (inode->i_mode & S_IRWXUGO) of files that have
+ Access Control Lists unchanged, or remove all permissions that
+ might lead to wrong decisions on the client machine.
+
+ The file mode permission bits should only be set to 'unchanged' in
+ an environment where it is known that all NFSv3 clients use the
+ access RPC. (The file mode permission bits for NFSv2 clients are
+ always "secured".)
+*/
+
+int nfs_permission_mode = NFS_FILE_MODE_PERMISSION_BITS_SECURE;
+
#ifdef MODULE
+
/* New-style module support since 2.1.18 */
#if LINUX_VERSION_CODE >= 131346
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Olaf Kirch ");
#endif
+MODULE_PARM(nfs_permission_mode, "i");
+MODULE_PARM_DESC(nfs_permission_mode,
+ "nfsd file mode permission bits: 0=unchanged, 1=secure (1)");
+
extern int (*do_nfsservctl)(int, void *, void *);
/*
@@ -367,4 +396,18 @@
nfsd_stat_shutdown();
nfsd_lockd_shutdown();
}
+
+#else
+
+static int __init
+nfs_permission_mode_setup(char *str)
+{
+ nfs_permission_mode = simple_strtol(str, &str, 10) ?
+ NFS_FILE_MODE_PERMISSION_BITS_SECURE :
+ NFS_FILE_MODE_PERMISSION_BITS_UNCHANGED;
+ return 0;
+}
+
+__setup("nfs_permission_mode=", nfs_permission_mode_setup);
+
#endif
diff -Nur linux-2.2.20ea/fs/nfsd/nfsxdr.c linux-2.2.20acl/fs/nfsd/nfsxdr.c
--- linux-2.2.20ea/fs/nfsd/nfsxdr.c Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/nfsxdr.c Fri May 17 15:31:37 2002
@@ -133,24 +133,37 @@
}
static inline u32 *
-encode_fattr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
{
- int type = (inode->i_mode & S_IFMT);
+ posix_acl_t *acl = nfsd_get_posix_acl(fhp, ACL_TYPE_ACCESS);
+ struct inode *inode = fhp->fh_dentry->d_inode;
+ mode_t mode = inode->i_mode;
+
if (!inode)
return 0;
- *p++ = htonl(nfs_ftypes[type >> 12]);
- *p++ = htonl((u32) inode->i_mode);
+
+ if (IS_ERR(acl))
+ return 0;
+ else if (acl) {
+ int error = posix_acl_masq_nfs_mode(acl, &mode);
+ posix_acl_release(acl);
+ if (error)
+ return 0;
+ }
+
+ *p++ = htonl(nfs_ftypes[(mode & S_IFMT) >> 12]);
+ *p++ = htonl((u32) mode);
*p++ = htonl((u32) inode->i_nlink);
*p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid));
*p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid));
- if (S_ISLNK(type) && inode->i_size > NFS_MAXPATHLEN) {
+ if (S_ISLNK(mode) && inode->i_size > NFS_MAXPATHLEN) {
*p++ = htonl(NFS_MAXPATHLEN);
} else {
*p++ = htonl((u32) inode->i_size);
}
*p++ = htonl((u32) inode->i_blksize);
- if (S_ISCHR(type) || S_ISBLK(type))
+ if (S_ISCHR(mode) || S_ISBLK(mode))
*p++ = htonl((u32) inode->i_rdev);
else
*p++ = htonl(0xffffffff);
@@ -334,7 +347,7 @@
nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
struct nfsd_attrstat *resp)
{
- if (!(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
+ if (!(p = encode_fattr(rqstp, p, &resp->fh)))
return 0;
return xdr_ressize_check(rqstp, p);
}
@@ -344,7 +357,7 @@
struct nfsd_diropres *resp)
{
if (!(p = encode_fh(p, &resp->fh))
- || !(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
+ || !(p = encode_fattr(rqstp, p, &resp->fh)))
return 0;
return xdr_ressize_check(rqstp, p);
}
@@ -362,7 +375,7 @@
nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
struct nfsd_readres *resp)
{
- if (!(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
+ if (!(p = encode_fattr(rqstp, p, &resp->fh)))
return 0;
*p++ = htonl(resp->count);
p += XDR_QUADLEN(resp->count);
diff -Nur linux-2.2.20ea/fs/nfsd/vfs.c linux-2.2.20acl/fs/nfsd/vfs.c
--- linux-2.2.20ea/fs/nfsd/vfs.c Sun Mar 25 18:37:38 2001
+++ linux-2.2.20acl/fs/nfsd/vfs.c Fri May 17 15:32:39 2002
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1799,3 +1800,22 @@
nfsd_nservers = 0;
}
}
+
+#ifdef CONFIG_FS_POSIX_ACL
+posix_acl_t *
+nfsd_get_posix_acl(struct svc_fh *fhp, int type)
+{
+ struct inode *inode = fhp->fh_dentry->d_inode;
+ posix_acl_t *acl = NULL;
+
+ if (inode && IS_POSIX_ACL(inode) && inode->i_op && inode->i_op->get_posix_acl) {
+ if (!fhp->fh_locked)
+ fh_lock(fhp); /* unlocking is done automatically */
+ lock_kernel(); /* goes away in 2.5 */
+ acl = inode->i_op->get_posix_acl(inode, type);
+ unlock_kernel(); /* goes away in 2.5 */
+ }
+
+ return acl;
+}
+#endif
diff -Nur linux-2.2.20ea/fs/posix_acl.c linux-2.2.20acl/fs/posix_acl.c
--- linux-2.2.20ea/fs/posix_acl.c Thu Jan 1 01:00:00 1970
+++ linux-2.2.20acl/fs/posix_acl.c Fri May 17 15:38:17 2002
@@ -0,0 +1,495 @@
+/*
+ * linux/fs/posix_acl.c
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher
+ *
+ * Fixes from William Schumacher incorporated on 15 March 2001.
+ * (Reported by Charles Bertsch, ).
+ */
+
+/*
+ * This file contains generic functions for manipulating
+ * POSIX 1003.1e draft standard 17 ACLs.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+MODULE_AUTHOR("Andreas Gruenbacher ");
+MODULE_DESCRIPTION("Generic Posix Access Control List (ACL) Manipulation");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+MODULE_LICENSE("GPL");
+#endif
+
+EXPORT_SYMBOL(posix_acl_alloc);
+EXPORT_SYMBOL(posix_acl_dup);
+EXPORT_SYMBOL(posix_acl_clone);
+EXPORT_SYMBOL(posix_acl_release);
+EXPORT_SYMBOL(posix_acl_valid);
+EXPORT_SYMBOL(posix_acl_equiv_mode);
+EXPORT_SYMBOL(posix_acl_from_mode);
+EXPORT_SYMBOL(posix_acl_create_masq);
+EXPORT_SYMBOL(posix_acl_chmod_masq);
+EXPORT_SYMBOL(posix_acl_masq_nfs_mode);
+EXPORT_SYMBOL(posix_acl_permission);
+
+EXPORT_SYMBOL(get_posix_acl);
+EXPORT_SYMBOL(set_posix_acl);
+
+/*
+ * Allocate a new ACL with the specified number of entries.
+ */
+posix_acl_t *
+posix_acl_alloc(int count)
+{
+ const size_t size = sizeof(posix_acl_t) +
+ count * sizeof(posix_acl_entry_t);
+ posix_acl_t *acl = kmalloc(size, GFP_KERNEL);
+ if (acl) {
+ atomic_set(&acl->a_refcount, 1);
+ acl->a_count = count;
+ }
+ return acl;
+}
+
+/*
+ * Duplicate an ACL handle.
+ */
+posix_acl_t *
+posix_acl_dup(posix_acl_t *acl)
+{
+ if (acl)
+ atomic_inc(&acl->a_refcount);
+ return acl;
+}
+
+/*
+ * Clone an ACL.
+ */
+posix_acl_t *
+posix_acl_clone(const posix_acl_t *acl)
+{
+ posix_acl_t *clone = NULL;
+
+ if (acl) {
+ int size = sizeof(posix_acl_t) + acl->a_count *
+ sizeof(posix_acl_entry_t);
+ clone = kmalloc(size, GFP_KERNEL);
+ if (clone) {
+ memcpy(clone, acl, size);
+ atomic_set(&clone->a_refcount, 1);
+ }
+ }
+ return clone;
+}
+
+/*
+ * Free an ACL handle.
+ */
+void
+posix_acl_release(posix_acl_t *acl)
+{
+ if (acl && atomic_dec_and_test(&acl->a_refcount))
+ kfree(acl);
+}
+
+/*
+ * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
+ */
+int
+posix_acl_valid(const posix_acl_t *acl)
+{
+ const posix_acl_entry_t *pa, *pe;
+ int state = ACL_USER_OBJ;
+ unsigned int id = 0; /* keep gcc happy */
+ int needs_mask = 0;
+
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
+ return -EINVAL;
+ switch (pa->e_tag) {
+ case ACL_USER_OBJ:
+ if (state == ACL_USER_OBJ) {
+ id = 0;
+ state = ACL_USER;
+ break;
+ }
+ return -EINVAL;
+
+ case ACL_USER:
+ if (state != ACL_USER)
+ return -EINVAL;
+ if (pa->e_id == ACL_UNDEFINED_ID ||
+ pa->e_id < id)
+ return -EINVAL;
+ id = pa->e_id + 1;
+ needs_mask = 1;
+ break;
+
+ case ACL_GROUP_OBJ:
+ if (state == ACL_USER) {
+ id = 0;
+ state = ACL_GROUP;
+ break;
+ }
+ return -EINVAL;
+
+ case ACL_GROUP:
+ if (state != ACL_GROUP)
+ return -EINVAL;
+ if (pa->e_id == ACL_UNDEFINED_ID ||
+ pa->e_id < id)
+ return -EINVAL;
+ id = pa->e_id + 1;
+ needs_mask = 1;
+ break;
+
+ case ACL_MASK:
+ if (state != ACL_GROUP)
+ return -EINVAL;
+ state = ACL_OTHER;
+ break;
+
+ case ACL_OTHER:
+ if (state == ACL_OTHER ||
+ (state == ACL_GROUP && !needs_mask)) {
+ state = 0;
+ break;
+ }
+ return -EINVAL;
+
+ default:
+ return -EINVAL;
+ }
+ }
+ if (state == 0)
+ return 0;
+ return -EINVAL;
+}
+
+/*
+ * Returns 0 if the acl can be exactly represented in the traditional
+ * file mode permission bits, or else 1. Returns -E... on error.
+ */
+int
+posix_acl_equiv_mode(const posix_acl_t *acl, mode_t *mode_p)
+{
+ const posix_acl_entry_t *pa, *pe;
+ mode_t mode = 0;
+ int not_equiv = 0;
+
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ switch (pa->e_tag) {
+ case ACL_USER_OBJ:
+ mode |= (pa->e_perm & S_IRWXO) << 6;
+ break;
+ case ACL_GROUP_OBJ:
+ mode |= (pa->e_perm & S_IRWXO) << 3;
+ break;
+ case ACL_OTHER:
+ mode |= pa->e_perm & S_IRWXO;
+ break;
+ case ACL_MASK:
+ mode = (mode & ~S_IRWXG) |
+ ((pa->e_perm & S_IRWXO) << 3);
+ not_equiv = 1;
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ not_equiv = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (mode_p)
+ *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
+ return not_equiv;
+}
+
+/*
+ * Create an ACL representing the file mode permission bits of an inode.
+ */
+posix_acl_t *
+posix_acl_from_mode(mode_t mode)
+{
+ posix_acl_t *acl = posix_acl_alloc(3);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+
+ acl->a_entries[0].e_tag = ACL_USER_OBJ;
+ acl->a_entries[0].e_id = ACL_UNDEFINED_ID;
+ acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
+
+ acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
+ acl->a_entries[1].e_id = ACL_UNDEFINED_ID;
+ acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
+
+ acl->a_entries[2].e_tag = ACL_OTHER;
+ acl->a_entries[2].e_id = ACL_UNDEFINED_ID;
+ acl->a_entries[2].e_perm = (mode & S_IRWXO);
+ return acl;
+}
+
+/*
+ * Return 0 if current is granted want access to the inode
+ * by the acl. Returns -E... otherwise.
+ */
+int
+posix_acl_permission(struct inode *inode, const posix_acl_t *acl, int want)
+{
+ const posix_acl_entry_t *pa, *pe, *mask_obj;
+ int found = 0;
+
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ switch(pa->e_tag) {
+ case ACL_USER_OBJ:
+ /* (May have been checked already) */
+ if (inode->i_uid == current->fsuid)
+ goto check_perm;
+ break;
+ case ACL_USER:
+ if (pa->e_id == current->fsuid)
+ goto mask;
+ break;
+ case ACL_GROUP_OBJ:
+ if (in_group_p(inode->i_gid)) {
+ found = 1;
+ if ((pa->e_perm & want) == want)
+ goto mask;
+ }
+ break;
+ case ACL_GROUP:
+ if (in_group_p(pa->e_id)) {
+ found = 1;
+ if ((pa->e_perm & want) == want)
+ goto mask;
+ }
+ break;
+ case ACL_MASK:
+ break;
+ case ACL_OTHER:
+ if (found)
+ return -EACCES;
+ else
+ goto check_perm;
+ default:
+ return -EIO;
+ }
+ }
+ return -EIO;
+
+mask:
+ for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
+ if (mask_obj->e_tag == ACL_MASK) {
+ if ((pa->e_perm & mask_obj->e_perm & want) == want)
+ return 0;
+ return -EACCES;
+ }
+ }
+
+check_perm:
+ if ((pa->e_perm & want) == want)
+ return 0;
+ return -EACCES;
+}
+
+/*
+ * Modify acl when creating a new inode. The caller must ensure the acl is
+ * only referenced once.
+ *
+ * mode_p initially must contain the mode parameter to the open() / creat()
+ * system calls. All permissions that are not granted by the acl are removed.
+ * The permissions in the acl are changed to reflect the mode_p parameter.
+ */
+int
+posix_acl_create_masq(posix_acl_t *acl, mode_t *mode_p)
+{
+ posix_acl_entry_t *pa, *pe;
+ posix_acl_entry_t *group_obj = NULL, *mask_obj = NULL;
+ mode_t mode = *mode_p;
+ int not_equiv = 0;
+
+ /* assert(atomic_read(acl->a_refcount) == 1); */
+
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ switch(pa->e_tag) {
+ case ACL_USER_OBJ:
+ pa->e_perm &= (mode >> 6) | ~S_IRWXO;
+ mode &= (pa->e_perm << 6) | ~S_IRWXU;
+ break;
+
+ case ACL_USER:
+ case ACL_GROUP:
+ not_equiv = 1;
+ break;
+
+ case ACL_GROUP_OBJ:
+ group_obj = pa;
+ break;
+
+ case ACL_OTHER:
+ pa->e_perm &= mode | ~S_IRWXO;
+ mode &= pa->e_perm | ~S_IRWXO;
+ break;
+
+ case ACL_MASK:
+ mask_obj = pa;
+ not_equiv = 1;
+ break;
+
+ default:
+ return -EIO;
+ }
+ }
+
+ if (mask_obj) {
+ mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
+ mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
+ } else {
+ if (!group_obj)
+ return -EIO;
+ group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
+ mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
+ }
+
+ *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
+ return not_equiv;
+}
+
+/*
+ * Modify the ACL for the chmod syscall.
+ */
+int
+posix_acl_chmod_masq(posix_acl_t *acl, mode_t mode)
+{
+ posix_acl_entry_t *group_obj = NULL, *mask_obj = NULL;
+ posix_acl_entry_t *pa, *pe;
+
+ /* assert(atomic_read(acl->a_refcount) == 1); */
+
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ switch(pa->e_tag) {
+ case ACL_USER_OBJ:
+ pa->e_perm = (mode & S_IRWXU) >> 6;
+ break;
+
+ case ACL_USER:
+ case ACL_GROUP:
+ break;
+
+ case ACL_GROUP_OBJ:
+ group_obj = pa;
+ break;
+
+ case ACL_MASK:
+ mask_obj = pa;
+ break;
+
+ case ACL_OTHER:
+ pa->e_perm = (mode & S_IRWXO);
+ break;
+
+ default:
+ return -EIO;
+ }
+ }
+
+ if (mask_obj) {
+ mask_obj->e_perm = (mode & S_IRWXG) >> 3;
+ } else {
+ if (!group_obj)
+ return -EIO;
+ group_obj->e_perm = (mode & S_IRWXG) >> 3;
+ }
+
+ return 0;
+}
+
+/*
+ * Adjust the mode parameter so that NFSv2 grants nobody permissions
+ * that may not be granted by the ACL. This is necessary because NFSv2
+ * may compute access permissions on the client side, and may serve cached
+ * data whenever it assumes access would be granted. Since ACLs may also
+ * be used to deny access to specific users, the minimal permissions
+ * for secure operation over NFSv2 are very restrictive. Permissions
+ * granted to users via Access Control Lists will not be effective over
+ * NFSv2.
+ *
+ * Privilege escalation can only happen for read operations, as writes are
+ * always carried out on the NFS server, where the proper access checks are
+ * implemented.
+ */
+int
+posix_acl_masq_nfs_mode(posix_acl_t *acl, mode_t *mode_p)
+{
+ posix_acl_entry_t *pa, *pe; int min_perm = S_IRWXO;
+
+ FOREACH_ACL_ENTRY(pa, acl, pe) {
+ switch(pa->e_tag) {
+ case ACL_USER_OBJ:
+ break;
+
+ case ACL_USER:
+ case ACL_GROUP_OBJ:
+ case ACL_GROUP:
+ case ACL_MASK:
+ case ACL_OTHER:
+ min_perm &= pa->e_perm;
+ break;
+
+ default:
+ return -EIO;
+ }
+ }
+ *mode_p = (*mode_p & ~(S_IRWXG|S_IRWXO)) | (min_perm << 3) | min_perm;
+
+ return 0;
+}
+
+/*
+ * Get the POSIX ACL of an inode.
+ */
+posix_acl_t *
+get_posix_acl(struct inode *inode, int type)
+{
+ posix_acl_t *acl;
+
+ if (!inode->i_op || !inode->i_op->get_posix_acl)
+ return ERR_PTR(-ENOTSUP);
+
+ down(&inode->i_sem);
+ lock_kernel(); /* goes away in 2.5.x */
+ acl = inode->i_op->get_posix_acl(inode, type);
+ unlock_kernel(); /* goes away in 2.5.x */
+ up(&inode->i_sem);
+
+ return acl;
+}
+
+/*
+ * Set the POSIX ACL of an inode.
+ */
+int
+set_posix_acl(struct inode *inode, int type, posix_acl_t *acl)
+{
+ int error;
+
+ if (!inode->i_op || !inode->i_op->set_posix_acl)
+ return -ENOTSUP;
+
+ down(&inode->i_sem);
+ lock_kernel(); /* goes away in 2.5.x */
+ error = inode->i_op->set_posix_acl(inode, type, acl);
+ unlock_kernel(); /* goes away in 2.5.x */
+ up(&inode->i_sem);
+
+ return error;
+}
diff -Nur linux-2.2.20ea/fs/xattr.c linux-2.2.20acl/fs/xattr.c
--- linux-2.2.20ea/fs/xattr.c Mon Mar 18 02:34:34 2002
+++ linux-2.2.20acl/fs/xattr.c Sat Mar 9 17:29:12 2002
@@ -84,9 +84,9 @@
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->setxattr) {
- down(&d->d_inode->i_sem);
+ lock_kernel();
error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags);
- up(&d->d_inode->i_sem);
+ unlock_kernel();
}
xattr_free(kvalue, size);
@@ -169,9 +169,9 @@
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->getxattr) {
- down(&d->d_inode->i_sem);
+ lock_kernel();
error = d->d_inode->i_op->getxattr(d, kname, kvalue, size);
- up(&d->d_inode->i_sem);
+ unlock_kernel();
}
if (kvalue && error > 0)
@@ -250,9 +250,9 @@
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->listxattr) {
- down(&d->d_inode->i_sem);
+ lock_kernel();
error = d->d_inode->i_op->listxattr(d, klist, size);
- up(&d->d_inode->i_sem);
+ unlock_kernel();
}
if (klist && error > 0)
@@ -333,9 +333,9 @@
error = -EOPNOTSUPP;
if (d->d_inode->i_op && d->d_inode->i_op->removexattr) {
- down(&d->d_inode->i_sem);
+ lock_kernel();
error = d->d_inode->i_op->removexattr(d, kname);
- up(&d->d_inode->i_sem);
+ unlock_kernel();
}
return error;
}
diff -Nur linux-2.2.20ea/include/asm-sparc64/unistd.h linux-2.2.20acl/include/asm-sparc64/unistd.h
--- linux-2.2.20ea/include/asm-sparc64/unistd.h Wed Apr 24 11:24:22 2002
+++ linux-2.2.20acl/include/asm-sparc64/unistd.h Sun Mar 25 18:37:40 2001
@@ -184,24 +184,24 @@
/* #define __NR_exportfs 166 SunOS Specific */
#define __NR_mount 167 /* Common */
#define __NR_ustat 168 /* Common */
- #define __NR_setxattr 169 /* SunOS Specific */
-#define __NR_lsetxattr 170 /* SunOS Specific */
-#define __NR_fsetxattr 171 /* SunOS Specific */
-#define __NR_getxattr 172 /* SunOS Specific */
-#define __NR_lgetxattr 173 /* SunOS Specific */
+/* #define __NR_semsys 169 SunOS Specific */
+/* #define __NR_msgsys 170 SunOS Specific */
+/* #define __NR_shmsys 171 SunOS Specific */
+/* #define __NR_auditsys 172 SunOS Specific */
+/* #define __NR_rfssys 173 SunOS Specific */
#define __NR_getdents 174 /* Common */
#define __NR_setsid 175 /* Common */
#define __NR_fchdir 176 /* Common */
-#define __NR_fgetxattr 177 /* SunOS Specific */
-#define __NR_listxattr 178 /* SunOS Specific */
-#define __NR_llistxattr 179 /* SunOS Specific */
-#define __NR_flistxattr 180 /* SunOS Specific */
-#define __NR_removexattr 181 /* SunOS Specific */
-#define __NR_lremovexattr 182 /* SunOS Specific */
+/* #define __NR_fchroot 177 SunOS Specific */
+/* #define __NR_vpixsys 178 SunOS Specific */
+/* #define __NR_aioread 179 SunOS Specific */
+/* #define __NR_aiowrite 180 SunOS Specific */
+/* #define __NR_aiowait 181 SunOS Specific */
+/* #define __NR_aiocancel 182 SunOS Specific */
#define __NR_sigpending 183 /* Common */
#define __NR_query_module 184 /* Linux Specific */
#define __NR_setpgid 185 /* Common */
-#define __NR_fremovexattr 186 /* SunOS Specific */
+/* #define __NR_pathconf 186 SunOS Specific */
/* #define __NR_fpathconf 187 SunOS Specific */
/* #define __NR_sysconf 188 SunOS Specific */
#define __NR_uname 189 /* Linux Specific */
diff -Nur linux-2.2.20ea/include/linux/ext2_acl.h linux-2.2.20acl/include/linux/ext2_acl.h
--- linux-2.2.20ea/include/linux/ext2_acl.h Thu Jan 1 01:00:00 1970
+++ linux-2.2.20acl/include/linux/ext2_acl.h Wed Apr 24 10:30:40 2002
@@ -0,0 +1,105 @@
+/*
+ File: linux/ext2_acl.h
+
+ (C) 2001 Andreas Gruenbacher,
+*/
+
+#include
+#include
+#include
+
+#define EXT2_ACL_VERSION 0x0001
+#define EXT2_ACL_MAX_ENTRIES 32
+
+typedef struct {
+ __u16 e_tag;
+ __u16 e_perm;
+ __u32 e_id;
+} ext2_acl_entry;
+
+typedef struct {
+ __u16 e_tag;
+ __u16 e_perm;
+} ext2_acl_entry_short;
+
+typedef struct {
+ __u32 a_version;
+} ext2_acl_header;
+
+static inline size_t ext2_acl_size(int count)
+{
+ if (count <= 4) {
+ return sizeof(ext2_acl_header) +
+ count * sizeof(ext2_acl_entry_short);
+ } else {
+ return sizeof(ext2_acl_header) +
+ 4 * sizeof(ext2_acl_entry_short) +
+ (count - 4) * sizeof(ext2_acl_entry);
+ }
+}
+
+static inline int ext2_acl_count(size_t size)
+{
+ ssize_t s;
+ size -= sizeof(ext2_acl_header);
+ s = size - 4 * sizeof(ext2_acl_entry_short);
+ if (s < 0) {
+ if (size % sizeof(ext2_acl_entry_short))
+ return -1;
+ return size / sizeof(ext2_acl_entry_short);
+ } else {
+ if (s % sizeof(ext2_acl_entry))
+ return -1;
+ return s / sizeof(ext2_acl_entry) + 4;
+ }
+}
+
+#ifdef __KERNEL__
+# ifdef CONFIG_EXT2_FS_POSIX_ACL
+
+/* Value for inode->u.ext2_i.i_acl and inode->u.ext2_i.i_default_acl
+ if the ACL has not been cached */
+# define EXT2_ACL_NOT_CACHED ((void *)-1)
+
+/* acl.c */
+extern int ext2_permission (struct inode *, int);
+extern posix_acl_t *ext2_get_acl (struct inode *, int);
+extern int ext2_set_acl (struct inode *, int, posix_acl_t *);
+extern int ext2_acl_chmod (struct inode *);
+extern int ext2_init_acl (struct inode *, struct inode *);
+
+extern int init_ext2_acl(void) __init;
+extern void exit_ext2_acl(void);
+
+# else
+# include
+# define ext2_permission NULL
+# define ext2_get_acl NULL
+# define ext2_set_acl NULL
+
+static inline int
+ext2_acl_chmod (struct inode *inode)
+{
+ return 0;
+}
+
+static inline int ext2_init_acl (struct inode *inode, struct inode *dir)
+{
+ inode->i_mode &= ~current->fs->umask;
+ return 0;
+}
+
+static inline int
+init_ext2_acl(void)
+{
+ return 0;
+}
+
+static inline void
+exit_ext2_acl(void)
+{
+}
+
+# endif
+#endif
+
diff -Nur linux-2.2.20ea/include/linux/ext2_fs.h linux-2.2.20acl/include/linux/ext2_fs.h
--- linux-2.2.20ea/include/linux/ext2_fs.h Wed Mar 20 02:09:09 2002
+++ linux-2.2.20acl/include/linux/ext2_fs.h Wed Mar 20 02:09:30 2002
@@ -509,9 +509,6 @@
# define ATTRIB_NORET __attribute__((noreturn))
# define NORET_AND noreturn,
-/* acl.c */
-extern int ext2_permission (struct inode *, int);
-
/* balloc.c */
extern int ext2_group_sparse(int group);
extern int ext2_new_block (const struct inode *, unsigned long,
@@ -541,7 +538,7 @@
extern int ext2_sync_file (struct file *, struct dentry *);
/* ialloc.c */
-extern struct inode * ext2_new_inode (const struct inode *, int, int *);
+extern struct inode * ext2_new_inode (struct inode *, int, int *);
extern void ext2_free_inode (struct inode *);
extern unsigned long ext2_count_free_inodes (struct super_block *);
extern void ext2_check_inodes_bitmap (struct super_block *);
diff -Nur linux-2.2.20ea/include/linux/ext2_fs_i.h linux-2.2.20acl/include/linux/ext2_fs_i.h
--- linux-2.2.20ea/include/linux/ext2_fs_i.h Sun Mar 25 18:31:03 2001
+++ linux-2.2.20acl/include/linux/ext2_fs_i.h Sat Mar 9 19:20:57 2002
@@ -36,6 +36,10 @@
__u32 i_prealloc_block;
__u32 i_prealloc_count;
__u32 i_high_size;
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+ posix_acl_t *i_acl;
+ posix_acl_t *i_default_acl;
+#endif
int i_new_inode:1; /* Is a freshly allocated inode */
};
diff -Nur linux-2.2.20ea/include/linux/ext2_xattr.h linux-2.2.20acl/include/linux/ext2_xattr.h
--- linux-2.2.20ea/include/linux/ext2_xattr.h Fri Apr 5 10:07:28 2002
+++ linux-2.2.20acl/include/linux/ext2_xattr.h Fri Apr 5 10:06:10 2002
@@ -19,6 +19,8 @@
/* Name indexes */
#define EXT2_XATTR_INDEX_MAX 10
#define EXT2_XATTR_INDEX_USER 1
+#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS 2
+#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT 3
struct ext2_xattr_header {
__u32 h_magic; /* magic number for identification */
diff -Nur linux-2.2.20ea/include/linux/ext3_acl.h linux-2.2.20acl/include/linux/ext3_acl.h
--- linux-2.2.20ea/include/linux/ext3_acl.h Thu Jan 1 01:00:00 1970
+++ linux-2.2.20acl/include/linux/ext3_acl.h Fri Apr 5 10:06:10 2002
@@ -0,0 +1,107 @@
+/*
+ File: linux/ext3_acl.h
+
+ (C) 2001 Andreas Gruenbacher,
+*/
+
+#include
+#include
+
+#define EXT3_ACL_VERSION 0x0001
+#define EXT3_ACL_MAX_ENTRIES 32
+
+typedef struct {
+ __u16 e_tag;
+ __u16 e_perm;
+ __u32 e_id;
+} ext3_acl_entry;
+
+typedef struct {
+ __u16 e_tag;
+ __u16 e_perm;
+} ext3_acl_entry_short;
+
+typedef struct {
+ __u32 a_version;
+} ext3_acl_header;
+
+static inline size_t ext3_acl_size(int count)
+{
+ if (count <= 4) {
+ return sizeof(ext3_acl_header) +
+ count * sizeof(ext3_acl_entry_short);
+ } else {
+ return sizeof(ext3_acl_header) +
+ 4 * sizeof(ext3_acl_entry_short) +
+ (count - 4) * sizeof(ext3_acl_entry);
+ }
+}
+
+static inline int ext3_acl_count(size_t size)
+{
+ ssize_t s;
+ size -= sizeof(ext3_acl_header);
+ s = size - 4 * sizeof(ext3_acl_entry_short);
+ if (s < 0) {
+ if (size % sizeof(ext3_acl_entry_short))
+ return -1;
+ return size / sizeof(ext3_acl_entry_short);
+ } else {
+ if (s % sizeof(ext3_acl_entry))
+ return -1;
+ return s / sizeof(ext3_acl_entry) + 4;
+ }
+}
+
+#ifdef __KERNEL__
+# ifdef CONFIG_EXT3_FS_POSIX_ACL
+
+/* Value for inode->u.ext3_i.i_acl and inode->u.ext3_i.i_default_acl
+ if the ACL has not been cached */
+# define EXT3_ACL_NOT_CACHED ((void *)-1)
+
+/* acl.c */
+extern int ext3_permission (struct inode *, int);
+extern posix_acl_t *ext3_get_acl (struct inode *, int);
+extern int ext3_set_acl (struct inode *, int, posix_acl_t *);
+extern int ext3_acl_chmod (handle_t *, struct inode *);
+extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
+extern int ext3_get_acl_xattr (struct inode *, int, void *, size_t);
+extern int ext3_set_acl_xattr (struct inode *, int, void *, size_t);
+
+extern int init_ext3_acl(void) __init;
+extern void exit_ext3_acl(void);
+
+# else /* CONFIG_EXT3_FS_POSIX_ACL */
+# include
+# define ext3_permission NULL
+# define ext3_get_acl NULL
+# define ext3_set_acl NULL
+
+static inline int
+ext3_acl_chmod(handle_t *handle, struct inode *inode)
+{
+ return 0;
+}
+
+static inline int
+ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+ inode->i_mode &= ~current->fs->umask;
+ return 0;
+}
+
+static inline int
+init_ext3_acl(void)
+{
+ return 0;
+}
+
+static inline void
+exit_ext3_acl(void)
+{
+}
+
+# endif /* CONFIG_EXT3_FS_POSIX_ACL */
+#endif /* __KERNEL__ */
+
diff -Nur linux-2.2.20ea/include/linux/ext3_xattr.h linux-2.2.20acl/include/linux/ext3_xattr.h
--- linux-2.2.20ea/include/linux/ext3_xattr.h Fri Apr 5 10:07:33 2002
+++ linux-2.2.20acl/include/linux/ext3_xattr.h Fri Apr 5 10:06:10 2002
@@ -19,6 +19,8 @@
/* Name indexes */
#define EXT3_XATTR_INDEX_MAX 10
#define EXT3_XATTR_INDEX_USER 1
+#define EXT3_XATTR_INDEX_POSIX_ACL_ACCESS 2
+#define EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT 3
struct ext3_xattr_header {
__u32 h_magic; /* magic number for identification */
diff -Nur linux-2.2.20ea/include/linux/fs.h linux-2.2.20acl/include/linux/fs.h
--- linux-2.2.20ea/include/linux/fs.h Sun Mar 24 23:49:51 2002
+++ linux-2.2.20acl/include/linux/fs.h Wed Apr 24 10:29:29 2002
@@ -24,6 +24,8 @@
#include
#include /* just in case the #define NULL previously in here was needed */
+#include
+
struct poll_table_struct;
@@ -108,12 +110,14 @@
* These are the super block s_xattr_flags
*/
#define XATTR_MNT_FLAG_USER 1 /* Extended user attributes */
+#define XATTR_MNT_FLAG_POSIX_ACL 2 /* Access Control Lists */
#define __IS_XATTR_FLG(inode,flg) \
((inode)->i_sb && \
(inode)->i_sb->s_xattr_flags & (XATTR_MNT_FLAG_ ## flg))
#define IS_XATTR_USER(inode) __IS_XATTR_FLG(inode, USER)
+#define IS_POSIX_ACL(inode) __IS_XATTR_FLG(inode, POSIX_ACL)
/*
* Flags that can be altered by MS_REMOUNT
*/
@@ -660,6 +664,8 @@
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *);
+ posix_acl_t *(*get_posix_acl) (struct inode *, int);
+ int (*set_posix_acl) (struct inode *, int, posix_acl_t *);
};
struct super_operations {
diff -Nur linux-2.2.20ea/include/linux/nfsd/nfsd.h linux-2.2.20acl/include/linux/nfsd/nfsd.h
--- linux-2.2.20ea/include/linux/nfsd/nfsd.h Sun Mar 24 23:52:34 2002
+++ linux-2.2.20acl/include/linux/nfsd/nfsd.h Fri May 17 15:34:24 2002
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
@@ -129,6 +130,15 @@
int nfsd_notify_change(struct inode *, struct iattr *);
int nfsd_permission(struct svc_export *, struct dentry *, int);
+#ifdef CONFIG_FS_POSIX_ACL
+posix_acl_t * nfsd_get_posix_acl(struct svc_fh *, int);
+#else
+static inline posix_acl_t *
+nfsd_get_posix_acl(struct svc_fh *fhp, int acl_type) {
+ return 0;
+}
+#endif
+
/* nfsd/nfsctl.c */
void nfsd_modcount(struct inode *, int);
@@ -185,6 +195,12 @@
* Time of server startup
*/
extern struct timeval nfssvc_boot;
+
+/* nfs_permission_mode module/kernel parameter */
+#define NFS_FILE_MODE_PERMISSION_BITS_UNCHANGED 0
+#define NFS_FILE_MODE_PERMISSION_BITS_SECURE 1
+
+extern int nfs_permission_mode;
/*
* The number of nfsd threads.
diff -Nur linux-2.2.20ea/include/linux/posix_acl.h linux-2.2.20acl/include/linux/posix_acl.h
--- linux-2.2.20ea/include/linux/posix_acl.h Thu Jan 1 01:00:00 1970
+++ linux-2.2.20acl/include/linux/posix_acl.h Wed Apr 24 10:10:59 2002
@@ -0,0 +1,69 @@
+/*
+ File: linux/posix_acl.h
+
+ (C) 2000 Andreas Gruenbacher,
+*/
+
+
+#ifndef __LINUX_POSIX_ACL_H
+#define __LINUX_POSIX_ACL_H
+
+
+#define ACL_UNDEFINED_ID (-1)
+
+/* a_type field in acl_user_posix_entry_t */
+#define ACL_TYPE_ACCESS (0x8000)
+#define ACL_TYPE_DEFAULT (0x4000)
+
+/* e_tag entry in posix_acl_entry_t */
+#define ACL_USER_OBJ (0x01)
+#define ACL_USER (0x02)
+#define ACL_GROUP_OBJ (0x04)
+#define ACL_GROUP (0x08)
+#define ACL_MASK (0x10)
+#define ACL_OTHER (0x20)
+
+/* permissions in the e_perm field */
+#define ACL_READ (0x04)
+#define ACL_WRITE (0x02)
+#define ACL_EXECUTE (0x01)
+//#define ACL_ADD (0x08)
+//#define ACL_DELETE (0x10)
+
+#ifdef __KERNEL__
+
+typedef struct {
+ short e_tag;
+ unsigned short e_perm;
+ unsigned int e_id;
+} posix_acl_entry_t;
+
+typedef struct {
+ atomic_t a_refcount;
+ unsigned int a_count;
+ posix_acl_entry_t a_entries[0];
+} posix_acl_t;
+
+#define FOREACH_ACL_ENTRY(pa, acl, pe) \
+ for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; pa
+*/
+
+#ifndef _LINUX_XATTR_ACL_H
+#define _LINUX_XATTR_ACL_H
+
+#include
+
+#define XATTR_NAME_ACL_ACCESS "system.posix_acl_access"
+#define XATTR_NAME_ACL_DEFAULT "system.posix_acl_default"
+
+#define XATTR_ACL_VERSION 0x0002
+
+typedef struct {
+ __u16 e_tag;
+ __u16 e_perm;
+ __u32 e_id;
+} xattr_acl_entry;
+
+typedef struct {
+ __u32 a_version;
+ xattr_acl_entry a_entries[0];
+} xattr_acl_header;
+
+static inline size_t xattr_acl_size(int count)
+{
+ return sizeof(xattr_acl_header) + count * sizeof(xattr_acl_entry);
+}
+
+static inline int xattr_acl_count(size_t size)
+{
+ if (size < sizeof(xattr_acl_header))
+ return -1;
+ size -= sizeof(xattr_acl_header);
+ if (size % sizeof(xattr_acl_entry))
+ return -1;
+ return size / sizeof(xattr_acl_entry);
+}
+
+#endif /* _LINUX_XATTR_ACL_H */
diff -Nur linux-2.2.20ea/unistd.h linux-2.2.20acl/unistd.h
--- linux-2.2.20ea/unistd.h Thu Jan 1 01:00:00 1970
+++ linux-2.2.20acl/unistd.h Wed Apr 24 11:29:31 2002
@@ -0,0 +1,450 @@
+/* $Id: unistd.h,v 1.28.2.2 2001/01/11 19:12:11 davem Exp $ */
+#ifndef _SPARC64_UNISTD_H
+#define _SPARC64_UNISTD_H
+
+/*
+ * System calls under the Sparc.
+ *
+ * Don't be scared by the ugly clobbers, it is the only way I can
+ * think of right now to force the arguments into fixed registers
+ * before the trap into the system call with gcc 'asm' statements.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * SunOS compatibility based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ */
+
+#define __NR_exit 1 /* Common */
+#define __NR_fork 2 /* Common */
+#define __NR_read 3 /* Common */
+#define __NR_write 4 /* Common */
+#define __NR_open 5 /* Common */
+#define __NR_close 6 /* Common */
+#define __NR_wait4 7 /* Common */
+#define __NR_creat 8 /* Common */
+#define __NR_link 9 /* Common */
+#define __NR_unlink 10 /* Common */
+#define __NR_execv 11 /* SunOS Specific */
+#define __NR_chdir 12 /* Common */
+#define __NR_chown 13 /* Common */
+#define __NR_mknod 14 /* Common */
+#define __NR_chmod 15 /* Common */
+#define __NR_lchown 16 /* Common */
+#define __NR_brk 17 /* Common */
+#define __NR_perfctr 18 /* Performance counter operations */
+#define __NR_lseek 19 /* Common */
+#define __NR_getpid 20 /* Common */
+#define __NR_capget 21 /* Linux Specific */
+#define __NR_capset 22 /* Linux Specific */
+#define __NR_setuid 23 /* Implemented via setreuid in SunOS */
+#define __NR_getuid 24 /* Common */
+/* #define __NR_time alias 25 ENOSYS under SunOS */
+#define __NR_ptrace 26 /* Common */
+#define __NR_alarm 27 /* Implemented via setitimer in SunOS */
+#define __NR_sigaltstack 28 /* Common */
+#define __NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
+#define __NR_utime 30 /* Implemented via utimes() under SunOS */
+/* #define __NR_stty 31 Implemented via ioctl() under SunOS */
+/* #define __NR_gtty 32 Implemented via ioctl() under SunOS */
+#define __NR_access 33 /* Common */
+#define __NR_nice 34 /* Implemented via get/setpriority() in SunOS */
+/* #define __NR_ftime 35 Implemented via gettimeofday() in SunOS */
+#define __NR_sync 36 /* Common */
+#define __NR_kill 37 /* Common */
+#define __NR_stat 38 /* Common */
+#define __NR_sendfile 39 /* Linux Specific */
+#define __NR_lstat 40 /* Common */
+#define __NR_dup 41 /* Common */
+#define __NR_pipe 42 /* Common */
+#define __NR_times 43 /* Implemented via getrusage() in SunOS */
+/* #define __NR_getuid32 44 Linux Sparc32 Specific */
+#define __NR_umount2 45 /* Linux Specific */
+#define __NR_setgid 46 /* Implemented via setregid() in SunOS */
+#define __NR_getgid 47 /* Common */
+#define __NR_signal 48 /* Implemented via sigvec() in SunOS */
+#define __NR_geteuid 49 /* SunOS calls getuid() */
+#define __NR_getegid 50 /* SunOS calls getgid() */
+#define __NR_acct 51 /* Common */
+#define __NR_memory_ordering 52 /* Linux Specific */
+/* #define __NR_mctl 53 SunOS specific */
+#define __NR_ioctl 54 /* Common */
+#define __NR_reboot 55 /* Common */
+/* #define __NR_ni_syscall 56 ENOSYS under SunOS */
+#define __NR_symlink 57 /* Common */
+#define __NR_readlink 58 /* Common */
+#define __NR_execve 59 /* Common */
+#define __NR_umask 60 /* Common */
+#define __NR_chroot 61 /* Common */
+#define __NR_fstat 62 /* Common */
+/* #define __NR_ni_syscall 63 ENOSYS under SunOS */
+#define __NR_getpagesize 64 /* Common */
+#define __NR_msync 65 /* Common in newer 1.3.x revs... */
+#define __NR_vfork 66 /* Common */
+#define __NR_pread 67 /* Linux Specific */
+#define __NR_pwrite 68 /* Linux Specific */
+/* #define __NR_sbrk 69 SunOS Specific */
+/* #define __NR_sstk 70 SunOS Specific */
+#define __NR_mmap 71 /* Common */
+/* #define __NR_vadvise 72 SunOS Specific */
+#define __NR_munmap 73 /* Common */
+#define __NR_mprotect 74 /* Common */
+/* #define __NR_madvise 75 SunOS Specific */
+#define __NR_vhangup 76 /* Common */
+/* #define __NR_ni_syscall 77 ENOSYS under SunOS */
+/* #define __NR_mincore 78 SunOS Specific */
+#define __NR_getgroups 79 /* Common */
+#define __NR_setgroups 80 /* Common */
+#define __NR_getpgrp 81 /* Common */
+/* #define __NR_setpgrp 82 setpgid, same difference... */
+#define __NR_setitimer 83 /* Common */
+/* #define __NR_ni_syscall 84 ENOSYS under SunOS */
+#define __NR_swapon 85 /* Common */
+#define __NR_getitimer 86 /* Common */
+/* #define __NR_gethostname 87 SunOS Specific */
+#define __NR_sethostname 88 /* Common */
+/* #define __NR_getdtablesize 89 SunOS Specific */
+#define __NR_dup2 90 /* Common */
+/* #define __NR_getdopt 91 SunOS Specific */
+#define __NR_fcntl 92 /* Common */
+#define __NR_select 93 /* Common */
+/* #define __NR_setdopt 94 SunOS Specific */
+#define __NR_fsync 95 /* Common */
+#define __NR_setpriority 96 /* Common */
+#define __NR_socket 97 /* Common */
+#define __NR_connect 98 /* Common */
+#define __NR_accept 99 /* Common */
+#define __NR_getpriority 100 /* Common */
+#define __NR_rt_sigreturn 101 /* Linux Specific */
+#define __NR_rt_sigaction 102 /* Linux Specific */
+#define __NR_rt_sigprocmask 103 /* Linux Specific */
+#define __NR_rt_sigpending 104 /* Linux Specific */
+#define __NR_rt_sigtimedwait 105 /* Linux Specific */
+#define __NR_rt_sigqueueinfo 106 /* Linux Specific */
+#define __NR_rt_sigsuspend 107 /* Linux Specific */
+/* #define __NR_sigvec 108 SunOS Specific */
+/* #define __NR_sigblock 109 SunOS Specific */
+/* #define __NR_sigsetmask 110 SunOS Specific */
+/* #define __NR_sigpause 111 SunOS Specific */
+/* #define __NR_sigstack 112 SunOS Specific */
+#define __NR_recvmsg 113 /* Common */
+#define __NR_sendmsg 114 /* Common */
+/* #define __NR_vtrace 115 SunOS Specific */
+#define __NR_gettimeofday 116 /* Common */
+#define __NR_getrusage 117 /* Common */
+#define __NR_getsockopt 118 /* Common */
+#define __NR_getcwd 119 /* Linux Specific */
+#define __NR_readv 120 /* Common */
+#define __NR_writev 121 /* Common */
+#define __NR_settimeofday 122 /* Common */
+#define __NR_fchown 123 /* Common */
+#define __NR_fchmod 124 /* Common */
+#define __NR_recvfrom 125 /* Common */
+#define __NR_setreuid 126 /* Common */
+#define __NR_setregid 127 /* Common */
+#define __NR_rename 128 /* Common */
+#define __NR_truncate 129 /* Common */
+#define __NR_ftruncate 130 /* Common */
+#define __NR_flock 131 /* Common */
+/* #define __NR_ni_syscall 132 ENOSYS under SunOS */
+#define __NR_sendto 133 /* Common */
+#define __NR_shutdown 134 /* Common */
+#define __NR_socketpair 135 /* Common */
+#define __NR_mkdir 136 /* Common */
+#define __NR_rmdir 137 /* Common */
+#define __NR_utimes 138 /* SunOS Specific */
+/* #define __NR_ni_syscall 139 ENOSYS under SunOS */
+/* #define __NR_adjtime 140 SunOS Specific */
+#define __NR_getpeername 141 /* Common */
+/* #define __NR_gethostid 142 SunOS Specific */
+/* #define __NR_ni_syscall 143 ENOSYS under SunOS */
+#define __NR_getrlimit 144 /* Common */
+#define __NR_setrlimit 145 /* Common */
+/* #define __NR_killpg 146 SunOS Specific */
+#define __NR_prctl 147 /* ENOSYS under SunOS */
+#define __NR_pciconfig_read 148 /* ENOSYS under SunOS */
+#define __NR_pciconfig_write 149 /* ENOSYS under SunOS */
+#define __NR_getsockname 150 /* Common */
+/* #define __NR_getmsg 151 SunOS Specific */
+/* #define __NR_putmsg 152 SunOS Specific */
+#define __NR_poll 153 /* Common */
+/* #define __NR_getdents64 154 Linux Sparc32 Specific */
+/* #define __NR_fcntl64 155 Linux Sparc32 Specific */
+/* #define __NR_getdirentries 156 SunOS Specific */
+#define __NR_statfs 157 /* Common */
+#define __NR_fstatfs 158 /* Common */
+#define __NR_umount 159 /* Common */
+/* #define __NR_async_daemon 160 SunOS Specific */
+/* #define __NR_getfh 161 SunOS Specific */
+#define __NR_getdomainname 162 /* SunOS Specific */
+#define __NR_setdomainname 163 /* Common */
+#define __NR_utrap_install 164 /* SYSV ABI/v9 required */
+#define __NR_quotactl 165 /* Common */
+/* #define __NR_exportfs 166 SunOS Specific */
+#define __NR_mount 167 /* Common */
+#define __NR_ustat 168 /* Common */
+ #define __NR_setxattr 169 /* SunOS Specific */
+#define __NR_lsetxattr 170 /* SunOS Specific */
+#define __NR_fsetxattr 171 /* SunOS Specific */
+#define __NR_getxattr 172 /* SunOS Specific */
+#define __NR_lgetxattr 173 /* SunOS Specific */
+#define __NR_getdents 174 /* Common */
+#define __NR_setsid 175 /* Common */
+#define __NR_fchdir 176 /* Common */
+#define __NR_fgetxattr 177 /* SunOS Specific */
+#define __NR_listxattr 178 /* SunOS Specific */
+#define __NR_llistxattr 179 /* SunOS Specific */
+#define __NR_flistxattr 180 /* SunOS Specific */
+#define __NR_removexattr 181 /* SunOS Specific */
+#define __NR_lremovexattr 182 /* SunOS Specific */
+#define __NR_sigpending 183 /* Common */
+#define __NR_query_module 184 /* Linux Specific */
+#define __NR_setpgid 185 /* Common */
+#define __NR_fremovexattr 186 /* SunOS Specific */
+/* #define __NR_fpathconf 187 SunOS Specific */
+/* #define __NR_sysconf 188 SunOS Specific */
+#define __NR_uname 189 /* Linux Specific */
+#define __NR_init_module 190 /* Linux Specific */
+#define __NR_personality 191 /* Linux Specific */
+/* #define __NR_prof 192 Linux Specific */
+/* #define __NR_break 193 Linux Specific */
+/* #define __NR_lock 194 Linux Specific */
+/* #define __NR_mpx 195 Linux Specific */
+/* #define __NR_ulimit 196 Linux Specific */
+#define __NR_getppid 197 /* Linux Specific */
+#define __NR_sigaction 198 /* Linux Specific */
+#define __NR_sgetmask 199 /* Linux Specific */
+#define __NR_ssetmask 200 /* Linux Specific */
+#define __NR_sigsuspend 201 /* Linux Specific */
+#define __NR_oldlstat 202 /* Linux Specific */
+#define __NR_uselib 203 /* Linux Specific */
+#define __NR_readdir 204 /* Linux Specific */
+/* #define __NR_ioperm 205 Linux Specific - i386 specific, unused */
+#define __NR_socketcall 206 /* Linux Specific */
+#define __NR_syslog 207 /* Linux Specific */
+/* #define __NR_olduname 208 Linux Specific */
+/* #define __NR_iopl 209 Linux Specific - i386 specific, unused */
+#define __NR_idle 210 /* Linux Specific */
+/* #define __NR_vm86 211 Linux Specific - i386 specific, unused */
+#define __NR_waitpid 212 /* Linux Specific */
+#define __NR_swapoff 213 /* Linux Specific */
+#define __NR_sysinfo 214 /* Linux Specific */
+#define __NR_ipc 215 /* Linux Specific */
+#define __NR_sigreturn 216 /* Linux Specific */
+#define __NR_clone 217 /* Linux Specific */
+/* #define __NR_modify_ldt 218 Linux Specific - i386 specific, unused */
+#define __NR_adjtimex 219 /* Linux Specific */
+#define __NR_sigprocmask 220 /* Linux Specific */
+#define __NR_create_module 221 /* Linux Specific */
+#define __NR_delete_module 222 /* Linux Specific */
+#define __NR_get_kernel_syms 223 /* Linux Specific */
+#define __NR_getpgid 224 /* Linux Specific */
+#define __NR_bdflush 225 /* Linux Specific */
+#define __NR_sysfs 226 /* Linux Specific */
+#define __NR_afs_syscall 227 /* Linux Specific */
+#define __NR_setfsuid 228 /* Linux Specific */
+#define __NR_setfsgid 229 /* Linux Specific */
+#define __NR__newselect 230 /* Linux Specific */
+#define __NR_time 231 /* Linux Specific */
+/* #define __NR_oldstat 232 Linux Specific */
+#define __NR_stime 233 /* Linux Specific */
+/* #define __NR_oldfstat 234 Linux Specific */
+/* #define __NR_phys 235 Linux Specific */
+#define __NR__llseek 236 /* Linux Specific */
+#define __NR_mlock 237
+#define __NR_munlock 238
+#define __NR_mlockall 239
+#define __NR_munlockall 240
+#define __NR_sched_setparam 241
+#define __NR_sched_getparam 242
+#define __NR_sched_setscheduler 243
+#define __NR_sched_getscheduler 244
+#define __NR_sched_yield 245
+#define __NR_sched_get_priority_max 246
+#define __NR_sched_get_priority_min 247
+#define __NR_sched_rr_get_interval 248
+#define __NR_nanosleep 249
+#define __NR_mremap 250
+#define __NR__sysctl 251
+#define __NR_getsid 252
+#define __NR_fdatasync 253
+#define __NR_nfsservctl 254
+#define __NR_aplib 255
+
+#define _syscall0(type,name) \
+type name(void) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+ "t 0x6d\n\t" \
+ "sub %%g0, %%o0, %0\n\t" \
+ "movcc %%xcc, %%o0, %0\n\t" \
+ : "=r" (__res)\
+ : "0" (__NR_##name) \
+ : "g1", "o0", "cc"); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall1(type,name,type1,arg1) \
+type name(type1 arg1) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+ "mov %1, %%o0\n\t" \
+ "t 0x6d\n\t" \
+ "sub %%g0, %%o0, %0\n\t" \
+ "movcc %%xcc, %%o0, %0\n\t" \
+ : "=r" (__res), "=r" ((long)(arg1)) \
+ : "0" (__NR_##name),"1" ((long)(arg1)) \
+ : "g1", "o0", "cc"); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+type name(type1 arg1,type2 arg2) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+ "mov %1, %%o0\n\t" \
+ "mov %2, %%o1\n\t" \
+ "t 0x6d\n\t" \
+ "sub %%g0, %%o0, %0\n\t" \
+ "movcc %%xcc, %%o0, %0\n\t" \
+ : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)) \
+ : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)) \
+ : "g1", "o0", "o1", "cc"); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name(type1 arg1,type2 arg2,type3 arg3) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+ "mov %1, %%o0\n\t" \
+ "mov %2, %%o1\n\t" \
+ "mov %3, %%o2\n\t" \
+ "t 0x6d\n\t" \
+ "sub %%g0, %%o0, %0\n\t" \
+ "movcc %%xcc, %%o0, %0\n\t" \
+ : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
+ "=r" ((long)(arg3)) \
+ : "0" (__NR_##name), "1" ((long)(arg1)), "2" ((long)(arg2)), \
+ "3" ((long)(arg3)) \
+ : "g1", "o0", "o1", "o2", "cc"); \
+if (__res>=0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+{ \
+long __res; \
+__asm__ __volatile__ ("mov %0, %%g1\n\t" \
+ "mov %1, %%o0\n\t" \
+ "mov %2, %%o1\n\t" \
+ "mov %3, %%o2\n\t" \
+ "mov %4, %%o3\n\t" \
+ "t 0x6d\n\t" \
+ "sub %%g0,%%o0, %0\n\t" \
+ "movcc %%xcc, %%o0, %0\n\t" \
+ : "=r" (__res), "=r" ((long)(arg1)), "=r" ((long)(arg2)), \
+ "=r" ((long)(arg3)), "=r" ((long)(arg4)) \
+ : "0" (__NR_##name),"1" ((long)(arg1)),"2" ((long)(arg2)), \
+ "3" ((long)(arg3)),"4" ((long)(arg4)) \
+ : "g1", "o0", "o1", "o2", "o3", "cc"); \
+if (__res>=0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
+{ \
+ long __res; \
+\
+__asm__ __volatile__ ("mov %1, %%o0\n\t" \
+ "mov %2, %%o1\n\t" \
+ "mov %3, %%o2\n\t" \
+ "mov %4, %%o3\n\t" \
+ "mov %5, %%o4\n\t" \
+ "mov %6, %%g1\n\t" \
+ "t 0x6d\n\t" \
+ "sub %%g0, %%o0, %0\n\t" \
+ "movcc %%xcc, %%o0, %0\n\t" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)), \
+ "r" ((long)(arg3)),"r" ((long)(arg4)),"r" ((long)(arg5)), \
+ "i" (__NR_##name) \
+ : "g1", "o0", "o1", "o2", "o3", "o4", "cc"); \
+if (__res>=0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+#ifdef __KERNEL_SYSCALLS__
+
+/*
+ * we need this inline - forking from kernel space will result
+ * in NO COPY ON WRITE (!!!), until an execve is executed. This
+ * is no problem, but for the stack. This is handled by not letting
+ * main() use the stack at all after fork(). Thus, no function
+ * calls - which means inline code for fork too, as otherwise we
+ * would use the stack upon exit from 'fork()'.
+ *
+ * Actually only pause and fork are needed inline, so that there
+ * won't be any messing with the stack from main(), but we define
+ * some others too.
+ */
+#define __NR__exit __NR_exit
+static __inline__ _syscall0(int,idle)
+static __inline__ _syscall0(int,pause)
+static __inline__ _syscall0(int,sync)
+static __inline__ _syscall0(pid_t,setsid)
+static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
+static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static __inline__ _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
+static __inline__ _syscall1(int,dup,int,fd)
+static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp)
+static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode)
+static __inline__ _syscall1(int,close,int,fd)
+static __inline__ _syscall1(int,_exit,int,exitcode)
+static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static __inline__ _syscall1(int,delete_module,const char *,name)
+
+static __inline__ pid_t wait(int * wait_stat)
+{
+ return waitpid(-1,wait_stat,0);
+}
+
+extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+
+#endif /* __KERNEL_SYSCALLS__ */
+
+#ifdef __KERNEL__
+/* sysconf options, for SunOS compatibility */
+#define _SC_ARG_MAX 1
+#define _SC_CHILD_MAX 2
+#define _SC_CLK_TCK 3
+#define _SC_NGROUPS_MAX 4
+#define _SC_OPEN_MAX 5
+#define _SC_JOB_CONTROL 6
+#define _SC_SAVED_IDS 7
+#define _SC_VERSION 8
+#endif
+
+#endif /* _SPARC64_UNISTD_H */
diff -Nur linux-2.2.20ea/xattr.c linux-2.2.20acl/xattr.c
--- linux-2.2.20ea/xattr.c Wed Mar 20 01:56:10 2002
+++ linux-2.2.20acl/xattr.c Thu Jan 1 01:00:00 1970
@@ -1,1259 +0,0 @@
-/*
- * linux/fs/ext2/xattr.c
- *
- * Copyright (C) 2001 by Andreas Gruenbacher,
- *
- * Fix by Harrison Xing .
- * Extended attributes for symlinks and special files added per
- * suggestion of Luka Renko .
- */
-
-/*
- * Extended attributes are stored on disk blocks that allocated outside of
- * any inode. The i_file_acl field is them made to point to this allocated
- * block. If all extended attributes of an inode are identical, these
- * inodes may share the same extended attribute block. Such situations
- * are automatically detected by keeping a cache of recent attribute block
- * numbers and hashes over the block's contents.
- *
- *
- * Extended attribute block layout:
- *
- * +------------------+
- * | header |
- * ¦ entry 1 | |
- * | entry 2 | | growing downwards
- * | entry 3 | v
- * | four null bytes |
- * | . . . |
- * | value 1 | ^
- * | value 3 | | growing upwards
- * | value 2 | |
- * +------------------+
- *
- * The block header is followed by multiple entry descriptors. These entry
- * descriptors are variable in size, and alligned to EXT2_XATTR_PAD
- * byte boundaries. The entry descriptors are sorted by attribute name,
- * so that two extended attribute blocks can be compared efficiently.
- *
- * Attribute values are aligned to the end of the block, stored in
- * no specific order. They are also padded to EXT2_XATTR_PAD byte
- * boundaries. No additional gaps are left between them.
- *
- * Locking strategy
- * ----------------
- * The VFS already holds the BKL and the inode->i_sem semaphore when any of
- * the xattr inode operations is called, so we are guaranteed that only one
- * processes accesses extended attributes of an inode at any time.
- *
- * For writing we also grab the ext2_xattr_sem semaphore. This ensures that
- * only a single process is modifying an extended attribute block, even
- * if the block is shared among inodes.
- *
- * Note for porting to 2.5
- * -----------------------
- * The BLK will no longer be held in the xattr inode operations.
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#define EXT2_EA_USER "user."
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
-# define mark_buffer_dirty(bh) mark_buffer_dirty(bh, 1)
-#endif
-
-#define HDR(bh) ((struct ext2_xattr_header *)((bh)->b_data))
-#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr))
-#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)
-#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
-
-#ifdef EXT2_XATTR_DEBUG
-# define ea_idebug(inode, f...) do { \
- printk(KERN_DEBUG "inode %s:%ld: ", \
- kdevname(inode->i_dev), inode->i_ino); \
- printk(f); \
- printk("\n"); \
- } while (0)
-# define ea_bdebug(bh, f...) do { \
- printk(KERN_DEBUG "block %s:%ld: ", \
- kdevname(bh->b_dev), bh->b_blocknr); \
- printk(f); \
- printk("\n"); \
- } while (0)
-#else
-# define ea_idebug(f...)
-# define ea_bdebug(f...)
-#endif
-
-static int ext2_xattr_set2(struct inode *, struct buffer_head *,
- struct ext2_xattr_header *);
-
-#ifdef CONFIG_EXT2_FS_XATTR_SHARING
-
-static int ext2_xattr_cache_insert(struct buffer_head *);
-static struct buffer_head * ext2_xattr_cache_find(struct inode *,
- struct ext2_xattr_header *);
-static void ext2_xattr_cache_remove(struct buffer_head *);
-static void ext2_xattr_rehash(struct ext2_xattr_header *,
- struct ext2_xattr_entry *);
-
-static struct mb_cache *ext2_xattr_cache;
-
-#else
-# define ext2_xattr_cache_insert(bh) 0
-# define ext2_xattr_cache_find(inode, header) NULL
-# define ext2_xattr_cache_remove(bh) while(0) {}
-# define ext2_xattr_rehash(header, entry) while(0) {}
-#endif
-
-/*
- * If a file system does not share extended attributes among inodes,
- * we should not need the ext2_xattr_sem semaphore. However, the
- * filesystem may still contain shared blocks, so we always take
- * the lock.
- */
-
-DECLARE_MUTEX(ext2_xattr_sem);
-
-static inline void
-ext2_xattr_lock(void)
-{
- down(&ext2_xattr_sem);
-}
-
-static inline void
-ext2_xattr_unlock(void)
-{
- up(&ext2_xattr_sem);
-}
-
-static inline int
-ext2_xattr_new_block(struct inode *inode, int * errp, int force)
-{
- struct super_block *sb = inode->i_sb;
- int goal = le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block) +
- EXT2_I(inode)->i_block_group * EXT2_BLOCKS_PER_GROUP(sb);
-
- /* How can we enforce the allocation? */
- int block = ext2_new_block(inode, goal, 0, 0, errp);
-#ifdef OLD_QUOTAS
- if (!*errp)
- inode->i_blocks += inode->i_sb->s_blocksize >> 9;
-#endif
- return block;
-}
-
-/*
- * Force the allocation of a block in quotas: We know that we will
- * later free a block, so effectively the quotas won't change.
- */
-static inline int
-ext2_xattr_quota_alloc(struct inode *inode, int force)
-{
- /* How can we enforce the allocation? */
-#ifdef OLD_QUOTAS
- int error = DQUOT_ALLOC_BLOCK(inode->i_sb, inode, 1);
- if (!error)
- inode->i_blocks += inode->i_sb->s_blocksize >> 9;
-#else
- int error = DQUOT_ALLOC_BLOCK(inode, 1);
-#endif
- return error;
-}
-
-#ifdef OLD_QUOTAS
-
-static inline void
-ext2_xattr_quota_free(struct inode *inode)
-{
- DQUOT_FREE_BLOCK(inode->i_sb, inode, 1);
- inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
-}
-
-void
-ext2_xattr_free_block(struct inode * inode, unsigned long block)
-{
- ext2_free_blocks(inode, block, 1);
- inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
-}
-
-#else
-# define ext2_xattr_quota_free(inode) \
- DQUOT_FREE_BLOCK(inode, 1)
-# define ext2_xattr_free_block(inode, block) \
- ext2_free_blocks(inode, block, 1)
-#endif
-
-
-#ifdef CONFIG_EXT2_FS_XATTR_USER
-static int
-ext2_get_user_xattr(struct inode *inode, const char *name,
- void *buffer, size_t size)
-{
- int error;
-
- if (!IS_XATTR_USER(inode))
- return -ENOTSUP;
- error = permission(inode, MAY_READ);
- if (error)
- return error;
-
- return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name, buffer, size);
-}
-
-static int
-ext2_set_user_xattr(struct inode *inode, const char *name,
- void *value, size_t size, int flags)
-{
- int error;
-
- if (!IS_XATTR_USER(inode))
- return -ENOTSUP;
- error = permission(inode, MAY_WRITE);
- if (error)
- return error;
-
- return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name,
- value, size, flags);
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
-
-static inline struct buffer_head *
-sb_bread(struct super_block *sb, int block)
-{
- return bread(sb->s_dev, block, sb->s_blocksize);
-}
-
-static inline struct buffer_head *
-sb_getblk(struct super_block *sb, int block)
-{
- return getblk(sb->s_dev, block, sb->s_blocksize);
-}
-
-#endif
-
-/*
- * Decode the extended attribute name, and translate it into
- * the name index + name suffix.
- */
-static int
-ext2_xattr_name(const char **name)
-{
- if (*name) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
- if (!strncmp(*name, EXT2_EA_USER, sizeof(EXT2_EA_USER)-1)) {
- if (!strcmp(*name+sizeof(EXT2_EA_USER)-1, ""))
- return -EINVAL;
- *name += 5;
- return EXT2_XATTR_INDEX_USER;
- } else
-#endif
- return -ENOTSUP;
- }
- return 0;
-}
-
-/*
- * Inode operation getxattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-ssize_t
-ext2_getxattr(struct dentry *dentry, const char *name,
- void *buffer, size_t size)
-{
- struct inode *inode = dentry->d_inode;
- int name_index;
-
- assert_down(&dentry->d_inode->i_sem);
-
- name_index = ext2_xattr_name(&name);
- switch(name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
- case EXT2_XATTR_INDEX_USER:
- return ext2_get_user_xattr(inode, name, buffer, size);
-#endif
- default:
- if (name_index >= 0)
- name_index = -EINVAL;
- return name_index;
- }
-}
-
-/*
- * Inode operation listxattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-ssize_t
-ext2_listxattr(struct dentry *dentry, char *buffer, size_t size)
-{
- assert_down(&dentry->d_inode->i_sem);
-
- return ext2_xattr_list(dentry->d_inode, buffer, size);
-}
-
-/*
- * Inode operation setxattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-int
-ext2_setxattr(struct dentry *dentry, const char *name,
- void *value, size_t size, int flags)
-{
- struct inode *inode = dentry->d_inode;
- int name_index;
-
- assert_down(&dentry->d_inode->i_sem);
-
- if (size == 0)
- value = ""; /* empty EA, do not remove */
-
- name_index = ext2_xattr_name(&name);
- switch(name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
- case EXT2_XATTR_INDEX_USER:
- return ext2_set_user_xattr(inode, name,
- value, size, flags);
-#endif
- default:
- if (name_index >= 0)
- name_index = -EINVAL;
- return name_index;
- }
-}
-
-/*
- * Inode operation removexattr()
- *
- * dentry->d_inode->i_sem down
- * BKL held [before 2.5.x]
- */
-int
-ext2_removexattr(struct dentry *dentry, const char *name)
-{
- struct inode *inode = dentry->d_inode;
- int name_index;
-
- assert_down(&dentry->d_inode->i_sem);
-
- name_index = ext2_xattr_name(&name);
- switch(name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
- case EXT2_XATTR_INDEX_USER:
- return ext2_set_user_xattr(inode, name,
- NULL, 0, XATTR_REPLACE);
-#endif
- default:
- if (name_index >= 0)
- name_index = -EINVAL;
- return name_index;
- }
-}
-
-/*
- * ext2_xattr_get()
- *
- * Copy an extended attribute into the buffer
- * provided, or compute the buffer size required.
- * Buffer is NULL to compute the size of the buffer required.
- *
- * Returns a negative error number on failure, or the number of bytes
- * used / required on success.
- */
-int
-ext2_xattr_get(struct inode *inode, int name_index, const char *name,
- void *buffer, size_t buffer_size)
-{
- struct buffer_head *bh = NULL;
- struct ext2_xattr_entry *entry;
- unsigned int block, size;
- char *end;
- int name_len, error;
-
- ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
- name_index, name, buffer, (long)buffer_size);
-
- if (!IS_XATTR(inode))
- return -ENOTSUP;
- if (name == NULL)
- return -EINVAL;
- if (!EXT2_I(inode)->i_file_acl)
- return -ENOATTR;
- block = EXT2_I(inode)->i_file_acl;
- ea_idebug(inode, "reading block %d", block);
- bh = sb_bread(inode->i_sb, block);
- if (!bh)
- return -EIO;
- ea_bdebug(bh, "b_count=%d, refcount=%d",
- atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
- end = bh->b_data + bh->b_size;
- if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
- HDR(bh)->h_blocks != cpu_to_le32(1)) {
-bad_block: ext2_error(inode->i_sb, "ext2_xattr_get",
- "inode %ld: bad block %d", inode->i_ino, block);
- error = -EIO;
- goto cleanup;
- }
- /* find named attribute */
- name_len = strlen(name);
-
- error = -ERANGE;
- if (name_len > 255)
- goto cleanup;
- entry = FIRST_ENTRY(bh);
- while (!IS_LAST_ENTRY(entry)) {
- struct ext2_xattr_entry *next =
- EXT2_XATTR_NEXT(entry);
- if ((char *)next >= end)
- goto bad_block;
- if (name_index == entry->e_name_index &&
- name_len == entry->e_name_len &&
- memcmp(name, entry->e_name, name_len) == 0)
- goto found;
- entry = next;
- }
- /* Check the remaining name entries */
- while (!IS_LAST_ENTRY(entry)) {
- struct ext2_xattr_entry *next =
- EXT2_XATTR_NEXT(entry);
- if ((char *)next >= end)
- goto bad_block;
- entry = next;
- }
- if (ext2_xattr_cache_insert(bh))
- ea_idebug(inode, "cache insert failed");
- error = -ENOATTR;
- goto cleanup;
-found:
- /* check the buffer size */
- if (entry->e_value_block != 0)
- goto bad_block;
- size = le32_to_cpu(entry->e_value_size);
- if (size > inode->i_sb->s_blocksize ||
- le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
- goto bad_block;
-
- if (ext2_xattr_cache_insert(bh))
- ea_idebug(inode, "cache insert failed");
- if (buffer) {
- error = -ERANGE;
- if (size > buffer_size)
- goto cleanup;
- /* return value of attribute */
- memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),
- size);
- }
- error = size;
-
-cleanup:
- brelse(bh);
-
- return error;
-}
-
-/*
- * ext2_xattr_list()
- *
- * Copy a list of attribute names into the buffer
- * provided, or compute the buffer size required.
- * Buffer is NULL to compute the size of the buffer required.
- *
- * Returns a negative error number on failure, or the number of bytes
- * used / required on success.
- */
-int
-ext2_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
-{
- struct buffer_head *bh = NULL;
- struct ext2_xattr_entry *entry;
- unsigned int block, size = 0;
- char *buf, *end;
- int error;
-
- ea_idebug(inode, "buffer=%p, buffer_size=%ld",
- buffer, (long)buffer_size);
-
- if (!IS_XATTR(inode))
- return -ENOTSUP;
- if (!EXT2_I(inode)->i_file_acl)
- return 0;
- block = EXT2_I(inode)->i_file_acl;
- ea_idebug(inode, "reading block %d", block);
- bh = sb_bread(inode->i_sb, block);
- if (!bh)
- return -EIO;
- ea_bdebug(bh, "b_count=%d, refcount=%d",
- atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
- end = bh->b_data + bh->b_size;
- if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
- HDR(bh)->h_blocks != cpu_to_le32(1)) {
-bad_block: ext2_error(inode->i_sb, "ext2_xattr_list",
- "inode %ld: bad block %d", inode->i_ino, block);
- error = -EIO;
- goto cleanup;
- }
- /* compute the size required for the list of attribute names */
- for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
- entry = EXT2_XATTR_NEXT(entry)) {
- struct ext2_xattr_entry *next =
- EXT2_XATTR_NEXT(entry);
- if ((char *)next >= end)
- goto bad_block;
- switch(entry->e_name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
- case EXT2_XATTR_INDEX_USER:
- if (!IS_XATTR_USER(inode))
- continue;
- size += 5 + entry->e_name_len;
- break;
-#endif
- default:
- /* skip unrecognized name index */
- continue;
- }
- size++;
- }
-
- if (ext2_xattr_cache_insert(bh))
- ea_idebug(inode, "cache insert failed");
- if (!buffer) {
- error = size;
- goto cleanup;
- } else {
- error = -ERANGE;
- if (size > buffer_size)
- goto cleanup;
- }
-
- /* list the attribute names */
- buf = buffer;
- for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
- entry = EXT2_XATTR_NEXT(entry)) {
- char *nsp = "", *name;
- int nsp_len, name_len;
-
- switch(entry->e_name_index) {
-#ifdef CONFIG_EXT2_FS_XATTR_USER
- case EXT2_XATTR_INDEX_USER:
- if (!IS_XATTR_USER(inode))
- continue;
- nsp = EXT2_EA_USER;
- nsp_len = sizeof(EXT2_EA_USER)-1;
- name = entry->e_name;
- name_len = entry->e_name_len;
- break;
-#endif
- default:
- /* skip unrecognized name index */
- continue;
- }
-
- memcpy(buf, nsp, nsp_len);
- buf += nsp_len;
- memcpy(buf, name, name_len);
- buf += name_len;
- memset(buf++, '\0', 1);
- }
- error = size;
-
-cleanup:
- brelse(bh);
-
- return error;
-}
-
-/*
- * If the EXT2_FEATURE_COMPAT_EXT_ATTR feature of this file system is
- * not set, set it.
- */
-static void ext2_xattr_update_super_block(struct super_block *sb)
-{
- if (EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR))
- return;
-
- lock_super(sb);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
- EXT2_SB(sb)->s_feature_compat |= EXT2_FEATURE_COMPAT_EXT_ATTR;
-#endif
- EXT2_SB(sb)->s_es->s_feature_compat |=
- cpu_to_le32(EXT2_FEATURE_COMPAT_EXT_ATTR);
- sb->s_dirt = 1;
- mark_buffer_dirty(EXT2_SB(sb)->s_sbh);
- unlock_super(sb);
-}
-
-/*
- * ext2_xattr_set()
- *
- * Create, replace or remove an extended attribute for this inode. Buffer
- * is NULL to remove an existing extended attribute, and non-NULL to
- * either replace an existing extended attribute, or create a new extended
- * attribute. The flags XATTR_REPLACE and XATTR_CREATE
- * specify that an extended attribute must exist and must not exist
- * previous to the call, respectively.
- *
- * Returns 0, or a negative error number on failure.
- */
-int
-ext2_xattr_set(struct inode *inode, int name_index, const char *name,
- void *value, size_t value_len, int flags)
-{
- struct super_block *sb = inode->i_sb;
- struct buffer_head *bh = NULL;
- struct ext2_xattr_header *header = NULL;
- struct ext2_xattr_entry *here, *last;
- unsigned int name_len;
- int min_offs = sb->s_blocksize, not_found = 1, free, error;
- char *end;
-
- /*
- * header -- Points either into bh, or to a temporarily
- * allocated buffer.
- * here -- The named entry found, or the place for inserting, within
- * the block pointed to by header.
- * last -- Points right after the last named entry within the block
- * pointed to by header.
- * min_offs -- The offset of the first value (values are aligned
- * towards the end of the block).
- * end -- Points right after the block pointed to by header.
- */
-
- ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
- name_index, name, value, (long)value_len);
-
- if (!IS_XATTR(inode))
- return -ENOTSUP;
- if (IS_RDONLY(inode))
- return -EROFS;
- if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- return -EPERM;
- if (value == NULL)
- value_len = 0;
- if (name == NULL)
- return -EINVAL;
- name_len = strlen(name);
- if (name_len > 255 || value_len > sb->s_blocksize)
- return -ERANGE;
- ext2_xattr_lock();
-
- if (EXT2_I(inode)->i_file_acl) {
- /* The inode already has an extended attribute block. */
- int block = EXT2_I(inode)->i_file_acl;
-
- bh = sb_bread(sb, block);
- error = -EIO;
- if (!bh)
- goto cleanup;
- ea_bdebug(bh, "b_count=%d, refcount=%d",
- atomic_read(&(bh->b_count)),
- le32_to_cpu(HDR(bh)->h_refcount));
- header = HDR(bh);
- end = bh->b_data + bh->b_size;
- if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
- header->h_blocks != cpu_to_le32(1)) {
-bad_block: ext2_error(sb, "ext2_xattr_set",
- "inode %ld: bad block %d", inode->i_ino, block);
- error = -EIO;
- goto cleanup;
- }
- /* Find the named attribute. */
- here = FIRST_ENTRY(bh);
- while (!IS_LAST_ENTRY(here)) {
- struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here);
- if ((char *)next >= end)
- goto bad_block;
- if (!here->e_value_block && here->e_value_size) {
- int offs = le16_to_cpu(here->e_value_offs);
- if (offs < min_offs)
- min_offs = offs;
- }
- not_found = name_index - here->e_name_index;
- if (!not_found)
- not_found = name_len - here->e_name_len;
- if (!not_found)
- not_found = memcmp(name, here->e_name,name_len);
- if (not_found <= 0)
- break;
- here = next;
- }
- last = here;
- /* We still need to compute min_offs and last. */
- while (!IS_LAST_ENTRY(last)) {
- struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last);
- if ((char *)next >= end)
- goto bad_block;
- if (!last->e_value_block && last->e_value_size) {
- int offs = le16_to_cpu(last->e_value_offs);
- if (offs < min_offs)
- min_offs = offs;
- }
- last = next;
- }
-
- /* Check whether we have enough space left. */
- free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);
- } else {
- /* We will use a new extended attribute block. */
- free = sb->s_blocksize -
- sizeof(struct ext2_xattr_header) - sizeof(__u32);
- here = last = NULL; /* avoid gcc uninitialized warning. */
- }
-
- if (not_found) {
- /* Request to remove a nonexistent attribute? */
- error = -ENOATTR;
- if (flags & XATTR_REPLACE)
- goto cleanup;
- error = 0;
- if (value == NULL)
- goto cleanup;
- else
- free -= EXT2_XATTR_LEN(name_len);
- } else {
- /* Request to create an existing attribute? */
- error = -EEXIST;
- if (flags & XATTR_CREATE)
- goto cleanup;
- if (!here->e_value_block && here->e_value_size) {
- unsigned int size = le32_to_cpu(here->e_value_size);
-
- if (le16_to_cpu(here->e_value_offs) + size >
- sb->s_blocksize || size > sb->s_blocksize)
- goto bad_block;
- free += EXT2_XATTR_SIZE(size);
- }
- }
- free -= EXT2_XATTR_SIZE(value_len);
- error = -ENOSPC;
- if (free < 0)
- goto cleanup;
-
- /* Here we know that we can set the new attribute. */
-
- if (header) {
- if (header->h_refcount == cpu_to_le32(1)) {
- ea_bdebug(bh, "modifying in-place");
- ext2_xattr_cache_remove(bh);
- } else {
- int offset;
-
- ea_bdebug(bh, "cloning");
- header = kmalloc(bh->b_size, GFP_KERNEL);
- error = -ENOMEM;
- if (header == NULL)
- goto cleanup;
- memcpy(header, HDR(bh), bh->b_size);
- header->h_refcount = cpu_to_le32(1);
- offset = (char *)header - bh->b_data;
- here = ENTRY((char *)here + offset);
- last = ENTRY((char *)last + offset);
- }
- } else {
- /* Allocate a buffer where we construct the new block. */
- header = kmalloc(sb->s_blocksize, GFP_KERNEL);
- error = -ENOMEM;
- if (header == NULL)
- goto cleanup;
- memset(header, '\0', sb->s_blocksize);
- end = (char *)header + sb->s_blocksize;
- header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC);
- header->h_blocks = header->h_refcount = cpu_to_le32(1);
- last = here = ENTRY(header+1);
- }
-
- if (not_found) {
- /* Insert the new name. */
- int size = EXT2_XATTR_LEN(name_len);
- int rest = (char *)last - (char *)here;
- memmove((char *)here + size, here, rest);
- memset(here, '\0', size);
- here->e_name_index = name_index;
- here->e_name_len = name_len;
- memcpy(here->e_name, name, name_len);
- } else {
- /* Remove the old value. */
- if (!here->e_value_block && here->e_value_size) {
- char *first_val = (char *)header + min_offs;
- int offs = le16_to_cpu(here->e_value_offs);
- char *val = (char *)header + offs;
- size_t size = EXT2_XATTR_SIZE(
- le32_to_cpu(here->e_value_size));
- memmove(first_val + size, first_val, val - first_val);
- memset(first_val, '\0', size);
- here->e_value_offs = 0;
- min_offs += size;
-
- /* Adjust all value offsets. */
- last = ENTRY(header+1);
- while (!IS_LAST_ENTRY(last)) {
- int o = le16_to_cpu(last->e_value_offs);
- if (!last->e_value_block && o < offs)
- last->e_value_offs =
- cpu_to_le16(o + size);
- last = EXT2_XATTR_NEXT(last);
- }
- }
- if (value == NULL) {
- /* Remove this attribute. */
- if (EXT2_XATTR_NEXT(ENTRY(header+1)) == last) {
- /* This block is now empty. */
- error = ext2_xattr_set2(inode, bh, NULL);
- goto cleanup;
- } else {
- /* Remove the old name. */
- int size = EXT2_XATTR_LEN(name_len);
- last = ENTRY((char *)last - size);
- memmove(here, (char*)here + size,
- (char*)last - (char*)here);
- memset(last, '\0', size);
- }
- }
- }
-
- if (value != NULL) {
- /* Insert the new value. */
- here->e_value_size = cpu_to_le32(value_len);
- if (value_len) {
- size_t size = EXT2_XATTR_SIZE(value_len);
- char *val = (char *)header + min_offs - size;
- here->e_value_offs =
- cpu_to_le16((char *)val - (char *)header);
- memset(val + size - EXT2_XATTR_PAD, '\0',
- EXT2_XATTR_PAD); /* Clear the pad bytes. */
- memcpy(val, value, value_len);
- }
- }
- ext2_xattr_rehash(header, here);
-
- error = ext2_xattr_set2(inode, bh, header);
-
-cleanup:
- brelse(bh);
- if (!(bh && header == HDR(bh)))
- kfree(header);
- ext2_xattr_unlock();
-
- return error;
-}
-
-/*
- * Second half of ext2_xattr_set(): Update the file system.
- */
-static int
-ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
- struct ext2_xattr_header *header)
-{
- struct super_block *sb = inode->i_sb;
- struct buffer_head *new_bh = NULL;
- int error;
-
- if (header) {
- new_bh = ext2_xattr_cache_find(inode, header);
- if (new_bh) {
- /*
- * We found an identical block in the cache.
- * The old block will be released after updating
- * the inode.
- */
- ea_bdebug(old_bh, "reusing block %ld",
- new_bh->b_blocknr);
-
- error = -EDQUOT;
- if (ext2_xattr_quota_alloc(inode, 1))
- goto cleanup;
-
- HDR(new_bh)->h_refcount = cpu_to_le32(
- le32_to_cpu(HDR(new_bh)->h_refcount) + 1);
- ea_bdebug(new_bh, "refcount now=%d",
- le32_to_cpu(HDR(new_bh)->h_refcount));
- } else if (old_bh && header == HDR(old_bh)) {
- /* Keep this block. */
- new_bh = old_bh;
- ext2_xattr_cache_insert(new_bh);
- } else {
- /* We need to allocate a new block */
- int force = EXT2_I(inode)->i_file_acl != 0;
- int block = ext2_xattr_new_block(inode, &error, force);
- if (error)
- goto cleanup;
- ea_idebug(inode, "creating block %d", block);
-
- new_bh = sb_getblk(sb, block);
- if (!new_bh) {
- ext2_xattr_free_block(inode, block);
- error = -EIO;
- goto cleanup;
- }
- lock_buffer(new_bh);
- memcpy(new_bh->b_data, header, new_bh->b_size);
- mark_buffer_uptodate(new_bh, 1);
- unlock_buffer(new_bh);
- ext2_xattr_cache_insert(new_bh);
-
- ext2_xattr_update_super_block(sb);
- }
- mark_buffer_dirty(new_bh);
- if (IS_SYNC(inode)) {
- ll_rw_block(WRITE, 1, &new_bh);
- wait_on_buffer(new_bh);
- error = -EIO;
- if (buffer_req(new_bh) && !buffer_uptodate(new_bh))
- goto cleanup;
- }
- }
-
- /* Update the inode. */
- EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
- inode->i_ctime = CURRENT_TIME;
- if (IS_SYNC(inode)) {
- error = ext2_sync_inode (inode);
- if (error)
- goto cleanup;
- } else
- mark_inode_dirty(inode);
-
- error = 0;
- if (old_bh && old_bh != new_bh) {
- /*
- * If there was an old block, and we are not still using it,
- * we now release the old block.
- */
- unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount);
-
- if (refcount == 1) {
- /* Free the old block. */
- ea_bdebug(old_bh, "freeing");
- ext2_xattr_free_block(inode, old_bh->b_blocknr);
- mark_buffer_clean(old_bh);
- } else {
- /* Decrement the refcount only. */
- refcount--;
- HDR(old_bh)->h_refcount = cpu_to_le32(refcount);
- ext2_xattr_quota_free(inode);
- mark_buffer_dirty(old_bh);
- ea_bdebug(old_bh, "refcount now=%d", refcount);
- }
- }
-
-cleanup:
- if (old_bh != new_bh)
- brelse(new_bh);
-
- return error;
-}
-
-/*
- * ext2_xattr_drop_inode()
- *
- * Free extended attribute resources associated with this inode. This
- * is called immediately before an inode is freed.
- */
-void
-ext2_xattr_drop_inode(struct inode *inode)
-{
- struct buffer_head *bh;
- unsigned int block = EXT2_I(inode)->i_file_acl;
-
- if (!block)
- return;
- ext2_xattr_lock();
-
- bh = sb_bread(inode->i_sb, block);
- if (!bh) {
- ext2_error(inode->i_sb, "ext2_xattr_drop_inode",
- "inode %ld: block %d read error", inode->i_ino, block);
- goto cleanup;
- }
- ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count)));
- if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
- HDR(bh)->h_blocks != cpu_to_le32(1)) {
- ext2_error(inode->i_sb, "ext2_xattr_drop_inode",
- "inode %ld: bad block %d", inode->i_ino, block);
- goto cleanup;
- }
- ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1);
- if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
- ext2_xattr_cache_remove(bh);
- ext2_xattr_free_block(inode, block);
- bforget(bh);
- bh = NULL;
- } else {
- HDR(bh)->h_refcount = cpu_to_le32(
- le32_to_cpu(HDR(bh)->h_refcount) - 1);
- mark_buffer_dirty(bh);
- if (IS_SYNC(inode)) {
- ll_rw_block(WRITE, 1, &bh);
- wait_on_buffer(bh);
- }
- ext2_xattr_quota_free(inode);
- }
- EXT2_I(inode)->i_file_acl = 0;
-
-cleanup:
- brelse(bh);
- ext2_xattr_unlock();
-}
-
-/*
- * ext2_xattr_put_super()
- *
- * This is called when a file system is unmounted.
- */
-void
-ext2_xattr_put_super(struct super_block *sb)
-{
-#ifdef CONFIG_EXT2_FS_XATTR_SHARING
- mb_cache_shrink(ext2_xattr_cache, sb->s_dev);
-#endif
-}
-
-#ifdef CONFIG_EXT2_FS_XATTR_SHARING
-
-/*
- * ext2_xattr_cache_insert()
- *
- * Create a new entry in the extended attribute cache, and insert
- * it unless such an entry is already in the cache.
- *
- * Returns 0, or a negative error number on failure.
- */
-static int
-ext2_xattr_cache_insert(struct buffer_head *bh)
-{
- __u32 hash = le32_to_cpu(HDR(bh)->h_hash);
- struct mb_cache_entry *ce;
- int error;
-
- ce = mb_cache_entry_alloc(ext2_xattr_cache);
- if (!ce)
- return -ENOMEM;
- error = mb_cache_entry_insert(ce, bh->b_dev, bh->b_blocknr, &hash);
- if (error) {
- mb_cache_entry_free(ce);
- if (error == -EBUSY) {
- ea_bdebug(bh, "already in cache (%d cache entries)",
- atomic_read(&ext2_xattr_cache->c_entry_count));
- error = 0;
- }
- } else {
- ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash,
- atomic_read(&ext2_xattr_cache->c_entry_count));
- mb_cache_entry_release(ce);
- }
- return error;
-}
-
-/*
- * ext2_xattr_cmp()
- *
- * Compare two extended attribute blocks for equality.
- *
- * Returns 0 if the blocks are equal, 1 if they differ, and
- * a negative error number on errors.
- */
-static int
-ext2_xattr_cmp(struct ext2_xattr_header *header1,
- struct ext2_xattr_header *header2)
-{
- struct ext2_xattr_entry *entry1, *entry2;
-
- entry1 = ENTRY(header1+1);
- entry2 = ENTRY(header2+1);
- while (!IS_LAST_ENTRY(entry1)) {
- if (IS_LAST_ENTRY(entry2))
- return 1;
- if (entry1->e_hash != entry2->e_hash ||
- entry1->e_name_len != entry2->e_name_len ||
- entry1->e_value_size != entry2->e_value_size ||
- memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len))
- return 1;
- if (entry1->e_value_block != 0 || entry2->e_value_block != 0)
- return -EIO;
- if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs),
- (char *)header2 + le16_to_cpu(entry2->e_value_offs),
- le32_to_cpu(entry1->e_value_size)))
- return 1;
-
- entry1 = EXT2_XATTR_NEXT(entry1);
- entry2 = EXT2_XATTR_NEXT(entry2);
- }
- if (!IS_LAST_ENTRY(entry2))
- return 1;
- return 0;
-}
-
-/*
- * ext2_xattr_cache_find()
- *
- * Find an identical extended attribute block.
- *
- * Returns a pointer to the block found, or NULL if such a block was
- * not found or an error occurred.
- */
-static struct buffer_head *
-ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header)
-{
- __u32 hash = le32_to_cpu(header->h_hash);
- struct mb_cache_entry *ce;
-
- if (!header->h_hash)
- return NULL; /* never share */
- ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
- ce = mb_cache_entry_find_first(ext2_xattr_cache, 0, inode->i_dev, hash);
- while (ce) {
- struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block);
-
- if (!bh) {
- ext2_error(inode->i_sb, "ext2_xattr_cache_find",
- "inode %ld: block %ld read error",
- inode->i_ino, ce->e_block);
- } else if (le32_to_cpu(HDR(bh)->h_refcount) >
- EXT2_XATTR_REFCOUNT_MAX) {
- ea_idebug(inode, "block %ld refcount %d>%d",ce->e_block,
- le32_to_cpu(HDR(bh)->h_refcount),
- EXT2_XATTR_REFCOUNT_MAX);
- } else if (!ext2_xattr_cmp(header, HDR(bh))) {
- ea_bdebug(bh, "b_count=%d",atomic_read(&(bh->b_count)));
- mb_cache_entry_release(ce);
- return bh;
- }
- brelse(bh);
- ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash);
- }
- return NULL;
-}
-
-/*
- * ext2_xattr_cache_remove()
- *
- * Remove the cache entry of a block from the cache. Called when a
- * block becomes invalid.
- */
-static void
-ext2_xattr_cache_remove(struct buffer_head *bh)
-{
- struct mb_cache_entry *ce;
-
- ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_dev, bh->b_blocknr);
- if (ce) {
- ea_bdebug(bh, "removing (%d cache entries remaining)",
- atomic_read(&ext2_xattr_cache->c_entry_count)-1);
- mb_cache_entry_free(ce);
- } else
- ea_bdebug(bh, "no cache entry");
-}
-
-#define NAME_HASH_SHIFT 5
-#define VALUE_HASH_SHIFT 16
-
-/*
- * ext2_xattr_hash_entry()
- *
- * Compute the hash of an extended attribute.
- */
-static inline void ext2_xattr_hash_entry(struct ext2_xattr_header *header,
- struct ext2_xattr_entry *entry)
-{
- __u32 hash = 0;
- char *name = entry->e_name;
- int n;
-
- for (n=0; n < entry->e_name_len; n++) {
- hash = (hash << NAME_HASH_SHIFT) ^
- (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
- *name++;
- }
-
- if (entry->e_value_block == 0 && entry->e_value_size != 0) {
- __u32 *value = (__u32 *)((char *)header +
- le16_to_cpu(entry->e_value_offs));
- for (n = (le32_to_cpu(entry->e_value_size) +
- EXT2_XATTR_ROUND) >> EXT2_XATTR_PAD_BITS; n; n--) {
- hash = (hash << VALUE_HASH_SHIFT) ^
- (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
- le32_to_cpu(*value++);
- }
- }
- entry->e_hash = cpu_to_le32(hash);
-}
-
-#undef NAME_HASH_SHIFT
-#undef VALUE_HASH_SHIFT
-
-#define BLOCK_HASH_SHIFT 16
-
-/*
- * ext2_xattr_rehash()
- *
- * Re-compute the extended attribute hash value after an entry has changed.
- */
-static void ext2_xattr_rehash(struct ext2_xattr_header *header,
- struct ext2_xattr_entry *entry)
-{
- struct ext2_xattr_entry *here;
- __u32 hash = 0;
-
- ext2_xattr_hash_entry(header, entry);
- here = ENTRY(header+1);
- while (!IS_LAST_ENTRY(here)) {
- if (!here->e_hash) {
- /* Block is not shared if an entry's hash value == 0 */
- hash = 0;
- break;
- }
- hash = (hash << BLOCK_HASH_SHIFT) ^
- (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
- le32_to_cpu(here->e_hash);
- here = EXT2_XATTR_NEXT(here);
- }
- header->h_hash = cpu_to_le32(hash);
-}
-
-#undef BLOCK_HASH_SHIFT
-
-int ext2_xattr_init(void)
-{
- ext2_xattr_cache = mb_cache_create("ext2_xattr", NULL,
- sizeof(struct mb_cache_entry) +
- sizeof(struct mb_cache_entry_index), 1, 61);
- if (!ext2_xattr_cache)
- return -ENOMEM;
-
- return 0;
-}
-
-void ext2_xattr_done(void)
-{
- mb_cache_destroy(ext2_xattr_cache);
-}
-
-#else /* CONFIG_EXT2_FS_XATTR_SHARING */
-
-int ext2_xattr_init(void)
-{
- return 0;
-}
-
-void ext2_xattr_done(void)
-{
-}
-
-#endif /* CONFIG_EXT2_FS_XATTR_SHARING */