diff -ruNp linux-2.4.23/fs/Config.in linux-2.4.23.xattr/fs/Config.in --- linux-2.4.23/fs/Config.in 2003-12-09 14:43:30.455840312 -0500 +++ linux-2.4.23.xattr/fs/Config.in 2003-12-09 14:43:36.647898976 -0500 @@ -15,6 +15,7 @@ dep_mbool ' Enable reiserfs debug mode' dep_mbool ' Stats in /proc/fs/reiserfs' CONFIG_REISERFS_PROC_INFO $CONFIG_REISERFS_FS dep_mbool ' ReiserFS extended attributes' CONFIG_REISERFS_FS_XATTR $CONFIG_REISERFS_FS dep_mbool ' ReiserFS extended user attributes' CONFIG_REISERFS_FS_XATTR_USER $CONFIG_REISERFS_FS_XATTR +dep_mbool ' ReiserFS POSIX Access Control Lists' CONFIG_REISERFS_FS_POSIX_ACL $CONFIG_REISERFS_FS_XATTR $CONFIG_FS_POSIX_ACL dep_tristate 'ADFS file system support (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL dep_mbool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW $CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL diff -ruNp linux-2.4.23/fs/reiserfs/Makefile linux-2.4.23.xattr/fs/reiserfs/Makefile --- linux-2.4.23/fs/reiserfs/Makefile 2003-12-09 14:43:30.456840160 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/Makefile 2003-12-09 14:43:36.647898976 -0500 @@ -13,6 +13,7 @@ lbalance.o ibalance.o stree.o hashes.o b obj-$(CONFIG_REISERFS_FS_XATTR) += xattr.o obj-$(CONFIG_REISERFS_FS_XATTR_USER) += xattr_user.o +obj-$(CONFIG_REISERFS_FS_POSIX_ACL) += xattr_acl.o obj-m := $(O_TARGET) diff -ruNp linux-2.4.23/fs/reiserfs/file.c linux-2.4.23.xattr/fs/reiserfs/file.c --- linux-2.4.23/fs/reiserfs/file.c 2003-12-09 14:43:30.456840160 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/file.c 2003-12-09 14:43:36.647898976 -0500 @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -108,6 +109,7 @@ struct inode_operations reiserfs_file_i getxattr: reiserfs_getxattr, listxattr: reiserfs_listxattr, removexattr: reiserfs_removexattr, + permission: reiserfs_permission, }; diff -ruNp linux-2.4.23/fs/reiserfs/inode.c linux-2.4.23.xattr/fs/reiserfs/inode.c --- linux-2.4.23/fs/reiserfs/inode.c 2003-12-09 14:43:30.458839856 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/inode.c 2003-12-09 14:43:36.649898672 -0500 @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -1701,6 +1703,18 @@ int reiserfs_new_inode (struct reiserfs_ goto out_inserted_sd; } + if (reiserfs_posixacl (inode->i_sb)) { + retval = reiserfs_inherit_default_acl (dir, dentry, inode); + if (retval) { + iput (inode); + err = retval; + reiserfs_check_path(&path_to_key) ; + goto out_inserted_sd; + } + } else if (inode->i_sb->s_flags & MS_POSIXACL) { + reiserfs_warning (inode->i_sb, "ACLs aren't enabled in the fs, but vfs thinks they are!\n"); + } + insert_inode_hash (inode); reiserfs_update_sd(th, inode) ; reiserfs_check_path(&path_to_key) ; @@ -2289,6 +2303,16 @@ int reiserfs_setattr(struct dentry *dent } + if (!error && reiserfs_posixacl (inode->i_sb)) { + if (attr->ia_valid & ATTR_MODE) { + if (!(attr->ia_valid & ATTR_SIZE)) + down (&inode->i_sem); + error = reiserfs_acl_chmod (inode); + if (!(attr->ia_valid & ATTR_SIZE)) + up (&inode->i_sem); + } + } + return error ; } diff -ruNp linux-2.4.23/fs/reiserfs/namei.c linux-2.4.23.xattr/fs/reiserfs/namei.c --- linux-2.4.23/fs/reiserfs/namei.c 2003-12-09 14:43:30.459839704 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/namei.c 2003-12-09 14:43:36.650898520 -0500 @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -539,6 +540,7 @@ static int reiserfs_create (struct inode struct inode * inode; int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 ; struct reiserfs_transaction_handle th ; + int locked; if (!(inode = new_inode(dir->i_sb))) { return -ENOMEM ; @@ -547,6 +549,10 @@ static int reiserfs_create (struct inode if (retval) return retval ; + locked = reiserfs_cache_default_acl (dir); + if (locked) + reiserfs_write_lock_xattrs (dir->i_sb); + journal_begin(&th, dir->i_sb, jbegin_count) ; retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode); if (retval) { @@ -559,6 +565,9 @@ static int reiserfs_create (struct inode retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len, inode, 1/*visible*/); + if (locked) + reiserfs_write_unlock_xattrs (dir->i_sb); + if (retval) { inode->i_nlink--; reiserfs_update_sd (&th, inode); @@ -584,6 +593,7 @@ static int reiserfs_mknod (struct inode struct inode * inode; struct reiserfs_transaction_handle th ; int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; + int locked; if (!(inode = new_inode(dir->i_sb))) { return -ENOMEM ; @@ -592,9 +602,17 @@ static int reiserfs_mknod (struct inode if (retval) return retval ; + locked = reiserfs_cache_default_acl (dir); + if (locked) + reiserfs_write_lock_xattrs (inode->i_sb); + journal_begin(&th, dir->i_sb, jbegin_count) ; retval = reiserfs_new_inode(&th, dir, mode, 0, 0/*i_size*/, dentry, inode); + + if (locked) + reiserfs_write_unlock_xattrs (inode->i_sb); + if (retval) { goto out_failed; } @@ -632,6 +650,7 @@ static int reiserfs_mkdir (struct inode struct inode * inode; struct reiserfs_transaction_handle th ; int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; + int locked; mode = S_IFDIR | mode; if (!(inode = new_inode(dir->i_sb))) { @@ -641,6 +660,10 @@ static int reiserfs_mkdir (struct inode if (retval) return retval ; + locked = reiserfs_cache_default_acl (dir); + if (locked) + reiserfs_write_lock_xattrs (inode->i_sb); + journal_begin(&th, dir->i_sb, jbegin_count) ; /* inc the link count now, so another writer doesn't overflow it while @@ -656,6 +679,9 @@ static int reiserfs_mkdir (struct inode old_format_only (dir->i_sb) ? EMPTY_DIR_SIZE_V1 : EMPTY_DIR_SIZE, dentry, inode) ; + if (locked) + reiserfs_write_unlock_xattrs (inode->i_sb); + if (retval) { dir->i_nlink-- ; goto out_failed ; @@ -903,6 +929,8 @@ static int reiserfs_symlink (struct inod memcpy (name, symname, strlen (symname)); padd_item (name, item_len, strlen (symname)); + /* We would inherit the default ACL here, but symlinks don't get ACLs */ + journal_begin(&th, parent_dir->i_sb, jbegin_count) ; retval = reiserfs_new_inode(&th, parent_dir, mode, name, @@ -1293,6 +1321,7 @@ struct inode_operations reiserfs_dir_ino getxattr: reiserfs_getxattr, listxattr: reiserfs_listxattr, removexattr: reiserfs_removexattr, + permission: reiserfs_permission, }; /* @@ -1307,4 +1336,5 @@ struct inode_operations reiserfs_symlink getxattr: reiserfs_getxattr, listxattr: reiserfs_listxattr, removexattr: reiserfs_removexattr, + permission: reiserfs_permission, }; diff -ruNp linux-2.4.23/fs/reiserfs/super.c linux-2.4.23.xattr/fs/reiserfs/super.c --- linux-2.4.23/fs/reiserfs/super.c 2003-12-09 14:43:30.461839400 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/super.c 2003-12-09 14:43:36.652898216 -0500 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -419,6 +420,22 @@ static void reiserfs_dirty_inode (struct unlock_kernel() ; } +static void reiserfs_clear_inode (struct inode *inode) +{ + struct posix_acl *acl; + + acl = inode->u.reiserfs_i.i_acl_access; + if (acl && !IS_ERR (acl)) + posix_acl_release (acl); + inode->u.reiserfs_i.i_acl_access = NULL; + + acl = inode->u.reiserfs_i.i_acl_default; + if (acl && !IS_ERR (acl)) + posix_acl_release (acl); + inode->u.reiserfs_i.i_acl_default = NULL; +} + + struct super_operations reiserfs_sops = { read_inode: reiserfs_read_inode, @@ -426,6 +443,7 @@ struct super_operations reiserfs_sops = write_inode: reiserfs_write_inode, dirty_inode: reiserfs_dirty_inode, delete_inode: reiserfs_delete_inode, + clear_inode: reiserfs_clear_inode, put_super: reiserfs_put_super, write_super: reiserfs_write_super, write_super_lockfs: reiserfs_write_super_lockfs, @@ -632,6 +650,10 @@ static int reiserfs_parse_options (struc {"user_xattr", 0, 0, 1< @@ -1177,6 +1183,11 @@ reiserfs_xattr_init (struct super_block clear_bit (REISERFS_NO_XATTRS_USER, &(s->u.reiserfs_sb.s_mount_opt)); } + if (test_bit (REISERFS_NO_POSIXACL, &(s->u.reiserfs_sb.s_mount_opt))) { + clear_bit (REISERFS_POSIXACL, &(s->u.reiserfs_sb.s_mount_opt)); + clear_bit (REISERFS_NO_POSIXACL, &(s->u.reiserfs_sb.s_mount_opt)); + } + /* If the user has requested an optional xattrs type (e.g. user/acl), then * enable xattrs. If we're a v3.5 filesystem, this will get caught and * error out. If no optional xattrs are enabled, disable xattrs */ @@ -1189,7 +1200,7 @@ reiserfs_xattr_init (struct super_block /* We need generation numbers to ensure that the oid mapping is correct * v3.5 filesystems don't have them. */ if (old_format_only (s)) { - reiserfs_warning (s, "reiserfs: xattrs not supported on pre v3.6 " + reiserfs_warning (s, "reiserfs: xattrs/ACLs not supported on pre v3.6 " "format filesystem. Failing mount.\n"); err = -EOPNOTSUPP; goto error; @@ -1228,7 +1239,7 @@ reiserfs_xattr_init (struct super_block * created. Not an error -- just no xattrs on the fs. We'll * check again if we go read-write */ if (!(mount_flags & MS_RDONLY)) { - reiserfs_warning (s, "reiserfs: xattrs enabled and couldn't " + reiserfs_warning (s, "reiserfs: xattrs/ACLs enabled and couldn't " "find/create .reiserfs_priv. Failing mount.\n"); err = -EOPNOTSUPP; goto error; @@ -1237,6 +1248,7 @@ reiserfs_xattr_init (struct super_block * we're read-only */ clear_bit (REISERFS_XATTRS, &(s->u.reiserfs_sb.s_mount_opt)); clear_bit (REISERFS_XATTRS_USER, &(s->u.reiserfs_sb.s_mount_opt)); + clear_bit (REISERFS_POSIXACL, &(s->u.reiserfs_sb.s_mount_opt)); } } } @@ -1248,6 +1260,10 @@ error: if (err) { clear_bit (REISERFS_XATTRS, &(s->u.reiserfs_sb.s_mount_opt)); clear_bit (REISERFS_XATTRS_USER, &(s->u.reiserfs_sb.s_mount_opt)); + clear_bit (REISERFS_POSIXACL, &(s->u.reiserfs_sb.s_mount_opt)); } + + s->s_flags = (s->s_flags & ~MS_POSIXACL) | + (reiserfs_posixacl (s) ? MS_POSIXACL : 0); return err; } diff -ruNp linux-2.4.23/fs/reiserfs/xattr_acl.c linux-2.4.23.xattr/fs/reiserfs/xattr_acl.c --- linux-2.4.23/fs/reiserfs/xattr_acl.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/xattr_acl.c 2003-12-09 14:43:36.654897912 -0500 @@ -0,0 +1,653 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +xattr_set_acl(struct inode *inode, int type, const void *value, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!reiserfs_posixacl(inode->i_sb)) + return -EOPNOTSUPP; + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) { + return PTR_ERR(acl); + } else if (acl) { + error = posix_acl_valid(acl); + if (error) + goto release_and_out; + } + } else + acl = NULL; + + error = reiserfs_set_acl (inode, type, acl); + +release_and_out: + posix_acl_release(acl); + return error; +} + + +static int +xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int error; + + if (!reiserfs_posixacl(inode->i_sb)) + return -EOPNOTSUPP; + + acl = reiserfs_get_acl (inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl == NULL) + return -ENODATA; + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return error; +} + + +/* + * Convert from filesystem to in-memory representation. + */ +static struct posix_acl * +posix_acl_from_disk(const void *value, size_t size) +{ + const char *end = (char *)value + size; + int n, count; + struct posix_acl *acl; + + if (!value) + return NULL; + if (size < sizeof(reiserfs_acl_header)) + return ERR_PTR(-EINVAL); + if (((reiserfs_acl_header *)value)->a_version != + cpu_to_le32(REISERFS_ACL_VERSION)) + return ERR_PTR(-EINVAL); + value = (char *)value + sizeof(reiserfs_acl_header); + count = reiserfs_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + acl = posix_acl_alloc(count, GFP_NOFS); + if (!acl) + return ERR_PTR(-ENOMEM); + for (n=0; n < count; n++) { + reiserfs_acl_entry *entry = + (reiserfs_acl_entry *)value; + if ((char *)value + sizeof(reiserfs_acl_entry_short) > end) + goto fail; + acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); + acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + + sizeof(reiserfs_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value = (char *)value + sizeof(reiserfs_acl_entry); + if ((char *)value > end) + goto fail; + acl->a_entries[n].e_id = + le32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +/* + * Convert from in-memory to filesystem representation. + */ +static void * +posix_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + reiserfs_acl_header *ext_acl; + char *e; + int n; + + *size = reiserfs_acl_size(acl->a_count); + ext_acl = (reiserfs_acl_header *)kmalloc(sizeof(reiserfs_acl_header) + + acl->a_count * sizeof(reiserfs_acl_entry), GFP_NOFS); + if (!ext_acl) + return ERR_PTR(-ENOMEM); + ext_acl->a_version = cpu_to_le32(REISERFS_ACL_VERSION); + e = (char *)ext_acl + sizeof(reiserfs_acl_header); + for (n=0; n < acl->a_count; n++) { + reiserfs_acl_entry *entry = (reiserfs_acl_entry *)e; + entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); + entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = + cpu_to_le32(acl->a_entries[n].e_id); + e += sizeof(reiserfs_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(reiserfs_acl_entry_short); + break; + + default: + goto fail; + } + } + return (char *)ext_acl; + +fail: + kfree(ext_acl); + return ERR_PTR(-EINVAL); +} + +/* + * Inode operation get_posix_acl(). + * + * inode->i_sem: down + * BKL held [before 2.5.x] + */ +struct posix_acl * +reiserfs_get_acl(struct inode *inode, int type) +{ + char *name, *value; + struct posix_acl *acl, **p_acl; + size_t size; + int retval; + + switch (type) { + case ACL_TYPE_ACCESS: + name = XATTR_NAME_ACL_ACCESS; + p_acl = &inode->u.reiserfs_i.i_acl_access; + break; + case ACL_TYPE_DEFAULT: + name = XATTR_NAME_ACL_DEFAULT; + p_acl = &inode->u.reiserfs_i.i_acl_default; + break; + default: + return ERR_PTR (-EINVAL); + } + + if (IS_ERR (*p_acl)) { + if (PTR_ERR (*p_acl) == -ENODATA) + return NULL; + } else if (*p_acl != NULL) + return posix_acl_dup (*p_acl); + + size = reiserfs_xattr_get (inode, name, NULL, 0); + if ((int)size < 0) { + if (size == -ENODATA || size == -ENOSYS) { + *p_acl = ERR_PTR (-ENODATA); + return NULL; + } + return ERR_PTR (size); + } + + value = kmalloc (size, GFP_NOFS); + if (!value) + return ERR_PTR (-ENOMEM); + + retval = reiserfs_xattr_get(inode, name, value, size); + if (retval == -ENODATA || retval == -ENOSYS) { + /* This shouldn't actually happen as it should have + been caught above.. but just in case */ + acl = NULL; + *p_acl = ERR_PTR (-ENODATA); + } else if (retval < 0) { + acl = ERR_PTR(retval); + } else { + acl = posix_acl_from_disk(value, retval); + *p_acl = posix_acl_dup (acl); + } + + kfree(value); + return acl; +} + +/* + * Inode operation set_posix_acl(). + * + * inode->i_sem: down + * BKL held [before 2.5.x] + */ +int +reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + char *name; + void *value = NULL; + struct posix_acl **p_acl; + size_t size; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + + switch (type) { + case ACL_TYPE_ACCESS: + name = XATTR_NAME_ACL_ACCESS; + p_acl = &inode->u.reiserfs_i.i_acl_access; + if (acl) { + mode_t mode = inode->i_mode; + error = posix_acl_equiv_mode (acl, &mode); + if (error < 0) + return error; + else { + inode->i_mode = mode; + if (error == 0) + acl = NULL; + } + } + break; + case ACL_TYPE_DEFAULT: + name = XATTR_NAME_ACL_DEFAULT; + p_acl = &inode->u.reiserfs_i.i_acl_default; + if (!S_ISDIR (inode->i_mode)) + return acl ? -EACCES : 0; + break; + default: + return -EINVAL; + } + + if (acl) { + value = posix_acl_to_disk(acl, &size); + if (IS_ERR(value)) + return (int)PTR_ERR(value); + error = reiserfs_xattr_set(inode, name, value, size, 0); + } else { + error = reiserfs_xattr_del (inode, name); + if (error == -ENODATA) + error = 0; + } + + if (value) + kfree(value); + + if (!error) { + /* Release the old one */ + if (!IS_ERR (*p_acl) && *p_acl) + posix_acl_release (*p_acl); + + if (acl == NULL) + *p_acl = ERR_PTR (-ENODATA); + else + *p_acl = posix_acl_dup (acl); + } + + return error; +} + +/* dir->i_sem: down, + * inode is new and not released into the wild yet */ +int +reiserfs_inherit_default_acl (struct inode *dir, struct dentry *dentry, struct inode *inode) +{ + struct posix_acl *acl; + int err = 0; + + /* ACLs only get applied to files and directories */ + if (S_ISLNK (inode->i_mode)) + return 0; + + /* ACLs can only be used on "new" objects, so if it's an old object + * there is nothing to inherit from */ + if (get_inode_sd_version (dir) == STAT_DATA_V1) + goto apply_umask; + + /* Don't apply ACLs to objects in the .reiserfs_priv tree.. This + * would be useless since permissions are ignored, and a pain because + * it introduces locking cycles */ + if (is_reiserfs_priv_object (dir)) { + inode->u.reiserfs_i.i_flags |= i_priv_object; + goto apply_umask; + } + + acl = reiserfs_get_acl (dir, ACL_TYPE_DEFAULT); + if (IS_ERR (acl)) { + if (PTR_ERR (acl) == -ENODATA) + goto apply_umask; + return PTR_ERR (acl); + } + + if (acl) { + struct posix_acl *acl_copy; + mode_t mode = inode->i_mode; + int need_acl; + + /* Copy the default ACL to the default ACL of a new directory */ + if (S_ISDIR (inode->i_mode)) { + err = reiserfs_set_acl (inode, ACL_TYPE_DEFAULT, acl); + if (err) + goto cleanup; + } + + /* Now we reconcile the new ACL and the mode, + potentially modifying both */ + acl_copy = posix_acl_clone (acl, GFP_NOFS); + if (!acl_copy) { + err = -ENOMEM; + goto cleanup; + } + + + need_acl = posix_acl_create_masq (acl_copy, &mode); + if (need_acl >= 0) { + if (mode != inode->i_mode) { + inode->i_mode = mode; + } + + /* If we need an ACL.. */ + if (need_acl > 0) { + err = reiserfs_set_acl (inode, ACL_TYPE_ACCESS, acl_copy); + if (err) + goto cleanup_copy; + } + } +cleanup_copy: + posix_acl_release (acl_copy); +cleanup: + posix_acl_release (acl); + } else { +apply_umask: + /* no ACL, apply umask */ + inode->i_mode &= ~current->fs->umask; + } + + return err; +} + +/* Looks up and caches the result of the default ACL. + * We do this so that we don't need to carry the xattr_sem into + * reiserfs_new_inode if we don't need to */ +int +reiserfs_cache_default_acl (struct inode *inode) +{ + int ret = 0; + if (reiserfs_posixacl (inode->i_sb) && + !is_reiserfs_priv_object (inode)) { + struct posix_acl *acl; + reiserfs_read_lock_xattrs (inode->i_sb); + acl = reiserfs_get_acl (inode, ACL_TYPE_DEFAULT); + reiserfs_read_unlock_xattrs (inode->i_sb); + ret = acl ? 1 : 0; + posix_acl_release (acl); + } + + return ret; +} + +static int +__reiserfs_permission (struct inode *inode, int mask, int need_lock) +{ + int mode = inode->i_mode; + + /* Nobody gets write access to a read-only fs */ + if ((mask & MAY_WRITE) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; + + /* Nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + + /* We don't do permission checks on the internal objects. + * Permissions are determined by the "owning" object. */ + if (is_reiserfs_priv_object (inode)) + return 0; + + if (current->fsuid == inode->i_uid) { + mode >>= 6; + } else if (reiserfs_posixacl(inode->i_sb) && + get_inode_sd_version (inode) != STAT_DATA_V1) { + struct posix_acl *acl; + + /* ACL can't contain additional permissions if + the ACL_MASK entry is 0 */ + if (!(mode & S_IRWXG)) + goto check_groups; + + if (need_lock) + reiserfs_read_lock_xattrs (inode->i_sb); + acl = reiserfs_get_acl (inode, ACL_TYPE_ACCESS); + if (need_lock) + reiserfs_read_unlock_xattrs (inode->i_sb); + if (IS_ERR (acl)) { + if (PTR_ERR (acl) == -ENODATA) + goto check_groups; + return PTR_ERR (acl); + } + + if (acl) { + int err = posix_acl_permission (inode, acl, mask); + posix_acl_release (acl); + if (err == -EACCES) { + goto check_capabilities; + } + return err; + } else { + goto check_groups; + } + + } else { +check_groups: + if (in_group_p(inode->i_gid)) + mode >>= 3; + } + if ((mode & mask & S_IRWXO) == mask) + return 0; + +check_capabilities: + /* Allowed to override Discretionary Access Control? */ + if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ + if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || + (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) + return 0; + return -EACCES; +} + +int +reiserfs_permission (struct inode *inode, int mask) +{ + return __reiserfs_permission (inode, mask, 1); +} + +int +reiserfs_permission_locked (struct inode *inode, int mask) +{ + return __reiserfs_permission (inode, mask, 0); +} + +int +reiserfs_acl_chmod (struct inode *inode) +{ + struct posix_acl *acl, *clone; + int error; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + if (get_inode_sd_version (inode) == STAT_DATA_V1 || + !reiserfs_posixacl(inode->i_sb)) + { + return 0; + } + + reiserfs_read_lock_xattrs (inode->i_sb); + acl = reiserfs_get_acl(inode, ACL_TYPE_ACCESS); + reiserfs_read_unlock_xattrs (inode->i_sb); + if (!acl) + return 0; + if (IS_ERR(acl)) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_NOFS); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + error = posix_acl_chmod_masq(clone, inode->i_mode); + if (!error) { + reiserfs_write_lock_xattrs (inode->i_sb); + error = reiserfs_set_acl(inode, ACL_TYPE_ACCESS, clone); + reiserfs_write_unlock_xattrs (inode->i_sb); + } + posix_acl_release(clone); + return error; +} + +static int +posix_acl_access_get(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1) + return -EINVAL; + return xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int +posix_acl_access_set(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1) + return -EINVAL; + return xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size); +} + +static int +posix_acl_access_del (struct inode *inode, const char *name) +{ + struct posix_acl **acl = &inode->u.reiserfs_i.i_acl_access; + if (strlen(name) != sizeof(XATTR_NAME_ACL_ACCESS)-1) + return -EINVAL; + if (!IS_ERR (*acl) && *acl) { + posix_acl_release (*acl); + *acl = ERR_PTR (-ENODATA); + } + + return 0; +} + +static int +posix_acl_access_list (struct inode *inode, const char *name, int namelen, char *out) +{ + int len = namelen; + if (!reiserfs_posixacl (inode->i_sb)) + return 0; + if (out) + memcpy (out, name, len); + + return len; +} + +struct reiserfs_xattr_handler posix_acl_access_handler = { + prefix: XATTR_NAME_ACL_ACCESS, + get: posix_acl_access_get, + set: posix_acl_access_set, + del: posix_acl_access_del, + list: posix_acl_access_list, +}; + +static int +posix_acl_default_get (struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1) + return -EINVAL; + return xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int +posix_acl_default_set(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1) + return -EINVAL; + return xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size); +} + +static int +posix_acl_default_del (struct inode *inode, const char *name) +{ + struct posix_acl **acl = &inode->u.reiserfs_i.i_acl_default; + if (strlen(name) != sizeof(XATTR_NAME_ACL_DEFAULT)-1) + return -EINVAL; + if (!IS_ERR (*acl) && *acl) { + posix_acl_release (*acl); + *acl = ERR_PTR (-ENODATA); + } + + return 0; +} + +static int +posix_acl_default_list (struct inode *inode, const char *name, int namelen, char *out) +{ + int len = namelen; + if (!reiserfs_posixacl (inode->i_sb)) + return 0; + if (out) + memcpy (out, name, len); + + return len; +} + +struct reiserfs_xattr_handler posix_acl_default_handler = { + prefix: XATTR_NAME_ACL_DEFAULT, + get: posix_acl_default_get, + set: posix_acl_default_set, + del: posix_acl_default_del, + list: posix_acl_default_list, +}; + +int __init +reiserfs_xattr_posix_acl_init (void) +{ + int err; + err = reiserfs_xattr_register_handler (&posix_acl_access_handler); + if (!err) + err = reiserfs_xattr_register_handler (&posix_acl_default_handler); + return err; +} + +int +reiserfs_xattr_posix_acl_exit (void) +{ + int err; + err = reiserfs_xattr_unregister_handler (&posix_acl_access_handler); + if (!err) + err = reiserfs_xattr_unregister_handler (&posix_acl_default_handler); + return err; +} diff -ruNp linux-2.4.23/fs/reiserfs/xattr_user.c linux-2.4.23.xattr/fs/reiserfs/xattr_user.c --- linux-2.4.23/fs/reiserfs/xattr_user.c 2003-12-09 14:43:30.464838944 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/xattr_user.c 2003-12-09 14:43:36.654897912 -0500 @@ -6,6 +6,10 @@ #include #include +#ifdef CONFIG_REISERFS_FS_POSIX_ACL +# include +#endif + #define XATTR_USER_PREFIX "user." static int @@ -20,7 +24,7 @@ user_get (struct inode *inode, const cha if (!reiserfs_xattrs_user (inode->i_sb)) return -EOPNOTSUPP; - error = permission (inode, MAY_READ); + error = reiserfs_permission_locked (inode, MAY_READ); if (error) return error; @@ -44,7 +48,7 @@ user_set (struct inode *inode, const cha (!S_ISDIR (inode->i_mode) || inode->i_mode & S_ISVTX)) return -EPERM; - error = permission (inode, MAY_WRITE); + error = reiserfs_permission_locked (inode, MAY_WRITE); if (error) return error; @@ -66,7 +70,7 @@ user_del (struct inode *inode, const cha (!S_ISDIR (inode->i_mode) || inode->i_mode & S_ISVTX)) return -EPERM; - error = permission (inode, MAY_WRITE); + error = reiserfs_permission_locked (inode, MAY_WRITE); if (error) return error; diff -ruNp linux-2.4.23/include/linux/reiserfs_acl.h linux-2.4.23.xattr/include/linux/reiserfs_acl.h --- linux-2.4.23/include/linux/reiserfs_acl.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.23.xattr/include/linux/reiserfs_acl.h 2003-12-09 14:43:36.655897760 -0500 @@ -0,0 +1,92 @@ +#include +#include +#include + +#define REISERFS_ACL_VERSION 0x0001 + +typedef struct { + __u16 e_tag; + __u16 e_perm; + __u32 e_id; +} reiserfs_acl_entry; + +typedef struct { + __u16 e_tag; + __u16 e_perm; +} reiserfs_acl_entry_short; + +typedef struct { + __u32 a_version; +} reiserfs_acl_header; + +static inline size_t reiserfs_acl_size(int count) +{ + if (count <= 4) { + return sizeof(reiserfs_acl_header) + + count * sizeof(reiserfs_acl_entry_short); + } else { + return sizeof(reiserfs_acl_header) + + 4 * sizeof(reiserfs_acl_entry_short) + + (count - 4) * sizeof(reiserfs_acl_entry); + } +} + +static inline int reiserfs_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(reiserfs_acl_header); + s = size - 4 * sizeof(reiserfs_acl_entry_short); + if (s < 0) { + if (size % sizeof(reiserfs_acl_entry_short)) + return -1; + return size / sizeof(reiserfs_acl_entry_short); + } else { + if (s % sizeof(reiserfs_acl_entry)) + return -1; + return s / sizeof(reiserfs_acl_entry) + 4; + } +} + + +#ifdef CONFIG_REISERFS_FS_POSIX_ACL +struct posix_acl * reiserfs_get_acl(struct inode *inode, int type); +int reiserfs_set_acl(struct inode *inode, int type, struct posix_acl *acl); +int reiserfs_permission (struct inode *inode, int mask); +int reiserfs_permission_locked (struct inode *inode, int mask); +int reiserfs_acl_chmod (struct inode *inode); +int reiserfs_inherit_default_acl (struct inode *dir, struct dentry *dentry, struct inode *inode); +int reiserfs_cache_default_acl (struct inode *dir); +extern int reiserfs_xattr_posix_acl_init (void) __init; +extern int reiserfs_xattr_posix_acl_exit (void); +#else + +#define reiserfs_permission NULL +#define reiserfs_set_acl NULL +#define reiserfs_get_acl NULL +#define reiserfs_cache_default_acl(inode) 0 + +static inline int +reiserfs_xattr_posix_acl_init (void) +{ + return 0; +} + +static inline int +reiserfs_xattr_posix_acl_exit (void) +{ + return 0; +} + +static inline int +reiserfs_acl_chmod (struct inode *inode) +{ + return 0; +} + +static inline int +reiserfs_inherit_default_acl (const struct inode *dir, struct dentry *dentry, struct inode *inode) +{ + return 0; +} + +#endif diff -ruNp linux-2.4.23/include/linux/reiserfs_fs_i.h linux-2.4.23.xattr/include/linux/reiserfs_fs_i.h --- linux-2.4.23/include/linux/reiserfs_fs_i.h 2003-12-09 14:43:30.466838640 -0500 +++ linux-2.4.23.xattr/include/linux/reiserfs_fs_i.h 2003-12-09 14:43:36.655897760 -0500 @@ -61,6 +61,9 @@ struct reiserfs_inode_info { */ unsigned long i_tail_trans_id; unsigned long i_tail_trans_index; + + struct posix_acl *i_acl_access; + struct posix_acl *i_acl_default; }; #endif diff -ruNp linux-2.4.23/include/linux/reiserfs_fs_sb.h linux-2.4.23.xattr/include/linux/reiserfs_fs_sb.h --- linux-2.4.23/include/linux/reiserfs_fs_sb.h 2003-12-09 14:43:30.467838488 -0500 +++ linux-2.4.23.xattr/include/linux/reiserfs_fs_sb.h 2003-12-09 14:43:36.656897608 -0500 @@ -470,6 +470,8 @@ struct reiserfs_sb_info #define REISERFS_XATTRS 16 #define REISERFS_XATTRS_USER 18 #define REISERFS_NO_XATTRS_USER 19 +#define REISERFS_POSIXACL 20 +#define REISERFS_NO_POSIXACL 21 #define reiserfs_r5_hash(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << FORCE_R5_HASH)) @@ -490,7 +492,8 @@ struct reiserfs_sb_info #define convert_reiserfs(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_CONVERT)) #define reiserfs_xattrs(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_XATTRS)) #define reiserfs_xattrs_user(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_XATTRS_USER)) -#define reiserfs_xattrs_optional(s) reiserfs_xattrs_user(s) +#define reiserfs_xattrs_optional(s) (reiserfs_xattrs_user(s) || reiserfs_posixacl(s)) +#define reiserfs_posixacl(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_POSIXACL)) void reiserfs_file_buffer (struct buffer_head * bh, int list); diff -ruNp linux-2.4.23/include/linux/reiserfs_xattr.h linux-2.4.23.xattr/include/linux/reiserfs_xattr.h --- linux-2.4.23/include/linux/reiserfs_xattr.h 2003-12-09 14:43:30.467838488 -0500 +++ linux-2.4.23.xattr/include/linux/reiserfs_xattr.h 2003-12-09 14:43:36.656897608 -0500 @@ -73,6 +73,7 @@ reiserfs_read_unlock_xattrs(struct super static inline int reiserfs_xattr_init (struct super_block *s, int mount_flags) { + s->s_flags = (s->s_flags & ~MS_POSIXACL); /* to be sure */ return 0; }