* Re: Generic infrastructure for acls
2006-09-01 22:14 ` Generic infrastructure for acls Andreas Gruenbacher
@ 2006-09-01 21:44 ` Andrew Morton
2006-09-01 22:32 ` Andreas Gruenbacher
2006-09-06 16:40 ` Andreas Gruenbacher
2006-09-06 6:54 ` Jan Engelhardt
1 sibling, 2 replies; 13+ messages in thread
From: Andrew Morton @ 2006-09-01 21:44 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: linux-kernel, James Morris, Kay Sievers
On Sat, 02 Sep 2006 00:14:22 +0200
Andreas Gruenbacher <agruen@suse.de> wrote:
> +generic_acl_list(struct inode *inode, struct generic_acl_operations *ops,
> + int type, char *list, size_t list_size)
> +{
> + struct posix_acl *acl;
> + const char *name;
> + size_t size;
> +
> + acl = ops->getacl(inode, type);
> + if (!acl)
> + return 0;
> + posix_acl_release(acl);
> +
> + switch(type) {
> + case ACL_TYPE_ACCESS:
> + name = POSIX_ACL_XATTR_ACCESS;
> + break;
> +
> + case ACL_TYPE_DEFAULT:
> + name = POSIX_ACL_XATTR_DEFAULT;
> + break;
> +
> + default:
> + return 0;
> + }
> + size = strlen(name) + 1;
> + if (list && size <= list_size)
> + memcpy(list, name, size);
> + return size;
> +}
That's a clumsy-looking interface. How is the caller to know that *list
got filled in? By checking the generic_acl_list() return value against
`list_size'?
If so, shouldn't this be covered in the API description (when you write
it ;))?
Or should it be returning some error code in this case?
Or should we just strdup() the thing?
Or return `name' and let the caller worry about it?
--
VGER BF report: H 1.83187e-15
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Access Control Lists for tmpfs
2006-09-01 22:14 ` Access Control Lists for tmpfs Andreas Gruenbacher
@ 2006-09-01 21:52 ` Andrew Morton
2006-09-02 7:24 ` Arjan van de Ven
2006-09-05 19:07 ` Andrew Morton
2006-09-06 7:02 ` Jan Engelhardt
2 siblings, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2006-09-01 21:52 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: linux-kernel, James Morris, Kay Sievers
On Sat, 02 Sep 2006 00:14:23 +0200
Andreas Gruenbacher <agruen@suse.de> wrote:
> +static void
> +shmem_set_acl(struct inode *inode, int type, struct posix_acl *acl)
> +{
> + spin_lock(&inode->i_lock);
> + switch(type) {
> + case ACL_TYPE_ACCESS:
> + if (SHMEM_I(inode)->i_acl)
> + posix_acl_release(SHMEM_I(inode)->i_acl);
> + SHMEM_I(inode)->i_acl = posix_acl_dup(acl);
> + break;
i_lock is "general-purpose, innermost per-inode lock". Calling kfree()
under it makes it no longer "innermost". But kfree() is surely atomic wrt
everything which filesystems and the VFS will want to do, so that's OK.
However it does point at an inefficiency. There's no need at all to be
holding onto that lock while running kfree().
--
VGER BF report: H 0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [patch 0/2] Tmpfs acls
@ 2006-09-01 22:14 Andreas Gruenbacher
2006-09-01 22:14 ` Generic infrastructure for acls Andreas Gruenbacher
2006-09-01 22:14 ` Access Control Lists for tmpfs Andreas Gruenbacher
0 siblings, 2 replies; 13+ messages in thread
From: Andreas Gruenbacher @ 2006-09-01 22:14 UTC (permalink / raw)
To: linux-kernel, Andrew Morton; +Cc: James Morris, Kay Sievers
This is a refresh of the patches we have to support POSIX ACLs on tmpfs,
with the request for inclusion.
The patches solve the following problem: We want to grant access to
devices based on who is logged in from where, etc. This includes
switching back and forth between multiple user sessions, etc.
Using ACLs to define device access for logged-in users gives us all the
flexibility we need in order to fully solve the problem.
Device special files nowadays usually live on tmpfs, hence tmpfs ACLs.
Different distros have come up with solutions that solve the problem to
different degrees: SUSE uses a resource manager which tracks login
sessions and sets ACLs on device inodes as appropriate. RedHat uses
pam_console, which changes the primary file ownership to the logged-in
user. Others use a set of groups that users must be in in order to be
granted the appropriate accesses.
The freedesktop.org project plans to implement a combination of a
console-tracker and a HAL-device-list based solution to grant access to
devices to users, and more distros will likely follow this approach.
-
These patches have first been posted here on 2 February 2005, and again
on 8 January 2006. We have been shipping them in SLES9 and SLES10 with
no problems reported. The previous submission is archived here:
http://lkml.org/lkml/2006/1/8/229
http://lkml.org/lkml/2006/1/8/230
http://lkml.org/lkml/2006/1/8/231
Could the patches please get included this time?
Andrew, is putting them in -mm for a while fine with you?
Thanks,
Andreas
--
Andreas Gruenbacher <agruen@suse.de>
Novell / SUSE Labs
--
VGER BF report: U 0.5
^ permalink raw reply [flat|nested] 13+ messages in thread
* Generic infrastructure for acls
2006-09-01 22:14 [patch 0/2] Tmpfs acls Andreas Gruenbacher
@ 2006-09-01 22:14 ` Andreas Gruenbacher
2006-09-01 21:44 ` Andrew Morton
2006-09-06 6:54 ` Jan Engelhardt
2006-09-01 22:14 ` Access Control Lists for tmpfs Andreas Gruenbacher
1 sibling, 2 replies; 13+ messages in thread
From: Andreas Gruenbacher @ 2006-09-01 22:14 UTC (permalink / raw)
To: linux-kernel, Andrew Morton; +Cc: James Morris, Kay Sievers
[-- Attachment #1: generic-acl.diff --]
[-- Type: text/plain, Size: 6182 bytes --]
Add some infrastructure for access control lists on in-memory
filesystems such as tmpfs.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/Kconfig | 4 +
fs/Makefile | 1
fs/generic_acl.c | 172 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/generic_acl.h | 30 +++++++
4 files changed, 207 insertions(+)
Index: linux-2.6.18-rc5/fs/Kconfig
===================================================================
--- linux-2.6.18-rc5.orig/fs/Kconfig
+++ linux-2.6.18-rc5/fs/Kconfig
@@ -1921,6 +1921,10 @@ config 9P_FS
If unsure, say N.
+config GENERIC_ACL
+ bool
+ select FS_POSIX_ACL
+
endmenu
menu "Partition Types"
Index: linux-2.6.18-rc5/fs/Makefile
===================================================================
--- linux-2.6.18-rc5.orig/fs/Makefile
+++ linux-2.6.18-rc5/fs/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat
obj-$(CONFIG_FS_MBCACHE) += mbcache.o
obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
obj-$(CONFIG_NFS_COMMON) += nfs_common/
+obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
obj-$(CONFIG_QUOTA) += dquot.o
obj-$(CONFIG_QFMT_V1) += quota_v1.o
Index: linux-2.6.18-rc5/include/linux/generic_acl.h
===================================================================
--- /dev/null
+++ linux-2.6.18-rc5/include/linux/generic_acl.h
@@ -0,0 +1,30 @@
+/*
+ * fs/generic_acl.c
+ *
+ * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef GENERIC_ACL_H
+#define GENERIC_ACL_H
+
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
+
+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,
+ char *, size_t);
+int generic_acl_get(struct inode *, struct generic_acl_operations *, int,
+ 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
Index: linux-2.6.18-rc5/fs/generic_acl.c
===================================================================
--- /dev/null
+++ linux-2.6.18-rc5/fs/generic_acl.c
@@ -0,0 +1,172 @@
+/*
+ * fs/generic_acl.c
+ *
+ * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/generic_acl.h>
+
+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;
+ const char *name;
+ size_t size;
+
+ acl = ops->getacl(inode, type);
+ if (!acl)
+ return 0;
+ posix_acl_release(acl);
+
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ name = POSIX_ACL_XATTR_ACCESS;
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ name = POSIX_ACL_XATTR_DEFAULT;
+ break;
+
+ default:
+ return 0;
+ }
+ size = strlen(name) + 1;
+ if (list && size <= list_size)
+ memcpy(list, name, size);
+ return size;
+}
+
+int
+generic_acl_get(struct inode *inode, struct generic_acl_operations *ops,
+ int type, void *buffer, size_t size)
+{
+ struct posix_acl *acl;
+ int error;
+
+ acl = ops->getacl(inode, type);
+ if (!acl)
+ return -ENODATA;
+ error = posix_acl_to_xattr(acl, buffer, size);
+ posix_acl_release(acl);
+
+ return error;
+}
+
+int
+generic_acl_set(struct inode *inode, struct generic_acl_operations *ops,
+ int type, const void *value, size_t size)
+{
+ struct posix_acl *acl = NULL;
+ int error;
+
+ if (S_ISLNK(inode->i_mode))
+ 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);
+ }
+ if (acl) {
+ mode_t mode;
+
+ error = posix_acl_valid(acl);
+ if (error)
+ goto failed;
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ mode = inode->i_mode;
+ error = posix_acl_equiv_mode(acl, &mode);
+ if (error < 0)
+ goto failed;
+ inode->i_mode = mode;
+ if (error == 0) {
+ posix_acl_release(acl);
+ acl = NULL;
+ }
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ if (!S_ISDIR(inode->i_mode)) {
+ error = -EINVAL;
+ goto failed;
+ }
+ break;
+ }
+ }
+ ops->setacl(inode, type, acl);
+ error = 0;
+failed:
+ posix_acl_release(acl);
+ return error;
+}
+
+int
+generic_acl_init(struct inode *inode, struct inode *dir,
+ struct generic_acl_operations *ops)
+{
+ struct posix_acl *acl = NULL;
+ mode_t mode = inode->i_mode;
+ int error;
+
+ inode->i_mode = mode & ~current->fs->umask;
+ if (!S_ISLNK(inode->i_mode))
+ acl = ops->getacl(dir, ACL_TYPE_DEFAULT);
+ if (acl) {
+ struct posix_acl *clone;
+
+ if (S_ISDIR(inode->i_mode)) {
+ clone = posix_acl_clone(acl, GFP_KERNEL);
+ error = -ENOMEM;
+ if (!clone)
+ goto cleanup;
+ ops->setacl(inode, ACL_TYPE_DEFAULT, clone);
+ posix_acl_release(clone);
+ }
+ clone = posix_acl_clone(acl, GFP_KERNEL);
+ error = -ENOMEM;
+ if (!clone)
+ goto cleanup;
+ error = posix_acl_create_masq(clone, &mode);
+ if (error >= 0) {
+ inode->i_mode = mode;
+ if (error > 0) {
+ ops->setacl(inode, ACL_TYPE_ACCESS, clone);
+ }
+ }
+ posix_acl_release(clone);
+ }
+ error = 0;
+
+cleanup:
+ posix_acl_release(acl);
+ return error;
+}
+
+int
+generic_acl_chmod(struct inode *inode, struct generic_acl_operations *ops)
+{
+ struct posix_acl *acl, *clone;
+ int error = 0;
+
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+ acl = ops->getacl(inode, ACL_TYPE_ACCESS);
+ if (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)
+ ops->setacl(inode, ACL_TYPE_ACCESS, clone);
+ posix_acl_release(clone);
+ }
+ return error;
+}
--
Andreas Gruenbacher <agruen@suse.de>
SUSE Labs, SUSE LINUX Products GmbH / Novell Inc.
--
VGER BF report: H 0
^ permalink raw reply [flat|nested] 13+ messages in thread
* Access Control Lists for tmpfs
2006-09-01 22:14 [patch 0/2] Tmpfs acls Andreas Gruenbacher
2006-09-01 22:14 ` Generic infrastructure for acls Andreas Gruenbacher
@ 2006-09-01 22:14 ` Andreas Gruenbacher
2006-09-01 21:52 ` Andrew Morton
` (2 more replies)
1 sibling, 3 replies; 13+ messages in thread
From: Andreas Gruenbacher @ 2006-09-01 22:14 UTC (permalink / raw)
To: linux-kernel, Andrew Morton; +Cc: James Morris, Kay Sievers
[-- Attachment #1: tmpfs-acl.diff --]
[-- Type: text/plain, Size: 12887 bytes --]
Add access control lists for tmpfs.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/Kconfig | 13 +++
include/linux/shmem_fs.h | 21 ++++++
mm/Makefile | 1
mm/shmem.c | 94 ++++++++++++++++++++++++++-
mm/shmem_acl.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 290 insertions(+), 2 deletions(-)
Index: linux-2.6.18-rc5/fs/Kconfig
===================================================================
--- linux-2.6.18-rc5.orig/fs/Kconfig
+++ linux-2.6.18-rc5/fs/Kconfig
@@ -862,6 +862,19 @@ config TMPFS
See <file:Documentation/filesystems/tmpfs.txt> for details.
+config TMPFS_POSIX_ACL
+ bool "Tmpfs POSIX Access Control Lists"
+ depends on TMPFS
+ select GENERIC_ACL
+ 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 HUGETLBFS
bool "HugeTLB file system support"
depends X86 || IA64 || PPC64 || SPARC64 || SUPERH || BROKEN
Index: linux-2.6.18-rc5/include/linux/shmem_fs.h
===================================================================
--- linux-2.6.18-rc5.orig/include/linux/shmem_fs.h
+++ linux-2.6.18-rc5/include/linux/shmem_fs.h
@@ -19,6 +19,10 @@ struct shmem_inode_info {
swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */
struct list_head swaplist; /* chain of maybes on swap */
struct inode vfs_inode;
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ struct posix_acl *i_acl;
+ struct posix_acl *i_default_acl;
+#endif
};
struct shmem_sb_info {
@@ -36,4 +40,21 @@ static inline struct shmem_inode_info *S
return container_of(inode, struct shmem_inode_info, vfs_inode);
}
+#ifdef CONFIG_TMPFS_POSIX_ACL
+int shmem_permission(struct inode *, int, struct nameidata *);
+int shmem_acl_init(struct inode *, struct inode *);
+void shmem_acl_destroy_inode(struct inode *);
+
+extern struct xattr_handler shmem_xattr_acl_access_handler;
+extern struct xattr_handler shmem_xattr_acl_default_handler;
+#else
+static inline int shmem_acl_init(struct inode *inode, struct inode *dir)
+{
+ return 0;
+}
+static inline void shmem_acl_destroy_inode(struct inode *inode)
+{
+}
+#endif /* CONFIG_TMPFS_POSIX_ACL */
+
#endif
Index: linux-2.6.18-rc5/mm/Makefile
===================================================================
--- linux-2.6.18-rc5.orig/mm/Makefile
+++ linux-2.6.18-rc5/mm/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_HUGETLBFS) += hugetlb.o
obj-$(CONFIG_NUMA) += mempolicy.o
obj-$(CONFIG_SPARSEMEM) += sparse.o
obj-$(CONFIG_SHMEM) += shmem.o
+obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o
obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o
obj-$(CONFIG_SLOB) += slob.o
obj-$(CONFIG_SLAB) += slab.o
Index: linux-2.6.18-rc5/mm/shmem.c
===================================================================
--- linux-2.6.18-rc5.orig/mm/shmem.c
+++ linux-2.6.18-rc5/mm/shmem.c
@@ -26,6 +26,8 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/generic_acl.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/file.h>
@@ -176,6 +178,7 @@ static const struct address_space_operat
static struct file_operations shmem_file_operations;
static struct inode_operations shmem_inode_operations;
static struct inode_operations shmem_dir_inode_operations;
+static struct inode_operations shmem_special_inode_operations;
static struct vm_operations_struct shmem_vm_ops;
static struct backing_dev_info shmem_backing_dev_info __read_mostly = {
@@ -630,13 +633,15 @@ static void shmem_truncate(struct inode
shmem_truncate_range(inode, inode->i_size, (loff_t)-1);
}
+extern struct generic_acl_operations shmem_acl_ops;
+
static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct page *page = NULL;
int error;
- if (attr->ia_valid & ATTR_SIZE) {
+ if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
if (attr->ia_size < inode->i_size) {
/*
* If truncating down to a partial page, then
@@ -669,6 +674,10 @@ static int shmem_notify_change(struct de
error = inode_change_ok(inode, attr);
if (!error)
error = inode_setattr(inode, attr);
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ if (!error && (attr->ia_valid & ATTR_MODE))
+ error = generic_acl_chmod(inode, &shmem_acl_ops);
+#endif
if (page)
page_cache_release(page);
return error;
@@ -1362,6 +1371,7 @@ shmem_get_inode(struct super_block *sb,
switch (mode & S_IFMT) {
default:
+ inode->i_op = &shmem_special_inode_operations;
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
@@ -1682,7 +1692,11 @@ shmem_mknod(struct inode *dir, struct de
iput(inode);
return error;
}
- error = 0;
+ }
+ error = shmem_acl_init(inode, dir);
+ if (error) {
+ iput(inode);
+ return error;
}
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
@@ -1897,6 +1911,46 @@ static struct inode_operations shmem_sym
.put_link = shmem_put_link,
};
+#ifdef CONFIG_TMPFS_POSIX_ACL
+static size_t shmem_xattr_security_list(struct inode *inode, char *list,
+ size_t list_len, const char *name,
+ size_t name_len)
+{
+ return security_inode_listsecurity(inode, list, list_len);
+}
+
+static int shmem_xattr_security_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ return security_inode_getsecurity(inode, name, buffer, size,
+ -EOPNOTSUPP);
+}
+
+static int shmem_xattr_security_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ return security_inode_setsecurity(inode, name, value, size, flags);
+}
+
+struct xattr_handler shmem_xattr_security_handler = {
+ .prefix = XATTR_SECURITY_PREFIX,
+ .list = shmem_xattr_security_list,
+ .get = shmem_xattr_security_get,
+ .set = shmem_xattr_security_set,
+};
+
+static struct xattr_handler *shmem_xattr_handlers[] = {
+ &shmem_xattr_acl_access_handler,
+ &shmem_xattr_acl_default_handler,
+ &shmem_xattr_security_handler,
+ NULL
+};
+#endif
+
static int shmem_parse_options(char *options, int *mode, uid_t *uid,
gid_t *gid, unsigned long *blocks, unsigned long *inodes,
int *policy, nodemask_t *policy_nodes)
@@ -2094,6 +2148,10 @@ static int shmem_fill_super(struct super
sb->s_magic = TMPFS_MAGIC;
sb->s_op = &shmem_ops;
sb->s_time_gran = 1;
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ sb->s_xattr = shmem_xattr_handlers;
+ sb->s_flags |= MS_POSIXACL;
+#endif
inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
if (!inode)
@@ -2130,6 +2188,7 @@ static void shmem_destroy_inode(struct i
/* only struct inode is valid if it's an inline symlink */
mpol_free_shared_policy(&SHMEM_I(inode)->policy);
}
+ shmem_acl_destroy_inode(inode);
kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
}
@@ -2141,6 +2200,10 @@ static void init_once(void *foo, struct
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
inode_init_once(&p->vfs_inode);
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ p->i_acl = NULL;
+ p->i_default_acl = NULL;
+#endif
}
}
@@ -2185,6 +2248,14 @@ static struct inode_operations shmem_ino
.truncate = shmem_truncate,
.setattr = shmem_notify_change,
.truncate_range = shmem_truncate_range,
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
+ .listxattr = generic_listxattr,
+ .removexattr = generic_removexattr,
+ .permission = shmem_permission,
+#endif
+
};
static struct inode_operations shmem_dir_inode_operations = {
@@ -2199,6 +2270,25 @@ static struct inode_operations shmem_dir
.mknod = shmem_mknod,
.rename = shmem_rename,
#endif
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ .setattr = shmem_notify_change,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
+ .listxattr = generic_listxattr,
+ .removexattr = generic_removexattr,
+ .permission = shmem_permission,
+#endif
+};
+
+static struct inode_operations shmem_special_inode_operations = {
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ .setattr = shmem_notify_change,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
+ .listxattr = generic_listxattr,
+ .removexattr = generic_removexattr,
+ .permission = shmem_permission,
+#endif
};
static struct super_operations shmem_ops = {
Index: linux-2.6.18-rc5/mm/shmem_acl.c
===================================================================
--- /dev/null
+++ linux-2.6.18-rc5/mm/shmem_acl.c
@@ -0,0 +1,163 @@
+/*
+ * 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>
+
+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(SHMEM_I(inode)->i_acl);
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ acl = posix_acl_dup(SHMEM_I(inode)->i_default_acl);
+ break;
+ }
+ spin_unlock(&inode->i_lock);
+
+ return acl;
+}
+
+static void
+shmem_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+ spin_lock(&inode->i_lock);
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ if (SHMEM_I(inode)->i_acl)
+ posix_acl_release(SHMEM_I(inode)->i_acl);
+ SHMEM_I(inode)->i_acl = posix_acl_dup(acl);
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ if (SHMEM_I(inode)->i_default_acl)
+ posix_acl_release(SHMEM_I(inode)->i_default_acl);
+ SHMEM_I(inode)->i_default_acl = posix_acl_dup(acl);
+ break;
+ }
+ spin_unlock(&inode->i_lock);
+}
+
+struct generic_acl_operations shmem_acl_ops = {
+ .getacl = shmem_get_acl,
+ .setacl = shmem_set_acl,
+};
+
+static size_t
+shmem_list_acl_access(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ return generic_acl_list(inode, &shmem_acl_ops, ACL_TYPE_ACCESS,
+ list, list_size);
+}
+
+static size_t
+shmem_list_acl_default(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ return generic_acl_list(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT,
+ list, list_size);
+}
+
+static int
+shmem_get_acl_access(struct inode *inode, const char *name, void *buffer,
+ size_t size)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_get(inode, &shmem_acl_ops, ACL_TYPE_ACCESS, buffer,
+ size);
+}
+
+static int
+shmem_get_acl_default(struct inode *inode, const char *name, void *buffer,
+ size_t size)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_get(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT, buffer,
+ size);
+}
+
+static int
+shmem_set_acl_access(struct inode *inode, const char *name, const void *value,
+ size_t size, int flags)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_set(inode, &shmem_acl_ops, ACL_TYPE_ACCESS, value,
+ size);
+}
+
+static int
+shmem_set_acl_default(struct inode *inode, const char *name, const void *value,
+ size_t size, int flags)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_set(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT, value,
+ size);
+}
+
+struct xattr_handler shmem_xattr_acl_access_handler = {
+ .prefix = POSIX_ACL_XATTR_ACCESS,
+ .list = shmem_list_acl_access,
+ .get = shmem_get_acl_access,
+ .set = shmem_set_acl_access,
+};
+
+struct xattr_handler shmem_xattr_acl_default_handler = {
+ .prefix = POSIX_ACL_XATTR_DEFAULT,
+ .list = shmem_list_acl_default,
+ .get = shmem_get_acl_default,
+ .set = shmem_set_acl_default,
+};
+
+int
+shmem_acl_init(struct inode *inode, struct inode *dir)
+{
+ return generic_acl_init(inode, dir, &shmem_acl_ops);
+}
+
+void
+shmem_acl_destroy_inode(struct inode *inode)
+{
+ if (SHMEM_I(inode)->i_acl)
+ posix_acl_release(SHMEM_I(inode)->i_acl);
+ SHMEM_I(inode)->i_acl = NULL;
+ if (SHMEM_I(inode)->i_default_acl)
+ posix_acl_release(SHMEM_I(inode)->i_default_acl);
+ SHMEM_I(inode)->i_default_acl = NULL;
+}
+
+static 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;
+}
+
+int
+shmem_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+ return generic_permission(inode, mask, shmem_check_acl);
+}
--
Andreas Gruenbacher <agruen@suse.de>
SUSE Labs, SUSE LINUX Products GmbH / Novell Inc.
--
VGER BF report: H 0.0585043
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Generic infrastructure for acls
2006-09-01 21:44 ` Andrew Morton
@ 2006-09-01 22:32 ` Andreas Gruenbacher
2006-09-06 16:40 ` Andreas Gruenbacher
1 sibling, 0 replies; 13+ messages in thread
From: Andreas Gruenbacher @ 2006-09-01 22:32 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, James Morris, Kay Sievers
On Friday, 01 September 2006 23:44, Andrew Morton wrote:
> On Sat, 02 Sep 2006 00:14:22 +0200
>
> Andreas Gruenbacher <agruen@suse.de> wrote:
> > +generic_acl_list(struct inode *inode, struct generic_acl_operations
> > *ops, + int type, char *list, size_t list_size)
> > +{
> > + struct posix_acl *acl;
> > + const char *name;
> > + size_t size;
> > +
> > + acl = ops->getacl(inode, type);
> > + if (!acl)
> > + return 0;
> > + posix_acl_release(acl);
> > +
> > + switch(type) {
> > + case ACL_TYPE_ACCESS:
> > + name = POSIX_ACL_XATTR_ACCESS;
> > + break;
> > +
> > + case ACL_TYPE_DEFAULT:
> > + name = POSIX_ACL_XATTR_DEFAULT;
> > + break;
> > +
> > + default:
> > + return 0;
> > + }
> > + size = strlen(name) + 1;
> > + if (list && size <= list_size)
> > + memcpy(list, name, size);
> > + return size;
> > +}
>
> That's a clumsy-looking interface.
We could get rid of the switch by passing in the type and the name, but
otherwise that's pretty exactly what's needed, no matter if done in a
fs-dependent or independent way.
> How is the caller to know that *list got filled in? By checking the
generic_acl_list() return value against `list_size'?
The return value determines how many bytes have been used or would be needed
in the buffer -- generic_listxattr() in fs/xattr.c has this code:
> for_each_xattr_handler(handlers, handler) {
> size = handler->list(inode, buf, buffer_size, NULL, 0);
> if (size > buffer_size)
> return -ERANGE;
> buf += size;
> buffer_size -= size;
> }
Andreas
--
VGER BF report: H 0
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Access Control Lists for tmpfs
2006-09-01 21:52 ` Andrew Morton
@ 2006-09-02 7:24 ` Arjan van de Ven
0 siblings, 0 replies; 13+ messages in thread
From: Arjan van de Ven @ 2006-09-02 7:24 UTC (permalink / raw)
To: Andrew Morton
Cc: Andreas Gruenbacher, linux-kernel, James Morris, Kay Sievers
On Fri, 2006-09-01 at 14:52 -0700, Andrew Morton wrote:
> On Sat, 02 Sep 2006 00:14:23 +0200
> Andreas Gruenbacher <agruen@suse.de> wrote:
>
> > +static void
> > +shmem_set_acl(struct inode *inode, int type, struct posix_acl *acl)
> > +{
> > + spin_lock(&inode->i_lock);
> > + switch(type) {
> > + case ACL_TYPE_ACCESS:
> > + if (SHMEM_I(inode)->i_acl)
> > + posix_acl_release(SHMEM_I(inode)->i_acl);
> > + SHMEM_I(inode)->i_acl = posix_acl_dup(acl);
> > + break;
>
> i_lock is "general-purpose, innermost per-inode lock". Calling kfree()
> under it makes it no longer "innermost". But kfree() is surely atomic wrt
> everything which filesystems and the VFS will want to do, so that's OK.
and lockdep probably will yell loudly if there's a problem.
--
VGER BF report: H 0
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Access Control Lists for tmpfs
2006-09-01 22:14 ` Access Control Lists for tmpfs Andreas Gruenbacher
2006-09-01 21:52 ` Andrew Morton
@ 2006-09-05 19:07 ` Andrew Morton
2006-09-06 16:40 ` Andreas Gruenbacher
2006-09-06 7:02 ` Jan Engelhardt
2 siblings, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2006-09-05 19:07 UTC (permalink / raw)
To: Andreas Gruenbacher; +Cc: linux-kernel, James Morris, Kay Sievers
On Sat, 02 Sep 2006 00:14:23 +0200
Andreas Gruenbacher <agruen@suse.de> wrote:
> --- linux-2.6.18-rc5.orig/mm/shmem.c
> +++ linux-2.6.18-rc5/mm/shmem.c
> @@ -26,6 +26,8 @@
> #include <linux/module.h>
> #include <linux/init.h>
> #include <linux/fs.h>
> +#include <linux/xattr.h>
> +#include <linux/generic_acl.h>
> #include <linux/mm.h>
> #include <linux/mman.h>
> #include <linux/file.h>
> @@ -176,6 +178,7 @@ static const struct address_space_operat
> static struct file_operations shmem_file_operations;
> static struct inode_operations shmem_inode_operations;
> static struct inode_operations shmem_dir_inode_operations;
> +static struct inode_operations shmem_special_inode_operations;
> static struct vm_operations_struct shmem_vm_ops;
>
> static struct backing_dev_info shmem_backing_dev_info __read_mostly = {
> @@ -630,13 +633,15 @@ static void shmem_truncate(struct inode
> shmem_truncate_range(inode, inode->i_size, (loff_t)-1);
> }
>
> +extern struct generic_acl_operations shmem_acl_ops;
Can we move this declaration into a header file please?
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Generic infrastructure for acls
2006-09-01 22:14 ` Generic infrastructure for acls Andreas Gruenbacher
2006-09-01 21:44 ` Andrew Morton
@ 2006-09-06 6:54 ` Jan Engelhardt
1 sibling, 0 replies; 13+ messages in thread
From: Jan Engelhardt @ 2006-09-06 6:54 UTC (permalink / raw)
To: Andreas Gruenbacher
Cc: linux-kernel, Andrew Morton, James Morris, Kay Sievers
>+generic_acl_set(struct inode *inode, struct generic_acl_operations *ops,
>+ int type, const void *value, size_t size)
>+{
>+ struct posix_acl *acl = NULL;
>+ int error;
>+
>+ if (S_ISLNK(inode->i_mode))
>+ return -EOPNOTSUPP;
>+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
>+ if (error > 0) {
>+ ops->setacl(inode, ACL_TYPE_ACCESS, clone);
>+ }
redundant () {}
Jan Engelhardt
--
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Access Control Lists for tmpfs
2006-09-01 22:14 ` Access Control Lists for tmpfs Andreas Gruenbacher
2006-09-01 21:52 ` Andrew Morton
2006-09-05 19:07 ` Andrew Morton
@ 2006-09-06 7:02 ` Jan Engelhardt
2 siblings, 0 replies; 13+ messages in thread
From: Jan Engelhardt @ 2006-09-06 7:02 UTC (permalink / raw)
To: Andreas Gruenbacher
Cc: linux-kernel, Andrew Morton, James Morris, Kay Sievers
>+static int
>+shmem_get_acl_access(struct inode *inode, const char *name, void *buffer,
>+ size_t size)
>+{
>+ if (strcmp(name, "") != 0)
>+ return -EINVAL;
An interesting thing (tested in userspace):
strcmp(somestring, "")
will only evaluate to *sometring=='\0' if I add -static to CFLAGS.
Jan Engelhardt
--
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Access Control Lists for tmpfs
2006-09-05 19:07 ` Andrew Morton
@ 2006-09-06 16:40 ` Andreas Gruenbacher
0 siblings, 0 replies; 13+ messages in thread
From: Andreas Gruenbacher @ 2006-09-06 16:40 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, James Morris, Kay Sievers
[-- Attachment #1: Type: text/plain, Size: 216 bytes --]
On Tuesday, 05 September 2006 21:07, Andrew Morton wrote:
> Can we move this declaration into a header file please?
That's better, yes. Attached is a version that incorporates the feedback so
far.
Thanks,
Andreas
[-- Attachment #2: tmpfs-acl.diff --]
[-- Type: text/x-diff, Size: 13863 bytes --]
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Access Control Lists for tmpfs
References: 118299
Add access control lists for tmpfs.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/Kconfig | 13 +++
include/linux/shmem_fs.h | 21 ++++++
mm/Makefile | 1
mm/shmem.c | 94 ++++++++++++++++++++++++++-
mm/shmem_acl.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 290 insertions(+), 2 deletions(-)
Index: linux-2.6.18-rc6/fs/Kconfig
===================================================================
--- linux-2.6.18-rc6.orig/fs/Kconfig
+++ linux-2.6.18-rc6/fs/Kconfig
@@ -862,6 +862,19 @@ config TMPFS
See <file:Documentation/filesystems/tmpfs.txt> for details.
+config TMPFS_POSIX_ACL
+ bool "Tmpfs POSIX Access Control Lists"
+ depends on TMPFS
+ select GENERIC_ACL
+ 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 HUGETLBFS
bool "HugeTLB file system support"
depends X86 || IA64 || PPC64 || SPARC64 || SUPERH || BROKEN
Index: linux-2.6.18-rc6/include/linux/shmem_fs.h
===================================================================
--- linux-2.6.18-rc6.orig/include/linux/shmem_fs.h
+++ linux-2.6.18-rc6/include/linux/shmem_fs.h
@@ -19,6 +19,10 @@ struct shmem_inode_info {
swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */
struct list_head swaplist; /* chain of maybes on swap */
struct inode vfs_inode;
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ struct posix_acl *i_acl;
+ struct posix_acl *i_default_acl;
+#endif
};
struct shmem_sb_info {
@@ -36,4 +40,24 @@ static inline struct shmem_inode_info *S
return container_of(inode, struct shmem_inode_info, vfs_inode);
}
+#ifdef CONFIG_TMPFS_POSIX_ACL
+int shmem_permission(struct inode *, int, struct nameidata *);
+int shmem_acl_init(struct inode *, struct inode *);
+void shmem_acl_destroy_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;
+}
+static inline void shmem_acl_destroy_inode(struct inode *inode)
+{
+}
+#endif /* CONFIG_TMPFS_POSIX_ACL */
+
#endif
Index: linux-2.6.18-rc6/mm/Makefile
===================================================================
--- linux-2.6.18-rc6.orig/mm/Makefile
+++ linux-2.6.18-rc6/mm/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_HUGETLBFS) += hugetlb.o
obj-$(CONFIG_NUMA) += mempolicy.o
obj-$(CONFIG_SPARSEMEM) += sparse.o
obj-$(CONFIG_SHMEM) += shmem.o
+obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o
obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o
obj-$(CONFIG_SLOB) += slob.o
obj-$(CONFIG_SLAB) += slab.o
Index: linux-2.6.18-rc6/mm/shmem.c
===================================================================
--- linux-2.6.18-rc6.orig/mm/shmem.c
+++ linux-2.6.18-rc6/mm/shmem.c
@@ -26,6 +26,8 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/generic_acl.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/file.h>
@@ -176,6 +178,7 @@ static const struct address_space_operat
static struct file_operations shmem_file_operations;
static struct inode_operations shmem_inode_operations;
static struct inode_operations shmem_dir_inode_operations;
+static struct inode_operations shmem_special_inode_operations;
static struct vm_operations_struct shmem_vm_ops;
static struct backing_dev_info shmem_backing_dev_info __read_mostly = {
@@ -636,7 +639,7 @@ static int shmem_notify_change(struct de
struct page *page = NULL;
int error;
- if (attr->ia_valid & ATTR_SIZE) {
+ if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
if (attr->ia_size < inode->i_size) {
/*
* If truncating down to a partial page, then
@@ -669,6 +672,10 @@ static int shmem_notify_change(struct de
error = inode_change_ok(inode, attr);
if (!error)
error = inode_setattr(inode, attr);
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ if (!error && (attr->ia_valid & ATTR_MODE))
+ error = generic_acl_chmod(inode, &shmem_acl_ops);
+#endif
if (page)
page_cache_release(page);
return error;
@@ -1362,6 +1369,7 @@ shmem_get_inode(struct super_block *sb,
switch (mode & S_IFMT) {
default:
+ inode->i_op = &shmem_special_inode_operations;
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
@@ -1682,7 +1690,11 @@ shmem_mknod(struct inode *dir, struct de
iput(inode);
return error;
}
- error = 0;
+ }
+ error = shmem_acl_init(inode, dir);
+ if (error) {
+ iput(inode);
+ return error;
}
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
@@ -1897,6 +1909,53 @@ static struct inode_operations shmem_sym
.put_link = shmem_put_link,
};
+#ifdef CONFIG_TMPFS_POSIX_ACL
+/**
+ * Superblocks without xattr inode operations will get security.* xattr
+ * support from the VFS "for free". As soon as we have any other xattrs
+ * like ACLs, we also need to implement the security.* handlers at
+ * filesystem level, though.
+ */
+
+static size_t shmem_xattr_security_list(struct inode *inode, char *list,
+ size_t list_len, const char *name,
+ size_t name_len)
+{
+ return security_inode_listsecurity(inode, list, list_len);
+}
+
+static int shmem_xattr_security_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ return security_inode_getsecurity(inode, name, buffer, size,
+ -EOPNOTSUPP);
+}
+
+static int shmem_xattr_security_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+ return security_inode_setsecurity(inode, name, value, size, flags);
+}
+
+struct xattr_handler shmem_xattr_security_handler = {
+ .prefix = XATTR_SECURITY_PREFIX,
+ .list = shmem_xattr_security_list,
+ .get = shmem_xattr_security_get,
+ .set = shmem_xattr_security_set,
+};
+
+static struct xattr_handler *shmem_xattr_handlers[] = {
+ &shmem_xattr_acl_access_handler,
+ &shmem_xattr_acl_default_handler,
+ &shmem_xattr_security_handler,
+ NULL
+};
+#endif
+
static int shmem_parse_options(char *options, int *mode, uid_t *uid,
gid_t *gid, unsigned long *blocks, unsigned long *inodes,
int *policy, nodemask_t *policy_nodes)
@@ -2094,6 +2153,10 @@ static int shmem_fill_super(struct super
sb->s_magic = TMPFS_MAGIC;
sb->s_op = &shmem_ops;
sb->s_time_gran = 1;
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ sb->s_xattr = shmem_xattr_handlers;
+ sb->s_flags |= MS_POSIXACL;
+#endif
inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
if (!inode)
@@ -2130,6 +2193,7 @@ static void shmem_destroy_inode(struct i
/* only struct inode is valid if it's an inline symlink */
mpol_free_shared_policy(&SHMEM_I(inode)->policy);
}
+ shmem_acl_destroy_inode(inode);
kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
}
@@ -2141,6 +2205,10 @@ static void init_once(void *foo, struct
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
inode_init_once(&p->vfs_inode);
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ p->i_acl = NULL;
+ p->i_default_acl = NULL;
+#endif
}
}
@@ -2185,6 +2253,14 @@ static struct inode_operations shmem_ino
.truncate = shmem_truncate,
.setattr = shmem_notify_change,
.truncate_range = shmem_truncate_range,
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
+ .listxattr = generic_listxattr,
+ .removexattr = generic_removexattr,
+ .permission = shmem_permission,
+#endif
+
};
static struct inode_operations shmem_dir_inode_operations = {
@@ -2199,6 +2275,25 @@ static struct inode_operations shmem_dir
.mknod = shmem_mknod,
.rename = shmem_rename,
#endif
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ .setattr = shmem_notify_change,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
+ .listxattr = generic_listxattr,
+ .removexattr = generic_removexattr,
+ .permission = shmem_permission,
+#endif
+};
+
+static struct inode_operations shmem_special_inode_operations = {
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ .setattr = shmem_notify_change,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
+ .listxattr = generic_listxattr,
+ .removexattr = generic_removexattr,
+ .permission = shmem_permission,
+#endif
};
static struct super_operations shmem_ops = {
Index: linux-2.6.18-rc6/mm/shmem_acl.c
===================================================================
--- /dev/null
+++ linux-2.6.18-rc6/mm/shmem_acl.c
@@ -0,0 +1,197 @@
+/*
+ * 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(SHMEM_I(inode)->i_acl);
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ acl = posix_acl_dup(SHMEM_I(inode)->i_default_acl);
+ break;
+ }
+ spin_unlock(&inode->i_lock);
+
+ return acl;
+}
+
+/**
+ * shmem_get_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 = SHMEM_I(inode)->i_acl;
+ SHMEM_I(inode)->i_acl = posix_acl_dup(acl);
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ free = SHMEM_I(inode)->i_default_acl;
+ SHMEM_I(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,
+};
+
+/**
+ * shmem_list_acl_access, shmem_get_acl_access, shmem_set_acl_access,
+ * shmem_xattr_acl_access_handler - plumbing code to implement the
+ * system.posix_acl_access xattr using the generic acl functions.
+ */
+
+static size_t
+shmem_list_acl_access(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ return generic_acl_list(inode, &shmem_acl_ops, ACL_TYPE_ACCESS,
+ list, list_size);
+}
+
+static int
+shmem_get_acl_access(struct inode *inode, const char *name, void *buffer,
+ size_t size)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_get(inode, &shmem_acl_ops, ACL_TYPE_ACCESS, buffer,
+ size);
+}
+
+static int
+shmem_set_acl_access(struct inode *inode, const char *name, const void *value,
+ size_t size, int flags)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_set(inode, &shmem_acl_ops, ACL_TYPE_ACCESS, value,
+ size);
+}
+
+struct xattr_handler shmem_xattr_acl_access_handler = {
+ .prefix = POSIX_ACL_XATTR_ACCESS,
+ .list = shmem_list_acl_access,
+ .get = shmem_get_acl_access,
+ .set = shmem_set_acl_access,
+};
+
+/**
+ * shmem_list_acl_default, shmem_get_acl_default, shmem_set_acl_default,
+ * shmem_xattr_acl_default_handler - plumbing code to implement the
+ * system.posix_acl_default xattr using the generic acl functions.
+ */
+
+static size_t
+shmem_list_acl_default(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ return generic_acl_list(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT,
+ list, list_size);
+}
+
+static int
+shmem_get_acl_default(struct inode *inode, const char *name, void *buffer,
+ size_t size)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_get(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT, buffer,
+ size);
+}
+
+static int
+shmem_set_acl_default(struct inode *inode, const char *name, const void *value,
+ size_t size, int flags)
+{
+ if (strcmp(name, "") != 0)
+ return -EINVAL;
+ return generic_acl_set(inode, &shmem_acl_ops, ACL_TYPE_DEFAULT, value,
+ size);
+}
+
+struct xattr_handler shmem_xattr_acl_default_handler = {
+ .prefix = POSIX_ACL_XATTR_DEFAULT,
+ .list = shmem_list_acl_default,
+ .get = shmem_get_acl_default,
+ .set = shmem_set_acl_default,
+};
+
+/**
+ * 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_acl_destroy_inode - destroy acls hanging off the in-memory inode
+ *
+ * This is done before destroying the actual inode.
+ */
+
+void
+shmem_acl_destroy_inode(struct inode *inode)
+{
+ if (SHMEM_I(inode)->i_acl)
+ posix_acl_release(SHMEM_I(inode)->i_acl);
+ SHMEM_I(inode)->i_acl = NULL;
+ if (SHMEM_I(inode)->i_default_acl)
+ posix_acl_release(SHMEM_I(inode)->i_default_acl);
+ SHMEM_I(inode)->i_default_acl = NULL;
+}
+
+/**
+ * shmem_check_acl - check_acl() callback for generic_permission()
+ */
+static 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;
+}
+
+/**
+ * shmem_permission - permission() inode operation
+ */
+int
+shmem_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+ return generic_permission(inode, mask, shmem_check_acl);
+}
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Generic infrastructure for acls
2006-09-01 21:44 ` Andrew Morton
2006-09-01 22:32 ` Andreas Gruenbacher
@ 2006-09-06 16:40 ` Andreas Gruenbacher
2006-09-06 20:06 ` Randy.Dunlap
1 sibling, 1 reply; 13+ messages in thread
From: Andreas Gruenbacher @ 2006-09-06 16:40 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, James Morris, Kay Sievers
[-- Attachment #1: Type: text/plain, Size: 244 bytes --]
On Friday, 01 September 2006 23:44, Andrew Morton wrote:
> That's a clumsy-looking interface.
I have added a little documentation now. This will hopefully suffice to
clarify why the interface is as clumsy-looking as it is ;)
Thanks,
Andreas
[-- Attachment #2: generic-acl.diff --]
[-- Type: text/x-diff, Size: 7176 bytes --]
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Generic infrastructure for acls
Add some infrastructure for access control lists on in-memory
filesystems such as tmpfs.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/Kconfig | 4 +
fs/Makefile | 1
fs/generic_acl.c | 172 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/generic_acl.h | 30 +++++++
4 files changed, 207 insertions(+)
Index: linux-2.6.18-rc6/fs/Kconfig
===================================================================
--- linux-2.6.18-rc6.orig/fs/Kconfig
+++ linux-2.6.18-rc6/fs/Kconfig
@@ -1921,6 +1921,10 @@ config 9P_FS
If unsure, say N.
+config GENERIC_ACL
+ bool
+ select FS_POSIX_ACL
+
endmenu
menu "Partition Types"
Index: linux-2.6.18-rc6/fs/Makefile
===================================================================
--- linux-2.6.18-rc6.orig/fs/Makefile
+++ linux-2.6.18-rc6/fs/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat
obj-$(CONFIG_FS_MBCACHE) += mbcache.o
obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
obj-$(CONFIG_NFS_COMMON) += nfs_common/
+obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
obj-$(CONFIG_QUOTA) += dquot.o
obj-$(CONFIG_QFMT_V1) += quota_v1.o
Index: linux-2.6.18-rc6/include/linux/generic_acl.h
===================================================================
--- /dev/null
+++ linux-2.6.18-rc6/include/linux/generic_acl.h
@@ -0,0 +1,36 @@
+/*
+ * fs/generic_acl.c
+ *
+ * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef GENERIC_ACL_H
+#define GENERIC_ACL_H
+
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
+
+/**
+ * struct generic_acl_operations - filesystem operations
+ *
+ * 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,
+ char *, size_t);
+int generic_acl_get(struct inode *, struct generic_acl_operations *, int,
+ 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
Index: linux-2.6.18-rc6/fs/generic_acl.c
===================================================================
--- /dev/null
+++ linux-2.6.18-rc6/fs/generic_acl.c
@@ -0,0 +1,197 @@
+/*
+ * fs/generic_acl.c
+ *
+ * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/generic_acl.h>
+
+/**
+ * generic_acl_list - Generic xattr_handler->list() operation
+ * @ops: Filesystem specific getacl and setacl callbacks
+ */
+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;
+ const char *name;
+ size_t size;
+
+ acl = ops->getacl(inode, type);
+ if (!acl)
+ return 0;
+ posix_acl_release(acl);
+
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ name = POSIX_ACL_XATTR_ACCESS;
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ name = POSIX_ACL_XATTR_DEFAULT;
+ break;
+
+ default:
+ return 0;
+ }
+ size = strlen(name) + 1;
+ if (list && size <= list_size)
+ memcpy(list, name, size);
+ return size;
+}
+
+/**
+ * generic_acl_get - Generic xattr_handler->get() operation
+ * @ops: Filesystem specific getacl and setacl callbacks
+ */
+int
+generic_acl_get(struct inode *inode, struct generic_acl_operations *ops,
+ int type, void *buffer, size_t size)
+{
+ struct posix_acl *acl;
+ int error;
+
+ acl = ops->getacl(inode, type);
+ if (!acl)
+ return -ENODATA;
+ error = posix_acl_to_xattr(acl, buffer, size);
+ posix_acl_release(acl);
+
+ return error;
+}
+
+/**
+ * generic_acl_set - Generic xattr_handler->set() operation
+ * @ops: Filesystem specific getacl and setacl callbacks
+ */
+int
+generic_acl_set(struct inode *inode, struct generic_acl_operations *ops,
+ int type, const void *value, size_t size)
+{
+ struct posix_acl *acl = NULL;
+ int error;
+
+ if (S_ISLNK(inode->i_mode))
+ 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);
+ }
+ if (acl) {
+ mode_t mode;
+
+ error = posix_acl_valid(acl);
+ if (error)
+ goto failed;
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ mode = inode->i_mode;
+ error = posix_acl_equiv_mode(acl, &mode);
+ if (error < 0)
+ goto failed;
+ inode->i_mode = mode;
+ if (error == 0) {
+ posix_acl_release(acl);
+ acl = NULL;
+ }
+ break;
+
+ case ACL_TYPE_DEFAULT:
+ if (!S_ISDIR(inode->i_mode)) {
+ error = -EINVAL;
+ goto failed;
+ }
+ break;
+ }
+ }
+ ops->setacl(inode, type, acl);
+ error = 0;
+failed:
+ posix_acl_release(acl);
+ return error;
+}
+
+/**
+ * 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
+ * directory's default ACL.
+ */
+int
+generic_acl_init(struct inode *inode, struct inode *dir,
+ struct generic_acl_operations *ops)
+{
+ struct posix_acl *acl = NULL;
+ mode_t mode = inode->i_mode;
+ int error;
+
+ inode->i_mode = mode & ~current->fs->umask;
+ if (!S_ISLNK(inode->i_mode))
+ acl = ops->getacl(dir, ACL_TYPE_DEFAULT);
+ if (acl) {
+ struct posix_acl *clone;
+
+ if (S_ISDIR(inode->i_mode)) {
+ clone = posix_acl_clone(acl, GFP_KERNEL);
+ error = -ENOMEM;
+ if (!clone)
+ goto cleanup;
+ ops->setacl(inode, ACL_TYPE_DEFAULT, clone);
+ posix_acl_release(clone);
+ }
+ clone = posix_acl_clone(acl, GFP_KERNEL);
+ error = -ENOMEM;
+ if (!clone)
+ goto cleanup;
+ error = posix_acl_create_masq(clone, &mode);
+ if (error >= 0) {
+ inode->i_mode = mode;
+ if (error > 0)
+ ops->setacl(inode, ACL_TYPE_ACCESS, clone);
+ }
+ posix_acl_release(clone);
+ }
+ error = 0;
+
+cleanup:
+ posix_acl_release(acl);
+ return error;
+}
+
+/**
+ * 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
+ * other ACL entries.
+ */
+int
+generic_acl_chmod(struct inode *inode, struct generic_acl_operations *ops)
+{
+ struct posix_acl *acl, *clone;
+ int error = 0;
+
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+ acl = ops->getacl(inode, ACL_TYPE_ACCESS);
+ if (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)
+ ops->setacl(inode, ACL_TYPE_ACCESS, clone);
+ posix_acl_release(clone);
+ }
+ return error;
+}
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Generic infrastructure for acls
2006-09-06 16:40 ` Andreas Gruenbacher
@ 2006-09-06 20:06 ` Randy.Dunlap
0 siblings, 0 replies; 13+ messages in thread
From: Randy.Dunlap @ 2006-09-06 20:06 UTC (permalink / raw)
To: Andreas Gruenbacher
Cc: Andrew Morton, linux-kernel, James Morris, Kay Sievers
On Wed, 6 Sep 2006 18:40:12 +0200 Andreas Gruenbacher wrote:
> On Friday, 01 September 2006 23:44, Andrew Morton wrote:
> > That's a clumsy-looking interface.
>
> I have added a little documentation now. This will hopefully suffice to
> clarify why the interface is as clumsy-looking as it is ;)
oops. A bit difficult to comment on the patch.
Anyway, for the kernel-doc function headers (Thanks),
all of the parameters need to be listed, not just the first
one (@ops).
---
~Randy
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2006-09-06 20:03 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-09-01 22:14 [patch 0/2] Tmpfs acls Andreas Gruenbacher
2006-09-01 22:14 ` Generic infrastructure for acls Andreas Gruenbacher
2006-09-01 21:44 ` Andrew Morton
2006-09-01 22:32 ` Andreas Gruenbacher
2006-09-06 16:40 ` Andreas Gruenbacher
2006-09-06 20:06 ` Randy.Dunlap
2006-09-06 6:54 ` Jan Engelhardt
2006-09-01 22:14 ` Access Control Lists for tmpfs Andreas Gruenbacher
2006-09-01 21:52 ` Andrew Morton
2006-09-02 7:24 ` Arjan van de Ven
2006-09-05 19:07 ` Andrew Morton
2006-09-06 16:40 ` Andreas Gruenbacher
2006-09-06 7:02 ` Jan Engelhardt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox