make generic_acl slightly more generic

Now that we cache the ACL pointers in the generic inode all the generic_acl
cruft can go away and generic_acl.c can directly implement xattr handlers
dealing with the full Posix ACL semantics for in-memory filesystems.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Christoph Hellwig 2009-11-03 16:44:44 +01:00 committed by Al Viro
parent 431547b3c4
commit 1c7c474c31
6 changed files with 109 additions and 257 deletions

View file

@ -1,62 +1,58 @@
/* /*
* fs/generic_acl.c
*
* (C) 2005 Andreas Gruenbacher <agruen@suse.de> * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
* *
* This file is released under the GPL. * This file is released under the GPL.
*
* Generic ACL support for in-memory filesystems.
*/ */
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/generic_acl.h> #include <linux/generic_acl.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
/**
* generic_acl_list - Generic xattr_handler->list() operation static size_t
* @ops: Filesystem specific getacl and setacl callbacks generic_acl_list(struct dentry *dentry, char *list, size_t list_size,
*/ const char *name, size_t name_len, int type)
size_t
generic_acl_list(struct inode *inode, struct generic_acl_operations *ops,
int type, char *list, size_t list_size)
{ {
struct posix_acl *acl; struct posix_acl *acl;
const char *name; const char *xname;
size_t size; size_t size;
acl = ops->getacl(inode, type); acl = get_cached_acl(dentry->d_inode, type);
if (!acl) if (!acl)
return 0; return 0;
posix_acl_release(acl); posix_acl_release(acl);
switch(type) { switch (type) {
case ACL_TYPE_ACCESS: case ACL_TYPE_ACCESS:
name = POSIX_ACL_XATTR_ACCESS; xname = POSIX_ACL_XATTR_ACCESS;
break; break;
case ACL_TYPE_DEFAULT:
case ACL_TYPE_DEFAULT: xname = POSIX_ACL_XATTR_DEFAULT;
name = POSIX_ACL_XATTR_DEFAULT; break;
break; default:
return 0;
default:
return 0;
} }
size = strlen(name) + 1; size = strlen(xname) + 1;
if (list && size <= list_size) if (list && size <= list_size)
memcpy(list, name, size); memcpy(list, xname, size);
return size; return size;
} }
/** static int
* generic_acl_get - Generic xattr_handler->get() operation generic_acl_get(struct dentry *dentry, const char *name, void *buffer,
* @ops: Filesystem specific getacl and setacl callbacks size_t size, int type)
*/
int
generic_acl_get(struct inode *inode, struct generic_acl_operations *ops,
int type, void *buffer, size_t size)
{ {
struct posix_acl *acl; struct posix_acl *acl;
int error; int error;
acl = ops->getacl(inode, type); if (strcmp(name, "") != 0)
return -EINVAL;
acl = get_cached_acl(dentry->d_inode, type);
if (!acl) if (!acl)
return -ENODATA; return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size); error = posix_acl_to_xattr(acl, buffer, size);
@ -65,17 +61,16 @@ generic_acl_get(struct inode *inode, struct generic_acl_operations *ops,
return error; return error;
} }
/** static int
* generic_acl_set - Generic xattr_handler->set() operation generic_acl_set(struct dentry *dentry, const char *name, const void *value,
* @ops: Filesystem specific getacl and setacl callbacks size_t size, int flags, int type)
*/
int
generic_acl_set(struct inode *inode, struct generic_acl_operations *ops,
int type, const void *value, size_t size)
{ {
struct inode *inode = dentry->d_inode;
struct posix_acl *acl = NULL; struct posix_acl *acl = NULL;
int error; int error;
if (strcmp(name, "") != 0)
return -EINVAL;
if (S_ISLNK(inode->i_mode)) if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (!is_owner_or_cap(inode)) if (!is_owner_or_cap(inode))
@ -91,28 +86,27 @@ generic_acl_set(struct inode *inode, struct generic_acl_operations *ops,
error = posix_acl_valid(acl); error = posix_acl_valid(acl);
if (error) if (error)
goto failed; goto failed;
switch(type) { switch (type) {
case ACL_TYPE_ACCESS: case ACL_TYPE_ACCESS:
mode = inode->i_mode; mode = inode->i_mode;
error = posix_acl_equiv_mode(acl, &mode); error = posix_acl_equiv_mode(acl, &mode);
if (error < 0) if (error < 0)
goto failed; goto failed;
inode->i_mode = mode; inode->i_mode = mode;
if (error == 0) { if (error == 0) {
posix_acl_release(acl); posix_acl_release(acl);
acl = NULL; acl = NULL;
} }
break; break;
case ACL_TYPE_DEFAULT:
case ACL_TYPE_DEFAULT: if (!S_ISDIR(inode->i_mode)) {
if (!S_ISDIR(inode->i_mode)) { error = -EINVAL;
error = -EINVAL; goto failed;
goto failed; }
} break;
break;
} }
} }
ops->setacl(inode, type, acl); set_cached_acl(inode, type, acl);
error = 0; error = 0;
failed: failed:
posix_acl_release(acl); posix_acl_release(acl);
@ -121,14 +115,12 @@ failed:
/** /**
* generic_acl_init - Take care of acl inheritance at @inode create time * generic_acl_init - Take care of acl inheritance at @inode create time
* @ops: Filesystem specific getacl and setacl callbacks
* *
* Files created inside a directory with a default ACL inherit the * Files created inside a directory with a default ACL inherit the
* directory's default ACL. * directory's default ACL.
*/ */
int int
generic_acl_init(struct inode *inode, struct inode *dir, generic_acl_init(struct inode *inode, struct inode *dir)
struct generic_acl_operations *ops)
{ {
struct posix_acl *acl = NULL; struct posix_acl *acl = NULL;
mode_t mode = inode->i_mode; mode_t mode = inode->i_mode;
@ -136,7 +128,7 @@ generic_acl_init(struct inode *inode, struct inode *dir,
inode->i_mode = mode & ~current_umask(); inode->i_mode = mode & ~current_umask();
if (!S_ISLNK(inode->i_mode)) if (!S_ISLNK(inode->i_mode))
acl = ops->getacl(dir, ACL_TYPE_DEFAULT); acl = get_cached_acl(dir, ACL_TYPE_DEFAULT);
if (acl) { if (acl) {
struct posix_acl *clone; struct posix_acl *clone;
@ -145,7 +137,7 @@ generic_acl_init(struct inode *inode, struct inode *dir,
error = -ENOMEM; error = -ENOMEM;
if (!clone) if (!clone)
goto cleanup; goto cleanup;
ops->setacl(inode, ACL_TYPE_DEFAULT, clone); set_cached_acl(inode, ACL_TYPE_DEFAULT, clone);
posix_acl_release(clone); posix_acl_release(clone);
} }
clone = posix_acl_clone(acl, GFP_KERNEL); clone = posix_acl_clone(acl, GFP_KERNEL);
@ -156,7 +148,7 @@ generic_acl_init(struct inode *inode, struct inode *dir,
if (error >= 0) { if (error >= 0) {
inode->i_mode = mode; inode->i_mode = mode;
if (error > 0) if (error > 0)
ops->setacl(inode, ACL_TYPE_ACCESS, clone); set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
} }
posix_acl_release(clone); posix_acl_release(clone);
} }
@ -169,20 +161,19 @@ cleanup:
/** /**
* generic_acl_chmod - change the access acl of @inode upon chmod() * generic_acl_chmod - change the access acl of @inode upon chmod()
* @ops: FIlesystem specific getacl and setacl callbacks
* *
* A chmod also changes the permissions of the owner, group/mask, and * A chmod also changes the permissions of the owner, group/mask, and
* other ACL entries. * other ACL entries.
*/ */
int int
generic_acl_chmod(struct inode *inode, struct generic_acl_operations *ops) generic_acl_chmod(struct inode *inode)
{ {
struct posix_acl *acl, *clone; struct posix_acl *acl, *clone;
int error = 0; int error = 0;
if (S_ISLNK(inode->i_mode)) if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP; return -EOPNOTSUPP;
acl = ops->getacl(inode, ACL_TYPE_ACCESS); acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
if (acl) { if (acl) {
clone = posix_acl_clone(acl, GFP_KERNEL); clone = posix_acl_clone(acl, GFP_KERNEL);
posix_acl_release(acl); posix_acl_release(acl);
@ -190,8 +181,37 @@ generic_acl_chmod(struct inode *inode, struct generic_acl_operations *ops)
return -ENOMEM; return -ENOMEM;
error = posix_acl_chmod_masq(clone, inode->i_mode); error = posix_acl_chmod_masq(clone, inode->i_mode);
if (!error) if (!error)
ops->setacl(inode, ACL_TYPE_ACCESS, clone); set_cached_acl(inode, ACL_TYPE_ACCESS, clone);
posix_acl_release(clone); posix_acl_release(clone);
} }
return error; return error;
} }
int
generic_check_acl(struct inode *inode, int mask)
{
struct posix_acl *acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
if (acl) {
int error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl);
return error;
}
return -EAGAIN;
}
struct xattr_handler generic_acl_access_handler = {
.prefix = POSIX_ACL_XATTR_ACCESS,
.flags = ACL_TYPE_ACCESS,
.list = generic_acl_list,
.get = generic_acl_get,
.set = generic_acl_set,
};
struct xattr_handler generic_acl_default_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT,
.flags = ACL_TYPE_DEFAULT,
.list = generic_acl_list,
.get = generic_acl_get,
.set = generic_acl_set,
};

View file

@ -1,36 +1,15 @@
/* #ifndef LINUX_GENERIC_ACL_H
* include/linux/generic_acl.h #define LINUX_GENERIC_ACL_H
*
* (C) 2005 Andreas Gruenbacher <agruen@suse.de>
*
* This file is released under the GPL.
*/
#ifndef GENERIC_ACL_H #include <linux/xattr.h>
#define GENERIC_ACL_H
#include <linux/posix_acl.h> struct inode;
#include <linux/posix_acl_xattr.h>
/** extern struct xattr_handler generic_acl_access_handler;
* struct generic_acl_operations - filesystem operations extern struct xattr_handler generic_acl_default_handler;
*
* Filesystems must make these operations available to the generic
* operations.
*/
struct generic_acl_operations {
struct posix_acl *(*getacl)(struct inode *, int);
void (*setacl)(struct inode *, int, struct posix_acl *);
};
size_t generic_acl_list(struct inode *, struct generic_acl_operations *, int, int generic_acl_init(struct inode *, struct inode *);
char *, size_t); int generic_acl_chmod(struct inode *);
int generic_acl_get(struct inode *, struct generic_acl_operations *, int, int generic_check_acl(struct inode *inode, int mask);
void *, size_t);
int generic_acl_set(struct inode *, struct generic_acl_operations *, int,
const void *, size_t);
int generic_acl_init(struct inode *, struct inode *,
struct generic_acl_operations *);
int generic_acl_chmod(struct inode *, struct generic_acl_operations *);
#endif #endif /* LINUX_GENERIC_ACL_H */

View file

@ -41,20 +41,4 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
extern int init_tmpfs(void); extern int init_tmpfs(void);
extern int shmem_fill_super(struct super_block *sb, void *data, int silent); extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
#ifdef CONFIG_TMPFS_POSIX_ACL
int shmem_check_acl(struct inode *, int);
int shmem_acl_init(struct inode *, struct inode *);
extern struct xattr_handler shmem_xattr_acl_access_handler;
extern struct xattr_handler shmem_xattr_acl_default_handler;
extern struct generic_acl_operations shmem_acl_ops;
#else
static inline int shmem_acl_init(struct inode *inode, struct inode *dir)
{
return 0;
}
#endif /* CONFIG_TMPFS_POSIX_ACL */
#endif #endif

View file

@ -22,7 +22,6 @@ obj-$(CONFIG_HUGETLBFS) += hugetlb.o
obj-$(CONFIG_NUMA) += mempolicy.o obj-$(CONFIG_NUMA) += mempolicy.o
obj-$(CONFIG_SPARSEMEM) += sparse.o obj-$(CONFIG_SPARSEMEM) += sparse.o
obj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o obj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o
obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o
obj-$(CONFIG_SLOB) += slob.o obj-$(CONFIG_SLOB) += slob.o
obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o
obj-$(CONFIG_KSM) += ksm.o obj-$(CONFIG_KSM) += ksm.o

View file

@ -41,6 +41,7 @@ static struct vfsmount *shm_mnt;
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/posix_acl.h>
#include <linux/generic_acl.h> #include <linux/generic_acl.h>
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/string.h> #include <linux/string.h>
@ -809,7 +810,7 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
error = inode_setattr(inode, attr); error = inode_setattr(inode, attr);
#ifdef CONFIG_TMPFS_POSIX_ACL #ifdef CONFIG_TMPFS_POSIX_ACL
if (!error && (attr->ia_valid & ATTR_MODE)) if (!error && (attr->ia_valid & ATTR_MODE))
error = generic_acl_chmod(inode, &shmem_acl_ops); error = generic_acl_chmod(inode);
#endif #endif
if (page) if (page)
page_cache_release(page); page_cache_release(page);
@ -1823,11 +1824,13 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
return error; return error;
} }
} }
error = shmem_acl_init(inode, dir); #ifdef CONFIG_TMPFS_POSIX_ACL
error = generic_acl_init(inode, dir);
if (error) { if (error) {
iput(inode); iput(inode);
return error; return error;
} }
#endif
if (dir->i_mode & S_ISGID) { if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid; inode->i_gid = dir->i_gid;
if (S_ISDIR(mode)) if (S_ISDIR(mode))
@ -2074,8 +2077,8 @@ static struct xattr_handler shmem_xattr_security_handler = {
}; };
static struct xattr_handler *shmem_xattr_handlers[] = { static struct xattr_handler *shmem_xattr_handlers[] = {
&shmem_xattr_acl_access_handler, &generic_acl_access_handler,
&shmem_xattr_acl_default_handler, &generic_acl_default_handler,
&shmem_xattr_security_handler, &shmem_xattr_security_handler,
NULL NULL
}; };
@ -2454,7 +2457,7 @@ static const struct inode_operations shmem_inode_operations = {
.getxattr = generic_getxattr, .getxattr = generic_getxattr,
.listxattr = generic_listxattr, .listxattr = generic_listxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
.check_acl = shmem_check_acl, .check_acl = generic_check_acl,
#endif #endif
}; };
@ -2477,7 +2480,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
.getxattr = generic_getxattr, .getxattr = generic_getxattr,
.listxattr = generic_listxattr, .listxattr = generic_listxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
.check_acl = shmem_check_acl, .check_acl = generic_check_acl,
#endif #endif
}; };
@ -2488,7 +2491,7 @@ static const struct inode_operations shmem_special_inode_operations = {
.getxattr = generic_getxattr, .getxattr = generic_getxattr,
.listxattr = generic_listxattr, .listxattr = generic_listxattr,
.removexattr = generic_removexattr, .removexattr = generic_removexattr,
.check_acl = shmem_check_acl, .check_acl = generic_check_acl,
#endif #endif
}; };

View file

@ -1,133 +0,0 @@
/*
* mm/shmem_acl.c
*
* (C) 2005 Andreas Gruenbacher <agruen@suse.de>
*
* This file is released under the GPL.
*/
#include <linux/fs.h>
#include <linux/shmem_fs.h>
#include <linux/xattr.h>
#include <linux/generic_acl.h>
/**
* shmem_get_acl - generic_acl_operations->getacl() operation
*/
static struct posix_acl *
shmem_get_acl(struct inode *inode, int type)
{
struct posix_acl *acl = NULL;
spin_lock(&inode->i_lock);
switch(type) {
case ACL_TYPE_ACCESS:
acl = posix_acl_dup(inode->i_acl);
break;
case ACL_TYPE_DEFAULT:
acl = posix_acl_dup(inode->i_default_acl);
break;
}
spin_unlock(&inode->i_lock);
return acl;
}
/**
* shmem_set_acl - generic_acl_operations->setacl() operation
*/
static void
shmem_set_acl(struct inode *inode, int type, struct posix_acl *acl)
{
struct posix_acl *free = NULL;
spin_lock(&inode->i_lock);
switch(type) {
case ACL_TYPE_ACCESS:
free = inode->i_acl;
inode->i_acl = posix_acl_dup(acl);
break;
case ACL_TYPE_DEFAULT:
free = inode->i_default_acl;
inode->i_default_acl = posix_acl_dup(acl);
break;
}
spin_unlock(&inode->i_lock);
posix_acl_release(free);
}
struct generic_acl_operations shmem_acl_ops = {
.getacl = shmem_get_acl,
.setacl = shmem_set_acl,
};
static size_t
shmem_xattr_list_acl(struct dentry *dentry, char *list, size_t list_size,
const char *name, size_t name_len, int type)
{
return generic_acl_list(dentry->d_inode, &shmem_acl_ops,
type, list, list_size);
}
static int
shmem_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,
size_t size, int type)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return generic_acl_get(dentry->d_inode, &shmem_acl_ops, type,
buffer, size);
}
static int
shmem_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags, int type)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return generic_acl_set(dentry->d_inode, &shmem_acl_ops, type,
value, size);
}
struct xattr_handler shmem_xattr_acl_access_handler = {
.prefix = POSIX_ACL_XATTR_ACCESS,
.flags = ACL_TYPE_ACCESS,
.list = shmem_xattr_list_acl,
.get = shmem_xattr_get_acl,
.set = shmem_xattr_set_acl,
};
struct xattr_handler shmem_xattr_acl_default_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT,
.flags = ACL_TYPE_DEFAULT,
.list = shmem_xattr_list_acl,
.get = shmem_xattr_get_acl,
.set = shmem_xattr_set_acl,
};
/**
* shmem_acl_init - Inizialize the acl(s) of a new inode
*/
int
shmem_acl_init(struct inode *inode, struct inode *dir)
{
return generic_acl_init(inode, dir, &shmem_acl_ops);
}
/**
* shmem_check_acl - check_acl() callback for generic_permission()
*/
int
shmem_check_acl(struct inode *inode, int mask)
{
struct posix_acl *acl = shmem_get_acl(inode, ACL_TYPE_ACCESS);
if (acl) {
int error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl);
return error;
}
return -EAGAIN;
}