Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d0f89bb authored by Joel Becker's avatar Joel Becker Committed by Mark Fasheh
Browse files

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: default avatarJoel Becker <joel.becker@oracle.com>
Signed-off-by: default avatarMark Fasheh <mark.fasheh@oracle.com>
parent 62ca3d26
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -320,6 +320,7 @@ static struct config_item_type simple_children_type = {
	.ct_item_ops	= &simple_children_item_ops,
	.ct_item_ops	= &simple_children_item_ops,
	.ct_group_ops	= &simple_children_group_ops,
	.ct_group_ops	= &simple_children_group_ops,
	.ct_attrs	= simple_children_attrs,
	.ct_attrs	= simple_children_attrs,
	.ct_owner	= THIS_MODULE,
};
};


static struct configfs_subsystem simple_children_subsys = {
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_item_ops	= &group_children_item_ops,
	.ct_group_ops	= &group_children_group_ops,
	.ct_group_ops	= &group_children_group_ops,
	.ct_attrs	= group_children_attrs,
	.ct_attrs	= group_children_attrs,
	.ct_owner	= THIS_MODULE,
};
};


static struct configfs_subsystem group_children_subsys = {
static struct configfs_subsystem group_children_subsys = {
+8 −3
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ struct configfs_dirent {
	int			s_type;
	int			s_type;
	umode_t			s_mode;
	umode_t			s_mode;
	struct dentry		* s_dentry;
	struct dentry		* s_dentry;
	struct iattr		* s_iattr;
};
};


#define CONFIGFS_ROOT		0x0001
#define CONFIGFS_ROOT		0x0001
@@ -48,10 +49,11 @@ struct configfs_dirent {
#define CONFIGFS_NOT_PINNED	(CONFIGFS_ITEM_ATTR)
#define CONFIGFS_NOT_PINNED	(CONFIGFS_ITEM_ATTR)


extern struct vfsmount * configfs_mount;
extern struct vfsmount * configfs_mount;
extern kmem_cache_t *configfs_dir_cachep;


extern int configfs_is_root(struct config_item *item);
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(struct dentry *, int mode, int (*init)(struct inode *));


extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
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 const unsigned char * configfs_get_name(struct configfs_dirent *sd);
extern void configfs_drop_dentry(struct configfs_dirent *sd, struct dentry *parent);
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 int configfs_pin_fs(void);
extern void configfs_release_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)
static inline void release_configfs_dirent(struct configfs_dirent * sd)
{
{
	if (!(sd->s_type & CONFIGFS_ROOT))
	if (!(sd->s_type & CONFIGFS_ROOT)) {
		kfree(sd);
		kfree(sd->s_iattr);
		kmem_cache_free(configfs_dir_cachep, sd);
	}
}
}


static inline struct configfs_dirent * configfs_get(struct configfs_dirent * sd)
static inline struct configfs_dirent * configfs_get(struct configfs_dirent * sd)
+26 −10
Original line number Original line Diff line number Diff line
@@ -72,7 +72,7 @@ static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent * pare
{
{
	struct configfs_dirent * sd;
	struct configfs_dirent * sd;


	sd = kmalloc(sizeof(*sd), GFP_KERNEL);
	sd = kmem_cache_alloc(configfs_dir_cachep, GFP_KERNEL);
	if (!sd)
	if (!sd)
		return NULL;
		return NULL;


@@ -136,13 +136,19 @@ static int create_dir(struct config_item * k, struct dentry * p,
	int error;
	int error;
	umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
	umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;


	error = configfs_create(d, mode, init_dir);
	if (!error) {
	error = configfs_make_dirent(p->d_fsdata, d, k, mode,
	error = configfs_make_dirent(p->d_fsdata, d, k, mode,
				     CONFIGFS_DIR);
				     CONFIGFS_DIR);
	if (!error) {
		error = configfs_create(d, mode, init_dir);
		if (!error) {
		if (!error) {
			p->d_inode->i_nlink++;
			p->d_inode->i_nlink++;
			(d)->d_op = &configfs_dentry_ops;
			(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;
	return error;
@@ -182,12 +188,19 @@ int configfs_create_link(struct configfs_symlink *sl,
	int err = 0;
	int err = 0;
	umode_t mode = S_IFLNK | S_IRWXUGO;
	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) {
	if (!err) {
		err = configfs_make_dirent(parent->d_fsdata, dentry, sl,
		err = configfs_create(dentry, mode, init_symlink);
					 mode, CONFIGFS_ITEM_LINK);
		if (!err)
		if (!err)
			dentry->d_op = &configfs_dentry_ops;
			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;
	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;
	struct configfs_attribute * attr = sd->s_element;
	int error;
	int error;


	dentry->d_fsdata = configfs_get(sd);
	sd->s_dentry = dentry;
	error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, init_file);
	error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, init_file);
	if (error)
	if (error) {
		configfs_put(sd);
		return error;
		return error;
	}


	dentry->d_op = &configfs_dentry_ops;
	dentry->d_op = &configfs_dentry_ops;
	dentry->d_fsdata = configfs_get(sd);
	sd->s_dentry = dentry;
	d_rehash(dentry);
	d_rehash(dentry);


	return 0;
	return 0;
@@ -839,6 +854,7 @@ struct inode_operations configfs_dir_inode_operations = {
	.symlink	= configfs_symlink,
	.symlink	= configfs_symlink,
	.unlink		= configfs_unlink,
	.unlink		= configfs_unlink,
	.lookup		= configfs_lookup,
	.lookup		= configfs_lookup,
	.setattr	= configfs_setattr,
};
};


#if 0
#if 0
+10 −9
Original line number Original line Diff line number Diff line
@@ -26,7 +26,6 @@


#include <linux/fs.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/dnotify.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <asm/semaphore.h>
@@ -150,7 +149,7 @@ configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *pp
/**
/**
 *	fill_write_buffer - copy buffer from userspace.
 *	fill_write_buffer - copy buffer from userspace.
 *	@buffer:	data buffer for file.
 *	@buffer:	data buffer for file.
 *	@userbuf:	data from user.
 *	@buf:		data from user.
 *	@count:		number of bytes in @userbuf.
 *	@count:		number of bytes in @userbuf.
 *
 *
 *	Allocate @buffer->page if it hasn't been already, then
 *	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.
 *	flush_write_buffer - push buffer to config_item.
 *	@file:		file pointer.
 *	@dentry:	dentry to the attribute
 *	@buffer:	data buffer for file.
 *	@buffer:	data buffer for file.
 *	@count:		number of bytes
 *
 *
 *	Get the correct pointers for the config_item and the attribute we're
 *	Get the correct pointers for the config_item and the attribute we're
 *	dealing with, then call the store() method for the attribute,
 *	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)
configfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
{
	struct configfs_buffer * buffer = file->private_data;
	struct configfs_buffer * buffer = file->private_data;
	ssize_t len;


	down(&buffer->sem);
	down(&buffer->sem);
	count = fill_write_buffer(buffer,buf,count);
	len = fill_write_buffer(buffer, buf, count);
	if (count > 0)
	if (len > 0)
		count = flush_write_buffer(file->f_dentry,buffer,count);
		len = flush_write_buffer(file->f_dentry, buffer, count);
	if (count > 0)
	if (len > 0)
		*ppos += count;
		*ppos += len;
	up(&buffer->sem);
	up(&buffer->sem);
	return count;
	return len;
}
}


static int check_perm(struct inode * inode, struct file * file)
static int check_perm(struct inode * inode, struct file * file)
+108 −9
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@
#include <linux/pagemap.h>
#include <linux/pagemap.h>
#include <linux/namei.h>
#include <linux/namei.h>
#include <linux/backing-dev.h>
#include <linux/backing-dev.h>
#include <linux/capability.h>


#include <linux/configfs.h>
#include <linux/configfs.h>
#include "configfs_internal.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,
	.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)
{
{
	struct inode * inode = new_inode(configfs_sb);
	if (inode) {
	inode->i_mode = mode;
	inode->i_mode = mode;
	inode->i_uid = 0;
	inode->i_uid = 0;
	inode->i_gid = 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_blksize = PAGE_CACHE_SIZE;
		inode->i_blksize = PAGE_CACHE_SIZE;
		inode->i_blocks = 0;
		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->a_ops = &configfs_aops;
		inode->i_mapping->backing_dev_info = &configfs_backing_dev_info;
		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;
	return inode;
}
}
@@ -70,7 +160,8 @@ int configfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *
	struct inode * inode = NULL;
	struct inode * inode = NULL;
	if (dentry) {
	if (dentry) {
		if (!dentry->d_inode) {
		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) {
				if (dentry->d_parent && dentry->d_parent->d_inode) {
					struct inode *p_inode = dentry->d_parent->d_inode;
					struct inode *p_inode = dentry->d_parent->d_inode;
					p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME;
					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)
const unsigned char * configfs_get_name(struct configfs_dirent *sd)
{
{
	struct attribute * attr;
	struct configfs_attribute *attr;


	if (!sd || !sd->s_element)
	if (!sd || !sd->s_element)
		BUG();
		BUG();
@@ -114,7 +205,7 @@ const unsigned char * configfs_get_name(struct configfs_dirent *sd)


	if (sd->s_type & CONFIGFS_ITEM_ATTR) {
	if (sd->s_type & CONFIGFS_ITEM_ATTR) {
		attr = sd->s_element;
		attr = sd->s_element;
		return attr->name;
		return attr->ca_name;
	}
	}
	return NULL;
	return NULL;
}
}
@@ -130,21 +221,29 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)


	if (dentry) {
	if (dentry) {
		spin_lock(&dcache_lock);
		spin_lock(&dcache_lock);
		spin_lock(&dentry->d_lock);
		if (!(d_unhashed(dentry) && dentry->d_inode)) {
		if (!(d_unhashed(dentry) && dentry->d_inode)) {
			dget_locked(dentry);
			dget_locked(dentry);
			__d_drop(dentry);
			__d_drop(dentry);
			spin_unlock(&dentry->d_lock);
			spin_unlock(&dcache_lock);
			spin_unlock(&dcache_lock);
			simple_unlink(parent->d_inode, dentry);
			simple_unlink(parent->d_inode, dentry);
		} else
		} else {
			spin_unlock(&dentry->d_lock);
			spin_unlock(&dcache_lock);
			spin_unlock(&dcache_lock);
		}
		}
	}
	}
}


void configfs_hash_and_remove(struct dentry * dir, const char * name)
void configfs_hash_and_remove(struct dentry * dir, const char * name)
{
{
	struct configfs_dirent * sd;
	struct configfs_dirent * sd;
	struct configfs_dirent * parent_sd = dir->d_fsdata;
	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);
	mutex_lock(&dir->d_inode->i_mutex);
	list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
	list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
		if (!sd->s_element)
		if (!sd->s_element)
Loading