diff options
Diffstat (limited to 'fs/tracefs/inode.c')
| -rw-r--r-- | fs/tracefs/inode.c | 76 | 
1 files changed, 76 insertions, 0 deletions
| diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 925a621b432e..3616839c5c4b 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -161,6 +161,77 @@ struct tracefs_fs_info {  	struct tracefs_mount_opts mount_opts;  }; +static void change_gid(struct dentry *dentry, kgid_t gid) +{ +	if (!dentry->d_inode) +		return; +	dentry->d_inode->i_gid = gid; +} + +/* + * Taken from d_walk, but without he need for handling renames. + * Nothing can be renamed while walking the list, as tracefs + * does not support renames. This is only called when mounting + * or remounting the file system, to set all the files to + * the given gid. + */ +static void set_gid(struct dentry *parent, kgid_t gid) +{ +	struct dentry *this_parent; +	struct list_head *next; + +	this_parent = parent; +	spin_lock(&this_parent->d_lock); + +	change_gid(this_parent, gid); +repeat: +	next = this_parent->d_subdirs.next; +resume: +	while (next != &this_parent->d_subdirs) { +		struct list_head *tmp = next; +		struct dentry *dentry = list_entry(tmp, struct dentry, d_child); +		next = tmp->next; + +		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + +		change_gid(dentry, gid); + +		if (!list_empty(&dentry->d_subdirs)) { +			spin_unlock(&this_parent->d_lock); +			spin_release(&dentry->d_lock.dep_map, _RET_IP_); +			this_parent = dentry; +			spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_); +			goto repeat; +		} +		spin_unlock(&dentry->d_lock); +	} +	/* +	 * All done at this level ... ascend and resume the search. +	 */ +	rcu_read_lock(); +ascend: +	if (this_parent != parent) { +		struct dentry *child = this_parent; +		this_parent = child->d_parent; + +		spin_unlock(&child->d_lock); +		spin_lock(&this_parent->d_lock); + +		/* go into the first sibling still alive */ +		do { +			next = child->d_child.next; +			if (next == &this_parent->d_subdirs) +				goto ascend; +			child = list_entry(next, struct dentry, d_child); +		} while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)); +		rcu_read_unlock(); +		goto resume; +	} +	rcu_read_unlock(); +	spin_unlock(&this_parent->d_lock); +	return; +} +  static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)  {  	substring_t args[MAX_OPT_ARGS]; @@ -193,6 +264,7 @@ static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)  			if (!gid_valid(gid))  				return -EINVAL;  			opts->gid = gid; +			set_gid(tracefs_mount->mnt_root, gid);  			break;  		case Opt_mode:  			if (match_octal(&args[0], &option)) @@ -414,6 +486,8 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,  	inode->i_mode = mode;  	inode->i_fop = fops ? fops : &tracefs_file_operations;  	inode->i_private = data; +	inode->i_uid = d_inode(dentry->d_parent)->i_uid; +	inode->i_gid = d_inode(dentry->d_parent)->i_gid;  	d_instantiate(dentry, inode);  	fsnotify_create(dentry->d_parent->d_inode, dentry);  	return end_creating(dentry); @@ -436,6 +510,8 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent,  	inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP;  	inode->i_op = ops;  	inode->i_fop = &simple_dir_operations; +	inode->i_uid = d_inode(dentry->d_parent)->i_uid; +	inode->i_gid = d_inode(dentry->d_parent)->i_gid;  	/* directory inodes start off with i_nlink == 2 (for "." entry) */  	inc_nlink(inode); |