Commit 651d4694 authored by Theodore Y. Ts'o's avatar Theodore Y. Ts'o Committed by Linus Torvalds

Port of (bugfixed) 0.8.50 acl-ext3 to 2.5.

This patch adds ACL support to the ext3 filesystem.
parent dcdc0bd8
...@@ -300,6 +300,20 @@ config EXT3_FS_XATTR ...@@ -300,6 +300,20 @@ config EXT3_FS_XATTR
If unsure, say N. If unsure, say N.
You need this for POSIX ACL support on ext3.
config EXT3_FS_POSIX_ACL
bool "Ext3 POSIX Access Control Lists"
depends on EXT3_FS_XATTR
---help---
Posix Access Control Lists (ACLs) support permissions for users and
groups beyond the owner/group/world scheme.
To learn more about Access Control Lists, visit the Posix ACLs for
Linux website <http://acl.bestbits.at/>.
If you don't know what Access Control Lists are, say N
# CONFIG_JBD could be its own option (even modular), but until there are # CONFIG_JBD could be its own option (even modular), but until there are
# other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS # other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS
# dep_tristate ' Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS # dep_tristate ' Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS
......
...@@ -13,4 +13,8 @@ ifeq ($(CONFIG_EXT3_FS_XATTR),y) ...@@ -13,4 +13,8 @@ ifeq ($(CONFIG_EXT3_FS_XATTR),y)
ext3-objs += xattr.o xattr_user.o ext3-objs += xattr.o xattr_user.o
endif endif
ifeq ($(CONFIG_EXT3_FS_POSIX_ACL),y)
ext3-objs += acl.o
endif
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/*
* linux/fs/ext3/acl.c
*
* Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/ext3_jbd.h>
#include <linux/ext3_fs.h>
#include "xattr.h"
#include "acl.h"
/*
* Convert from filesystem to in-memory representation.
*/
static struct posix_acl *
ext3_acl_from_disk(const void *value, size_t size)
{
const char *end = (char *)value + size;
int n, count;
struct posix_acl *acl;
if (!value)
return NULL;
if (size < sizeof(ext3_acl_header))
return ERR_PTR(-EINVAL);
if (((ext3_acl_header *)value)->a_version !=
cpu_to_le32(EXT3_ACL_VERSION))
return ERR_PTR(-EINVAL);
value = (char *)value + sizeof(ext3_acl_header);
count = ext3_acl_count(size);
if (count < 0)
return ERR_PTR(-EINVAL);
if (count == 0)
return NULL;
acl = posix_acl_alloc(count, GFP_KERNEL);
if (!acl)
return ERR_PTR(-ENOMEM);
for (n=0; n < count; n++) {
ext3_acl_entry *entry =
(ext3_acl_entry *)value;
if ((char *)value + sizeof(ext3_acl_entry_short) > end)
goto fail;
acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
switch(acl->a_entries[n].e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
value = (char *)value +
sizeof(ext3_acl_entry_short);
acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
break;
case ACL_USER:
case ACL_GROUP:
value = (char *)value + sizeof(ext3_acl_entry);
if ((char *)value > end)
goto fail;
acl->a_entries[n].e_id =
le32_to_cpu(entry->e_id);
break;
default:
goto fail;
}
}
if (value != end)
goto fail;
return acl;
fail:
posix_acl_release(acl);
return ERR_PTR(-EINVAL);
}
/*
* Convert from in-memory to filesystem representation.
*/
static void *
ext3_acl_to_disk(const struct posix_acl *acl, size_t *size)
{
ext3_acl_header *ext_acl;
char *e;
int n;
*size = ext3_acl_size(acl->a_count);
ext_acl = (ext3_acl_header *)kmalloc(sizeof(ext3_acl_header) +
acl->a_count * sizeof(ext3_acl_entry), GFP_KERNEL);
if (!ext_acl)
return ERR_PTR(-ENOMEM);
ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION);
e = (char *)ext_acl + sizeof(ext3_acl_header);
for (n=0; n < acl->a_count; n++) {
ext3_acl_entry *entry = (ext3_acl_entry *)e;
entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
switch(acl->a_entries[n].e_tag) {
case ACL_USER:
case ACL_GROUP:
entry->e_id =
cpu_to_le32(acl->a_entries[n].e_id);
e += sizeof(ext3_acl_entry);
break;
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
e += sizeof(ext3_acl_entry_short);
break;
default:
goto fail;
}
}
return (char *)ext_acl;
fail:
kfree(ext_acl);
return ERR_PTR(-EINVAL);
}
/*
* Inode operation get_posix_acl().
*
* inode->i_sem: down
*/
static struct posix_acl *
ext3_get_acl(struct inode *inode, int type)
{
int name_index;
char *value;
struct posix_acl *acl, **p_acl;
const size_t size = ext3_acl_size(EXT3_ACL_MAX_ENTRIES);
int retval;
if (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
switch(type) {
case ACL_TYPE_ACCESS:
p_acl = &EXT3_I(inode)->i_acl;
name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
p_acl = &EXT3_I(inode)->i_default_acl;
name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;
break;
default:
return ERR_PTR(-EINVAL);
}
if (*p_acl != EXT3_ACL_NOT_CACHED)
return posix_acl_dup(*p_acl);
value = kmalloc(size, GFP_KERNEL);
if (!value)
return ERR_PTR(-ENOMEM);
retval = ext3_xattr_get(inode, name_index, "", value, size);
if (retval == -ENODATA || retval == -ENOSYS)
*p_acl = acl = NULL;
else if (retval < 0)
acl = ERR_PTR(retval);
else {
acl = ext3_acl_from_disk(value, retval);
if (!IS_ERR(acl))
*p_acl = posix_acl_dup(acl);
}
kfree(value);
return acl;
}
/*
* Set the access or default ACL of an inode.
*
* inode->i_sem: down unless called from ext3_new_inode
*/
static int
ext3_set_acl(handle_t *handle, struct inode *inode, int type,
struct posix_acl *acl)
{
int name_index;
void *value = NULL;
struct posix_acl **p_acl;
size_t size;
int error;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
switch(type) {
case ACL_TYPE_ACCESS:
name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
p_acl = &EXT3_I(inode)->i_acl;
if (acl) {
mode_t mode = inode->i_mode;
error = posix_acl_equiv_mode(acl, &mode);
if (error < 0)
return error;
else {
inode->i_mode = mode;
ext3_mark_inode_dirty(handle, inode);
if (error == 0)
acl = NULL;
}
}
break;
case ACL_TYPE_DEFAULT:
name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;
p_acl = &EXT3_I(inode)->i_default_acl;
if (!S_ISDIR(inode->i_mode))
return acl ? -EACCES : 0;
break;
default:
return -EINVAL;
}
if (acl) {
if (acl->a_count > EXT3_ACL_MAX_ENTRIES)
return -EINVAL;
value = ext3_acl_to_disk(acl, &size);
if (IS_ERR(value))
return (int)PTR_ERR(value);
}
error = ext3_xattr_set(handle, inode, name_index, "", value, size, 0);
if (value)
kfree(value);
if (!error) {
if (*p_acl && *p_acl != EXT3_ACL_NOT_CACHED)
posix_acl_release(*p_acl);
*p_acl = posix_acl_dup(acl);
}
return error;
}
static int
__ext3_permission(struct inode *inode, int mask, int lock)
{
int mode = inode->i_mode;
/* Nobody gets write access to a read-only fs */
if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/* Nobody gets write access to an immutable file */
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
return -EACCES;
if (current->fsuid == inode->i_uid) {
mode >>= 6;
} else if (test_opt(inode->i_sb, POSIX_ACL)) {
/* ACL can't contain additional permissions if
the ACL_MASK entry is 0 */
if (!(mode & S_IRWXG))
goto check_groups;
if (EXT3_I(inode)->i_acl == EXT3_ACL_NOT_CACHED) {
struct posix_acl *acl;
if (lock) {
down(&inode->i_sem);
acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
up(&inode->i_sem);
} else
acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
return PTR_ERR(acl);
posix_acl_release(acl);
if (EXT3_I(inode)->i_acl == EXT3_ACL_NOT_CACHED)
return -EIO;
}
if (EXT3_I(inode)->i_acl) {
int error = posix_acl_permission(inode,
EXT3_I(inode)->i_acl, mask);
if (error == -EACCES)
goto check_capabilities;
return error;
} else
goto check_groups;
} else {
check_groups:
if (in_group_p(inode->i_gid))
mode >>= 3;
}
if ((mode & mask & S_IRWXO) == mask)
return 0;
check_capabilities:
/* Allowed to override Discretionary Access Control? */
if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
if (capable(CAP_DAC_OVERRIDE))
return 0;
/* Read and search granted if capable(CAP_DAC_READ_SEARCH) */
if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) ||
(S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))))
return 0;
return -EACCES;
}
/*
* Inode operation permission().
*
* inode->i_sem: up
*/
int
ext3_permission(struct inode *inode, int mask)
{
return __ext3_permission(inode, mask, 1);
}
/*
* Used internally if i_sem is already down.
*/
int
ext3_permission_locked(struct inode *inode, int mask)
{
return __ext3_permission(inode, mask, 0);
}
/*
* Initialize the ACLs of a new inode. Called from ext3_new_inode.
*
* dir->i_sem: down
* inode->i_sem: up (access to inode is still exclusive)
*/
int
ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
{
struct posix_acl *acl = NULL;
int error = 0;
if (!S_ISLNK(inode->i_mode)) {
if (test_opt(dir->i_sb, POSIX_ACL)) {
acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
}
if (!acl)
inode->i_mode &= ~current->fs->umask;
}
if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
struct posix_acl *clone;
mode_t mode;
if (S_ISDIR(inode->i_mode)) {
error = ext3_set_acl(handle, inode,
ACL_TYPE_DEFAULT, acl);
if (error)
goto cleanup;
}
clone = posix_acl_clone(acl, GFP_KERNEL);
error = -ENOMEM;
if (!clone)
goto cleanup;
mode = inode->i_mode;
error = posix_acl_create_masq(clone, &mode);
if (error >= 0) {
inode->i_mode = mode;
if (error > 0) {
/* This is an extended ACL */
error = ext3_set_acl(handle, 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
*/
int
ext3_acl_chmod(struct inode *inode)
{
struct posix_acl *acl, *clone;
int error;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
if (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl)
return PTR_ERR(acl);
clone = posix_acl_clone(acl, GFP_KERNEL);
posix_acl_release(acl);
if (!clone)
return -ENOMEM;
error = posix_acl_chmod_masq(clone, inode->i_mode);
if (!error) {
handle_t *handle;
handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS);
if (IS_ERR(handle)) {
ext3_std_error(inode->i_sb, error);
return PTR_ERR(handle);
}
error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, clone);
ext3_journal_stop(handle, inode);
}
posix_acl_release(clone);
return error;
}
/*
* Extended attribute handlers
*/
static size_t
ext3_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 (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
if (list)
memcpy(list, XATTR_NAME_ACL_ACCESS, len);
return len;
}
static size_t
ext3_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 (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
if (list)
memcpy(list, XATTR_NAME_ACL_DEFAULT, len);
return len;
}
static int
ext3_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
{
struct posix_acl *acl;
int error;
if (!test_opt(inode->i_sb, POSIX_ACL))
return -EOPNOTSUPP;
acl = ext3_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;
}
static int
ext3_xattr_get_acl_access(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext3_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
}
static int
ext3_xattr_get_acl_default(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext3_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
}
static int
ext3_xattr_set_acl(struct inode *inode, int type, const void *value, size_t size)
{
handle_t *handle;
struct posix_acl *acl;
int error;
if (!test_opt(inode->i_sb, POSIX_ACL))
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;
handle = ext3_journal_start(inode, EXT3_XATTR_TRANS_BLOCKS);
if (IS_ERR(handle))
return PTR_ERR(handle);
error = ext3_set_acl(handle, inode, type, acl);
ext3_journal_stop(handle, inode);
release_and_out:
posix_acl_release(acl);
return error;
}
static int
ext3_xattr_set_acl_access(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext3_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
}
static int
ext3_xattr_set_acl_default(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext3_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
}
struct ext3_xattr_handler ext3_xattr_acl_access_handler = {
prefix: XATTR_NAME_ACL_ACCESS,
list: ext3_xattr_list_acl_access,
get: ext3_xattr_get_acl_access,
set: ext3_xattr_set_acl_access,
};
struct ext3_xattr_handler ext3_xattr_acl_default_handler = {
prefix: XATTR_NAME_ACL_DEFAULT,
list: ext3_xattr_list_acl_default,
get: ext3_xattr_get_acl_default,
set: ext3_xattr_set_acl_default,
};
void
exit_ext3_acl(void)
{
ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS,
&ext3_xattr_acl_access_handler);
ext3_xattr_unregister(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT,
&ext3_xattr_acl_default_handler);
}
int __init
init_ext3_acl(void)
{
int error;
error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_ACCESS,
&ext3_xattr_acl_access_handler);
if (error)
goto fail;
error = ext3_xattr_register(EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT,
&ext3_xattr_acl_default_handler);
if (error)
goto fail;
return 0;
fail:
exit_ext3_acl();
return error;
}
/*
File: fs/ext3/acl.h
(C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/xattr_acl.h>
#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 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 int ext3_permission_locked (struct inode *, int);
extern int ext3_acl_chmod (struct inode *);
extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
extern int init_ext3_acl(void);
extern void exit_ext3_acl(void);
#else /* CONFIG_EXT3_FS_POSIX_ACL */
#include <linux/sched.h>
#define ext3_permission NULL
static inline int
ext3_acl_chmod(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;
}
#endif /* CONFIG_EXT3_FS_POSIX_ACL */
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/ext3_fs.h> #include <linux/ext3_fs.h>
#include <linux/ext3_jbd.h> #include <linux/ext3_jbd.h>
#include "xattr.h" #include "xattr.h"
#include "acl.h"
/* /*
* Called when an inode is released. Note that this is different * Called when an inode is released. Note that this is different
...@@ -102,5 +103,6 @@ struct inode_operations ext3_file_inode_operations = { ...@@ -102,5 +103,6 @@ struct inode_operations ext3_file_inode_operations = {
.getxattr = ext3_getxattr, .getxattr = ext3_getxattr,
.listxattr = ext3_listxattr, .listxattr = ext3_listxattr,
.removexattr = ext3_removexattr, .removexattr = ext3_removexattr,
.permission = ext3_permission,
}; };
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include "xattr.h" #include "xattr.h"
#include "acl.h"
/* /*
* ialloc.c contains the inodes allocation and deallocation routines * ialloc.c contains the inodes allocation and deallocation routines
...@@ -423,20 +424,27 @@ struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, int mode) ...@@ -423,20 +424,27 @@ struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, int mode)
inode->i_generation = EXT3_SB(sb)->s_next_generation++; inode->i_generation = EXT3_SB(sb)->s_next_generation++;
ei->i_state = EXT3_STATE_NEW; ei->i_state = EXT3_STATE_NEW;
err = ext3_mark_inode_dirty(handle, inode);
if (err) goto fail;
unlock_super(sb); unlock_super(sb);
ret = inode; ret = inode;
if(DQUOT_ALLOC_INODE(inode)) { if(DQUOT_ALLOC_INODE(inode)) {
DQUOT_DROP(inode); DQUOT_DROP(inode);
inode->i_flags |= S_NOQUOTA; err = -EDQUOT;
inode->i_nlink = 0; goto fail2;
iput(inode);
ret = ERR_PTR(-EDQUOT);
} else {
ext3_debug("allocating inode %lu\n", inode->i_ino);
} }
err = ext3_init_acl(handle, inode, dir);
if (err) {
DQUOT_FREE_INODE(inode);
goto fail2;
}
err = ext3_mark_inode_dirty(handle, inode);
if (err) {
ext3_std_error(sb, err);
DQUOT_FREE_INODE(inode);
goto fail2;
}
ext3_debug("allocating inode %lu\n", inode->i_ino);
goto really_out; goto really_out;
fail: fail:
ext3_std_error(sb, err); ext3_std_error(sb, err);
...@@ -447,6 +455,12 @@ struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, int mode) ...@@ -447,6 +455,12 @@ struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, int mode)
really_out: really_out:
brelse(bitmap_bh); brelse(bitmap_bh);
return ret; return ret;
fail2:
inode->i_flags |= S_NOQUOTA;
inode->i_nlink = 0;
iput(inode);
return ERR_PTR(err);
} }
/* Verify that we are loading a valid orphan from disk */ /* Verify that we are loading a valid orphan from disk */
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/mpage.h> #include <linux/mpage.h>
#include "xattr.h"
#include "acl.h"
/* /*
* SEARCH_FROM_ZERO forces each block allocation to search from the start * SEARCH_FROM_ZERO forces each block allocation to search from the start
...@@ -2199,7 +2201,11 @@ void ext3_read_inode(struct inode * inode) ...@@ -2199,7 +2201,11 @@ void ext3_read_inode(struct inode * inode)
struct buffer_head *bh; struct buffer_head *bh;
int block; int block;
if(ext3_get_inode_loc(inode, &iloc)) #ifdef CONFIG_EXT3_FS_POSIX_ACL
ei->i_acl = EXT3_ACL_NOT_CACHED;
ei->i_default_acl = EXT3_ACL_NOT_CACHED;
#endif
if (ext3_get_inode_loc(inode, &iloc))
goto bad_inode; goto bad_inode;
bh = iloc.bh; bh = iloc.bh;
raw_inode = iloc.raw_inode; raw_inode = iloc.raw_inode;
...@@ -2491,13 +2497,8 @@ void ext3_write_inode(struct inode *inode, int wait) ...@@ -2491,13 +2497,8 @@ void ext3_write_inode(struct inode *inode, int wait)
* be freed, so we have a strong guarantee that no future commit will * be freed, so we have a strong guarantee that no future commit will
* leave these blocks visible to the user.) * leave these blocks visible to the user.)
* *
* This is only needed for regular files. rmdir() has its own path, and * Called with inode->sem down.
* we can never truncate a direcory except on final unlink (at which
* point i_nlink is zero so recovery is easy.)
*
* Called with the BKL.
*/ */
int ext3_setattr(struct dentry *dentry, struct iattr *attr) int ext3_setattr(struct dentry *dentry, struct iattr *attr)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -2517,7 +2518,8 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -2517,7 +2518,8 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
lock_kernel(); lock_kernel();
if (attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { if (S_ISREG(inode->i_mode) &&
attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) {
handle_t *handle; handle_t *handle;
handle = ext3_journal_start(inode, 3); handle = ext3_journal_start(inode, 3);
...@@ -2542,6 +2544,9 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -2542,6 +2544,9 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
if (inode->i_nlink) if (inode->i_nlink)
ext3_orphan_del(NULL, inode); ext3_orphan_del(NULL, inode);
if (!rc && (ia_valid & ATTR_MODE))
rc = ext3_acl_chmod(inode);
err_out: err_out:
ext3_std_error(inode->i_sb, error); ext3_std_error(inode->i_sb, error);
unlock_kernel(); unlock_kernel();
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include "xattr.h" #include "xattr.h"
#include "acl.h"
/* /*
* define how far ahead to read directories while searching them. * define how far ahead to read directories while searching them.
...@@ -1624,7 +1624,10 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry, ...@@ -1624,7 +1624,10 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry,
inode = ext3_new_inode (handle, dir, mode); inode = ext3_new_inode (handle, dir, mode);
err = PTR_ERR(inode); err = PTR_ERR(inode);
if (!IS_ERR(inode)) { if (!IS_ERR(inode)) {
init_special_inode(inode, mode, rdev); init_special_inode(inode, inode->i_mode, rdev);
#ifdef CONFIG_EXT3_FS_XATTR
inode->i_op = &ext3_special_inode_operations;
#endif
err = ext3_add_nondir(handle, dentry, inode); err = ext3_add_nondir(handle, dentry, inode);
ext3_mark_inode_dirty(handle, inode); ext3_mark_inode_dirty(handle, inode);
} }
...@@ -2281,17 +2284,21 @@ struct inode_operations ext3_dir_inode_operations = { ...@@ -2281,17 +2284,21 @@ struct inode_operations ext3_dir_inode_operations = {
.rmdir = ext3_rmdir, .rmdir = ext3_rmdir,
.mknod = ext3_mknod, .mknod = ext3_mknod,
.rename = ext3_rename, .rename = ext3_rename,
.setattr = ext3_setattr,
.setxattr = ext3_setxattr, .setxattr = ext3_setxattr,
.getxattr = ext3_getxattr, .getxattr = ext3_getxattr,
.listxattr = ext3_listxattr, .listxattr = ext3_listxattr,
.removexattr = ext3_removexattr, .removexattr = ext3_removexattr,
.permission = ext3_permission,
}; };
struct inode_operations ext3_special_inode_operations = { struct inode_operations ext3_special_inode_operations = {
.setattr = ext3_setattr,
.setxattr = ext3_setxattr, .setxattr = ext3_setxattr,
.getxattr = ext3_getxattr, .getxattr = ext3_getxattr,
.listxattr = ext3_listxattr, .listxattr = ext3_listxattr,
.removexattr = ext3_removexattr, .removexattr = ext3_removexattr,
.permission = ext3_permission,
}; };
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "xattr.h" #include "xattr.h"
#include "acl.h"
#ifdef CONFIG_JBD_DEBUG #ifdef CONFIG_JBD_DEBUG
static int ext3_ro_after; /* Make fs read-only after this many jiffies */ static int ext3_ro_after; /* Make fs read-only after this many jiffies */
...@@ -428,6 +429,10 @@ static struct inode *ext3_alloc_inode(struct super_block *sb) ...@@ -428,6 +429,10 @@ static struct inode *ext3_alloc_inode(struct super_block *sb)
ei = kmem_cache_alloc(ext3_inode_cachep, SLAB_NOFS); ei = kmem_cache_alloc(ext3_inode_cachep, SLAB_NOFS);
if (!ei) if (!ei)
return NULL; return NULL;
#ifdef CONFIG_EXT3_FS_POSIX_ACL
ei->i_acl = EXT3_ACL_NOT_CACHED;
ei->i_default_acl = EXT3_ACL_NOT_CACHED;
#endif
return &ei->vfs_inode; return &ei->vfs_inode;
} }
...@@ -465,6 +470,26 @@ static void destroy_inodecache(void) ...@@ -465,6 +470,26 @@ static void destroy_inodecache(void)
printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n"); printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n");
} }
#ifdef CONFIG_EXT3_FS_POSIX_ACL
static void ext3_clear_inode(struct inode *inode)
{
if (EXT3_I(inode)->i_acl &&
EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) {
posix_acl_release(EXT3_I(inode)->i_acl);
EXT3_I(inode)->i_acl = EXT3_ACL_NOT_CACHED;
}
if (EXT3_I(inode)->i_default_acl &&
EXT3_I(inode)->i_default_acl != EXT3_ACL_NOT_CACHED) {
posix_acl_release(EXT3_I(inode)->i_default_acl);
EXT3_I(inode)->i_default_acl = EXT3_ACL_NOT_CACHED;
}
}
#else
# define ext3_clear_inode NULL
#endif
static struct super_operations ext3_sops = { static struct super_operations ext3_sops = {
.alloc_inode = ext3_alloc_inode, .alloc_inode = ext3_alloc_inode,
.destroy_inode = ext3_destroy_inode, .destroy_inode = ext3_destroy_inode,
...@@ -479,6 +504,7 @@ static struct super_operations ext3_sops = { ...@@ -479,6 +504,7 @@ static struct super_operations ext3_sops = {
.unlockfs = ext3_unlockfs, /* BKL not held. We take it */ .unlockfs = ext3_unlockfs, /* BKL not held. We take it */
.statfs = ext3_statfs, /* BKL not held. */ .statfs = ext3_statfs, /* BKL not held. */
.remount_fs = ext3_remount, /* BKL held */ .remount_fs = ext3_remount, /* BKL held */
.clear_inode = ext3_clear_inode, /* BKL not needed. */
}; };
struct dentry *ext3_get_parent(struct dentry *child); struct dentry *ext3_get_parent(struct dentry *child);
...@@ -559,6 +585,13 @@ static int parse_options (char * options, struct ext3_sb_info *sbi, ...@@ -559,6 +585,13 @@ static int parse_options (char * options, struct ext3_sb_info *sbi,
else if (!strcmp (this_char, "nouser_xattr")) else if (!strcmp (this_char, "nouser_xattr"))
clear_opt (sbi->s_mount_opt, XATTR_USER); clear_opt (sbi->s_mount_opt, XATTR_USER);
else else
#endif
#ifdef CONFIG_EXT3_FS_POSIX_ACL
if (!strcmp(this_char, "acl"))
set_opt (sbi->s_mount_opt, POSIX_ACL);
else if (!strcmp(this_char, "noacl"))
clear_opt (sbi->s_mount_opt, POSIX_ACL);
else
#endif #endif
if (!strcmp (this_char, "bsddf")) if (!strcmp (this_char, "bsddf"))
clear_opt (sbi->s_mount_opt, MINIX_DF); clear_opt (sbi->s_mount_opt, MINIX_DF);
...@@ -1028,6 +1061,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) ...@@ -1028,6 +1061,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
set_opt(sbi->s_mount_opt, NO_UID32); set_opt(sbi->s_mount_opt, NO_UID32);
if (def_mount_opts & EXT3_DEFM_XATTR_USER) if (def_mount_opts & EXT3_DEFM_XATTR_USER)
set_opt(sbi->s_mount_opt, XATTR_USER); set_opt(sbi->s_mount_opt, XATTR_USER);
if (def_mount_opts & EXT3_DEFM_ACL)
set_opt(sbi->s_mount_opt, POSIX_ACL);
if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_DATA) if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_DATA)
sbi->s_mount_opt |= EXT3_MOUNT_JOURNAL_DATA; sbi->s_mount_opt |= EXT3_MOUNT_JOURNAL_DATA;
else if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_ORDERED) else if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_ORDERED)
...@@ -1046,6 +1081,9 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) ...@@ -1046,6 +1081,9 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
if (!parse_options ((char *) data, sbi, &journal_inum, 0)) if (!parse_options ((char *) data, sbi, &journal_inum, 0))
goto failed_mount; goto failed_mount;
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV && if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV &&
(EXT3_HAS_COMPAT_FEATURE(sb, ~0U) || (EXT3_HAS_COMPAT_FEATURE(sb, ~0U) ||
EXT3_HAS_RO_COMPAT_FEATURE(sb, ~0U) || EXT3_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
...@@ -1735,6 +1773,9 @@ int ext3_remount (struct super_block * sb, int * flags, char * data) ...@@ -1735,6 +1773,9 @@ int ext3_remount (struct super_block * sb, int * flags, char * data)
if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
ext3_abort(sb, __FUNCTION__, "Abort forced by user"); ext3_abort(sb, __FUNCTION__, "Abort forced by user");
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
es = sbi->s_es; es = sbi->s_es;
ext3_init_journal_params(sbi, sbi->s_journal); ext3_init_journal_params(sbi, sbi->s_journal);
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#include <linux/quotaops.h> #include <linux/quotaops.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include "xattr.h" #include "xattr.h"
#include "acl.h"
#define EXT3_EA_USER "user." #define EXT3_EA_USER "user."
...@@ -1105,15 +1106,27 @@ init_ext3_xattr(void) ...@@ -1105,15 +1106,27 @@ init_ext3_xattr(void)
err = ext3_xattr_register(EXT3_XATTR_INDEX_USER, &ext3_xattr_user_handler); err = ext3_xattr_register(EXT3_XATTR_INDEX_USER, &ext3_xattr_user_handler);
if (err) if (err)
return err; return err;
#ifdef CONFIG_EXT3_FS_POSIX_ACL
err = init_ext3_acl();
if (err)
goto out;
#endif
ext3_xattr_cache = mb_cache_create("ext3_xattr", NULL, ext3_xattr_cache = mb_cache_create("ext3_xattr", NULL,
sizeof(struct mb_cache_entry) + sizeof(struct mb_cache_entry) +
sizeof(struct mb_cache_entry_index), 1, 6); sizeof(struct mb_cache_entry_index), 1, 6);
if (!ext3_xattr_cache) { if (!ext3_xattr_cache) {
ext3_xattr_unregister(EXT3_XATTR_INDEX_USER, &ext3_xattr_user_handler); err = -ENOMEM;
return -ENOMEM; goto out1;
} }
return 0; return 0;
out1:
#ifdef CONFIG_EXT3_FS_POSIX_ACL
exit_ext3_acl();
out:
#endif
ext3_xattr_unregister(EXT3_XATTR_INDEX_USER,
&ext3_xattr_user_handler);
return err;
} }
void void
...@@ -1122,6 +1135,8 @@ exit_ext3_xattr(void) ...@@ -1122,6 +1135,8 @@ exit_ext3_xattr(void)
if (ext3_xattr_cache) if (ext3_xattr_cache)
mb_cache_destroy(ext3_xattr_cache); mb_cache_destroy(ext3_xattr_cache);
ext3_xattr_cache = NULL; ext3_xattr_cache = NULL;
#ifdef CONFIG_EXT3_FS_POSIX_ACL
exit_ext3_acl();
#endif
ext3_xattr_unregister(EXT3_XATTR_INDEX_USER, &ext3_xattr_user_handler); ext3_xattr_unregister(EXT3_XATTR_INDEX_USER, &ext3_xattr_user_handler);
} }
...@@ -323,6 +323,7 @@ struct ext3_inode { ...@@ -323,6 +323,7 @@ struct ext3_inode {
#define EXT3_MOUNT_UPDATE_JOURNAL 0x1000 /* Update the journal format */ #define EXT3_MOUNT_UPDATE_JOURNAL 0x1000 /* Update the journal format */
#define EXT3_MOUNT_NO_UID32 0x2000 /* Disable 32-bit UIDs */ #define EXT3_MOUNT_NO_UID32 0x2000 /* Disable 32-bit UIDs */
#define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ #define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */
#define EXT3_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */
/* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */
#ifndef _LINUX_EXT2_FS_H #ifndef _LINUX_EXT2_FS_H
......
...@@ -41,6 +41,10 @@ struct ext3_inode_info { ...@@ -41,6 +41,10 @@ struct ext3_inode_info {
__u32 i_prealloc_count; __u32 i_prealloc_count;
#endif #endif
__u32 i_dir_start_lookup; __u32 i_dir_start_lookup;
#ifdef CONFIG_EXT3_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
struct list_head i_orphan; /* unlinked but open inodes */ struct list_head i_orphan; /* unlinked but open inodes */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment