mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
configfs: Add permission and ownership to configfs objects.
configfs always made item and attribute ownership root.root and permissions based on a umask of 022. Add ->setattr() to allow chown(2)/chmod(2), and persist the changes for the lifetime of the items and attributes. Signed-off-by: Joel Becker <joel.becker@oracle.com> Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
This commit is contained in:
parent
62ca3d2603
commit
3d0f89bb16
@ -320,6 +320,7 @@ static struct config_item_type simple_children_type = {
|
||||
.ct_item_ops = &simple_children_item_ops,
|
||||
.ct_group_ops = &simple_children_group_ops,
|
||||
.ct_attrs = simple_children_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct configfs_subsystem simple_children_subsys = {
|
||||
@ -403,6 +404,7 @@ static struct config_item_type group_children_type = {
|
||||
.ct_item_ops = &group_children_item_ops,
|
||||
.ct_group_ops = &group_children_group_ops,
|
||||
.ct_attrs = group_children_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct configfs_subsystem group_children_subsys = {
|
||||
|
@ -36,6 +36,7 @@ struct configfs_dirent {
|
||||
int s_type;
|
||||
umode_t s_mode;
|
||||
struct dentry * s_dentry;
|
||||
struct iattr * s_iattr;
|
||||
};
|
||||
|
||||
#define CONFIGFS_ROOT 0x0001
|
||||
@ -48,10 +49,11 @@ struct configfs_dirent {
|
||||
#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR)
|
||||
|
||||
extern struct vfsmount * configfs_mount;
|
||||
extern kmem_cache_t *configfs_dir_cachep;
|
||||
|
||||
extern int configfs_is_root(struct config_item *item);
|
||||
|
||||
extern struct inode * configfs_new_inode(mode_t mode);
|
||||
extern struct inode * configfs_new_inode(mode_t mode, struct configfs_dirent *);
|
||||
extern int configfs_create(struct dentry *, int mode, int (*init)(struct inode *));
|
||||
|
||||
extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
|
||||
@ -63,6 +65,7 @@ extern void configfs_hash_and_remove(struct dentry * dir, const char * name);
|
||||
|
||||
extern const unsigned char * configfs_get_name(struct configfs_dirent *sd);
|
||||
extern void configfs_drop_dentry(struct configfs_dirent *sd, struct dentry *parent);
|
||||
extern int configfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
||||
|
||||
extern int configfs_pin_fs(void);
|
||||
extern void configfs_release_fs(void);
|
||||
@ -120,8 +123,10 @@ static inline struct config_item *configfs_get_config_item(struct dentry *dentry
|
||||
|
||||
static inline void release_configfs_dirent(struct configfs_dirent * sd)
|
||||
{
|
||||
if (!(sd->s_type & CONFIGFS_ROOT))
|
||||
kfree(sd);
|
||||
if (!(sd->s_type & CONFIGFS_ROOT)) {
|
||||
kfree(sd->s_iattr);
|
||||
kmem_cache_free(configfs_dir_cachep, sd);
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct configfs_dirent * configfs_get(struct configfs_dirent * sd)
|
||||
|
@ -72,7 +72,7 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent * pare
|
||||
{
|
||||
struct configfs_dirent * sd;
|
||||
|
||||
sd = kmalloc(sizeof(*sd), GFP_KERNEL);
|
||||
sd = kmem_cache_alloc(configfs_dir_cachep, GFP_KERNEL);
|
||||
if (!sd)
|
||||
return NULL;
|
||||
|
||||
@ -136,13 +136,19 @@ static int create_dir(struct config_item * k, struct dentry * p,
|
||||
int error;
|
||||
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
|
||||
|
||||
error = configfs_create(d, mode, init_dir);
|
||||
error = configfs_make_dirent(p->d_fsdata, d, k, mode,
|
||||
CONFIGFS_DIR);
|
||||
if (!error) {
|
||||
error = configfs_make_dirent(p->d_fsdata, d, k, mode,
|
||||
CONFIGFS_DIR);
|
||||
error = configfs_create(d, mode, init_dir);
|
||||
if (!error) {
|
||||
p->d_inode->i_nlink++;
|
||||
(d)->d_op = &configfs_dentry_ops;
|
||||
} else {
|
||||
struct configfs_dirent *sd = d->d_fsdata;
|
||||
if (sd) {
|
||||
list_del_init(&sd->s_sibling);
|
||||
configfs_put(sd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
@ -182,12 +188,19 @@ int configfs_create_link(struct configfs_symlink *sl,
|
||||
int err = 0;
|
||||
umode_t mode = S_IFLNK | S_IRWXUGO;
|
||||
|
||||
err = configfs_create(dentry, mode, init_symlink);
|
||||
err = configfs_make_dirent(parent->d_fsdata, dentry, sl, mode,
|
||||
CONFIGFS_ITEM_LINK);
|
||||
if (!err) {
|
||||
err = configfs_make_dirent(parent->d_fsdata, dentry, sl,
|
||||
mode, CONFIGFS_ITEM_LINK);
|
||||
err = configfs_create(dentry, mode, init_symlink);
|
||||
if (!err)
|
||||
dentry->d_op = &configfs_dentry_ops;
|
||||
else {
|
||||
struct configfs_dirent *sd = dentry->d_fsdata;
|
||||
if (sd) {
|
||||
list_del_init(&sd->s_sibling);
|
||||
configfs_put(sd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@ -241,13 +254,15 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
|
||||
struct configfs_attribute * attr = sd->s_element;
|
||||
int error;
|
||||
|
||||
error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, init_file);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
dentry->d_op = &configfs_dentry_ops;
|
||||
dentry->d_fsdata = configfs_get(sd);
|
||||
sd->s_dentry = dentry;
|
||||
error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, init_file);
|
||||
if (error) {
|
||||
configfs_put(sd);
|
||||
return error;
|
||||
}
|
||||
|
||||
dentry->d_op = &configfs_dentry_ops;
|
||||
d_rehash(dentry);
|
||||
|
||||
return 0;
|
||||
@ -839,6 +854,7 @@ struct inode_operations configfs_dir_inode_operations = {
|
||||
.symlink = configfs_symlink,
|
||||
.unlink = configfs_unlink,
|
||||
.lookup = configfs_lookup,
|
||||
.setattr = configfs_setattr,
|
||||
};
|
||||
|
||||
#if 0
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dnotify.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/semaphore.h>
|
||||
@ -150,7 +149,7 @@ out:
|
||||
/**
|
||||
* fill_write_buffer - copy buffer from userspace.
|
||||
* @buffer: data buffer for file.
|
||||
* @userbuf: data from user.
|
||||
* @buf: data from user.
|
||||
* @count: number of bytes in @userbuf.
|
||||
*
|
||||
* Allocate @buffer->page if it hasn't been already, then
|
||||
@ -177,8 +176,9 @@ fill_write_buffer(struct configfs_buffer * buffer, const char __user * buf, size
|
||||
|
||||
/**
|
||||
* flush_write_buffer - push buffer to config_item.
|
||||
* @file: file pointer.
|
||||
* @dentry: dentry to the attribute
|
||||
* @buffer: data buffer for file.
|
||||
* @count: number of bytes
|
||||
*
|
||||
* Get the correct pointers for the config_item and the attribute we're
|
||||
* dealing with, then call the store() method for the attribute,
|
||||
@ -217,15 +217,16 @@ static ssize_t
|
||||
configfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct configfs_buffer * buffer = file->private_data;
|
||||
ssize_t len;
|
||||
|
||||
down(&buffer->sem);
|
||||
count = fill_write_buffer(buffer,buf,count);
|
||||
if (count > 0)
|
||||
count = flush_write_buffer(file->f_dentry,buffer,count);
|
||||
if (count > 0)
|
||||
*ppos += count;
|
||||
len = fill_write_buffer(buffer, buf, count);
|
||||
if (len > 0)
|
||||
len = flush_write_buffer(file->f_dentry, buffer, count);
|
||||
if (len > 0)
|
||||
*ppos += len;
|
||||
up(&buffer->sem);
|
||||
return count;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int check_perm(struct inode * inode, struct file * file)
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/capability.h>
|
||||
|
||||
#include <linux/configfs.h>
|
||||
#include "configfs_internal.h"
|
||||
@ -48,18 +49,107 @@ static struct backing_dev_info configfs_backing_dev_info = {
|
||||
.capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
|
||||
};
|
||||
|
||||
struct inode * configfs_new_inode(mode_t mode)
|
||||
static struct inode_operations configfs_inode_operations ={
|
||||
.setattr = configfs_setattr,
|
||||
};
|
||||
|
||||
int configfs_setattr(struct dentry * dentry, struct iattr * iattr)
|
||||
{
|
||||
struct inode * inode = dentry->d_inode;
|
||||
struct configfs_dirent * sd = dentry->d_fsdata;
|
||||
struct iattr * sd_iattr;
|
||||
unsigned int ia_valid = iattr->ia_valid;
|
||||
int error;
|
||||
|
||||
if (!sd)
|
||||
return -EINVAL;
|
||||
|
||||
sd_iattr = sd->s_iattr;
|
||||
|
||||
error = inode_change_ok(inode, iattr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = inode_setattr(inode, iattr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!sd_iattr) {
|
||||
/* setting attributes for the first time, allocate now */
|
||||
sd_iattr = kmalloc(sizeof(struct iattr), GFP_KERNEL);
|
||||
if (!sd_iattr)
|
||||
return -ENOMEM;
|
||||
/* assign default attributes */
|
||||
memset(sd_iattr, 0, sizeof(struct iattr));
|
||||
sd_iattr->ia_mode = sd->s_mode;
|
||||
sd_iattr->ia_uid = 0;
|
||||
sd_iattr->ia_gid = 0;
|
||||
sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME;
|
||||
sd->s_iattr = sd_iattr;
|
||||
}
|
||||
|
||||
/* attributes were changed atleast once in past */
|
||||
|
||||
if (ia_valid & ATTR_UID)
|
||||
sd_iattr->ia_uid = iattr->ia_uid;
|
||||
if (ia_valid & ATTR_GID)
|
||||
sd_iattr->ia_gid = iattr->ia_gid;
|
||||
if (ia_valid & ATTR_ATIME)
|
||||
sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_MTIME)
|
||||
sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_CTIME)
|
||||
sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_MODE) {
|
||||
umode_t mode = iattr->ia_mode;
|
||||
|
||||
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
|
||||
mode &= ~S_ISGID;
|
||||
sd_iattr->ia_mode = sd->s_mode = mode;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static inline void set_default_inode_attr(struct inode * inode, mode_t mode)
|
||||
{
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = 0;
|
||||
inode->i_gid = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
}
|
||||
|
||||
static inline void set_inode_attr(struct inode * inode, struct iattr * iattr)
|
||||
{
|
||||
inode->i_mode = iattr->ia_mode;
|
||||
inode->i_uid = iattr->ia_uid;
|
||||
inode->i_gid = iattr->ia_gid;
|
||||
inode->i_atime = iattr->ia_atime;
|
||||
inode->i_mtime = iattr->ia_mtime;
|
||||
inode->i_ctime = iattr->ia_ctime;
|
||||
}
|
||||
|
||||
struct inode * configfs_new_inode(mode_t mode, struct configfs_dirent * sd)
|
||||
{
|
||||
struct inode * inode = new_inode(configfs_sb);
|
||||
if (inode) {
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = 0;
|
||||
inode->i_gid = 0;
|
||||
inode->i_blksize = PAGE_CACHE_SIZE;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_mapping->a_ops = &configfs_aops;
|
||||
inode->i_mapping->backing_dev_info = &configfs_backing_dev_info;
|
||||
inode->i_op = &configfs_inode_operations;
|
||||
|
||||
if (sd->s_iattr) {
|
||||
/* sysfs_dirent has non-default attributes
|
||||
* get them for the new inode from persistent copy
|
||||
* in sysfs_dirent
|
||||
*/
|
||||
set_inode_attr(inode, sd->s_iattr);
|
||||
} else
|
||||
set_default_inode_attr(inode, mode);
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
@ -70,7 +160,8 @@ int configfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *
|
||||
struct inode * inode = NULL;
|
||||
if (dentry) {
|
||||
if (!dentry->d_inode) {
|
||||
if ((inode = configfs_new_inode(mode))) {
|
||||
struct configfs_dirent *sd = dentry->d_fsdata;
|
||||
if ((inode = configfs_new_inode(mode, sd))) {
|
||||
if (dentry->d_parent && dentry->d_parent->d_inode) {
|
||||
struct inode *p_inode = dentry->d_parent->d_inode;
|
||||
p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME;
|
||||
@ -103,7 +194,7 @@ int configfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *
|
||||
*/
|
||||
const unsigned char * configfs_get_name(struct configfs_dirent *sd)
|
||||
{
|
||||
struct attribute * attr;
|
||||
struct configfs_attribute *attr;
|
||||
|
||||
if (!sd || !sd->s_element)
|
||||
BUG();
|
||||
@ -114,7 +205,7 @@ const unsigned char * configfs_get_name(struct configfs_dirent *sd)
|
||||
|
||||
if (sd->s_type & CONFIGFS_ITEM_ATTR) {
|
||||
attr = sd->s_element;
|
||||
return attr->name;
|
||||
return attr->ca_name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -130,13 +221,17 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)
|
||||
|
||||
if (dentry) {
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!(d_unhashed(dentry) && dentry->d_inode)) {
|
||||
dget_locked(dentry);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
simple_unlink(parent->d_inode, dentry);
|
||||
} else
|
||||
} else {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,6 +240,10 @@ void configfs_hash_and_remove(struct dentry * dir, const char * name)
|
||||
struct configfs_dirent * sd;
|
||||
struct configfs_dirent * parent_sd = dir->d_fsdata;
|
||||
|
||||
if (dir->d_inode == NULL)
|
||||
/* no inode means this hasn't been made visible yet */
|
||||
return;
|
||||
|
||||
mutex_lock(&dir->d_inode->i_mutex);
|
||||
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
|
||||
if (!sd->s_element)
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
struct vfsmount * configfs_mount = NULL;
|
||||
struct super_block * configfs_sb = NULL;
|
||||
kmem_cache_t *configfs_dir_cachep;
|
||||
static int configfs_mnt_count = 0;
|
||||
|
||||
static struct super_operations configfs_ops = {
|
||||
@ -62,6 +63,7 @@ static struct configfs_dirent configfs_root = {
|
||||
.s_children = LIST_HEAD_INIT(configfs_root.s_children),
|
||||
.s_element = &configfs_root_group.cg_item,
|
||||
.s_type = CONFIGFS_ROOT,
|
||||
.s_iattr = NULL,
|
||||
};
|
||||
|
||||
static int configfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
@ -73,9 +75,11 @@ static int configfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = CONFIGFS_MAGIC;
|
||||
sb->s_op = &configfs_ops;
|
||||
sb->s_time_gran = 1;
|
||||
configfs_sb = sb;
|
||||
|
||||
inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO);
|
||||
inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
|
||||
&configfs_root);
|
||||
if (inode) {
|
||||
inode->i_op = &configfs_dir_inode_operations;
|
||||
inode->i_fop = &configfs_dir_operations;
|
||||
@ -128,19 +132,31 @@ static decl_subsys(config, NULL, NULL);
|
||||
|
||||
static int __init configfs_init(void)
|
||||
{
|
||||
int err;
|
||||
int err = -ENOMEM;
|
||||
|
||||
configfs_dir_cachep = kmem_cache_create("configfs_dir_cache",
|
||||
sizeof(struct configfs_dirent),
|
||||
0, 0, NULL, NULL);
|
||||
if (!configfs_dir_cachep)
|
||||
goto out;
|
||||
|
||||
kset_set_kset_s(&config_subsys, kernel_subsys);
|
||||
err = subsystem_register(&config_subsys);
|
||||
if (err)
|
||||
return err;
|
||||
if (err) {
|
||||
kmem_cache_destroy(configfs_dir_cachep);
|
||||
configfs_dir_cachep = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = register_filesystem(&configfs_fs_type);
|
||||
if (err) {
|
||||
printk(KERN_ERR "configfs: Unable to register filesystem!\n");
|
||||
subsystem_unregister(&config_subsys);
|
||||
kmem_cache_destroy(configfs_dir_cachep);
|
||||
configfs_dir_cachep = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -148,11 +164,13 @@ static void __exit configfs_exit(void)
|
||||
{
|
||||
unregister_filesystem(&configfs_fs_type);
|
||||
subsystem_unregister(&config_subsys);
|
||||
kmem_cache_destroy(configfs_dir_cachep);
|
||||
configfs_dir_cachep = NULL;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Oracle");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.0.1");
|
||||
MODULE_VERSION("0.0.2");
|
||||
MODULE_DESCRIPTION("Simple RAM filesystem for user driven kernel subsystem configuration.");
|
||||
|
||||
module_init(configfs_init);
|
||||
|
@ -277,5 +277,6 @@ struct inode_operations configfs_symlink_inode_operations = {
|
||||
.follow_link = configfs_follow_link,
|
||||
.readlink = generic_readlink,
|
||||
.put_link = configfs_put_link,
|
||||
.setattr = configfs_setattr,
|
||||
};
|
||||
|
||||
|
@ -126,7 +126,7 @@ extern struct config_item *config_group_find_obj(struct config_group *, const ch
|
||||
|
||||
|
||||
struct configfs_attribute {
|
||||
char *ca_name;
|
||||
const char *ca_name;
|
||||
struct module *ca_owner;
|
||||
mode_t ca_mode;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user