diff options
Diffstat (limited to 'fs/configfs/dir.c')
| -rw-r--r-- | fs/configfs/dir.c | 267 | 
1 files changed, 127 insertions, 140 deletions
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 0b502f80c69..668dcabc569 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -53,29 +53,31 @@ DEFINE_SPINLOCK(configfs_dirent_lock);  static void configfs_d_iput(struct dentry * dentry,  			    struct inode * inode)  { -	struct configfs_dirent * sd = dentry->d_fsdata; +	struct configfs_dirent *sd = dentry->d_fsdata;  	if (sd) { -		BUG_ON(sd->s_dentry != dentry); -		sd->s_dentry = NULL; +		/* Coordinate with configfs_readdir */ +		spin_lock(&configfs_dirent_lock); +		/* Coordinate with configfs_attach_attr where will increase +		 * sd->s_count and update sd->s_dentry to new allocated one. +		 * Only set sd->dentry to null when this dentry is the only +		 * sd owner. +		 * If not do so, configfs_d_iput may run just after +		 * configfs_attach_attr and set sd->s_dentry to null +		 * even it's still in use. +		 */ +		if (atomic_read(&sd->s_count) <= 2) +			sd->s_dentry = NULL; + +		spin_unlock(&configfs_dirent_lock);  		configfs_put(sd);  	}  	iput(inode);  } -/* - * We _must_ delete our dentries on last dput, as the chain-to-parent - * behavior is required to clear the parents of default_groups. - */ -static int configfs_d_delete(struct dentry *dentry) -{ -	return 1; -} - -static const struct dentry_operations configfs_dentry_ops = { +const struct dentry_operations configfs_dentry_ops = {  	.d_iput		= configfs_d_iput, -	/* simple_delete_dentry() isn't exported */ -	.d_delete	= configfs_d_delete, +	.d_delete	= always_delete_dentry,  };  #ifdef CONFIG_LOCKDEP @@ -232,10 +234,8 @@ int configfs_make_dirent(struct configfs_dirent * parent_sd,  	sd->s_mode = mode;  	sd->s_dentry = dentry; -	if (dentry) { +	if (dentry)  		dentry->d_fsdata = configfs_get(sd); -		dentry->d_op = &configfs_dentry_ops; -	}  	return 0;  } @@ -263,11 +263,13 @@ static int init_symlink(struct inode * inode)  	return 0;  } -static int create_dir(struct config_item * k, struct dentry * p, -		      struct dentry * d) +static int create_dir(struct config_item *k, struct dentry *d)  {  	int error;  	umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; +	struct dentry *p = d->d_parent; + +	BUG_ON(!k);  	error = configfs_dirent_exists(p->d_fsdata, d->d_name.name);  	if (!error) @@ -278,7 +280,6 @@ static int create_dir(struct config_item * k, struct dentry * p,  		error = configfs_create(d, mode, init_dir);  		if (!error) {  			inc_nlink(p->d_inode); -			(d)->d_op = &configfs_dentry_ops;  		} else {  			struct configfs_dirent *sd = d->d_fsdata;  			if (sd) { @@ -304,19 +305,7 @@ static int create_dir(struct config_item * k, struct dentry * p,  static int configfs_create_dir(struct config_item * item, struct dentry *dentry)  { -	struct dentry * parent; -	int error = 0; - -	BUG_ON(!item); - -	if (item->ci_parent) -		parent = item->ci_parent->ci_dentry; -	else if (configfs_mount && configfs_mount->mnt_sb) -		parent = configfs_mount->mnt_sb->s_root; -	else -		return -EFAULT; - -	error = create_dir(item,parent,dentry); +	int error = create_dir(item, dentry);  	if (!error)  		item->ci_dentry = dentry;  	return error; @@ -371,9 +360,7 @@ int configfs_create_link(struct configfs_symlink *sl,  				   CONFIGFS_ITEM_LINK);  	if (!err) {  		err = configfs_create(dentry, mode, init_symlink); -		if (!err) -			dentry->d_op = &configfs_dentry_ops; -		else { +		if (err) {  			struct configfs_dirent *sd = dentry->d_fsdata;  			if (sd) {  				spin_lock(&configfs_dirent_lock); @@ -399,8 +386,7 @@ static void remove_dir(struct dentry * d)  	if (d->d_inode)  		simple_rmdir(parent->d_inode,d); -	pr_debug(" o %s removing done (%d)\n",d->d_name.name, -		 atomic_read(&d->d_count)); +	pr_debug(" o %s removing done (%d)\n",d->d_name.name, d_count(d));  	dput(parent);  } @@ -439,8 +425,11 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den  	struct configfs_attribute * attr = sd->s_element;  	int error; +	spin_lock(&configfs_dirent_lock);  	dentry->d_fsdata = configfs_get(sd);  	sd->s_dentry = dentry; +	spin_unlock(&configfs_dirent_lock); +  	error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG,  				configfs_init_file);  	if (error) { @@ -448,7 +437,6 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den  		return error;  	} -	dentry->d_op = &configfs_dentry_ops;  	d_rehash(dentry);  	return 0; @@ -456,7 +444,7 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den  static struct dentry * configfs_lookup(struct inode *dir,  				       struct dentry *dentry, -				       struct nameidata *nd) +				       unsigned int flags)  {  	struct configfs_dirent * parent_sd = dentry->d_parent->d_fsdata;  	struct configfs_dirent * sd; @@ -493,7 +481,10 @@ static struct dentry * configfs_lookup(struct inode *dir,  		 * If it doesn't exist and it isn't a NOT_PINNED item,  		 * it must be negative.  		 */ -		return simple_lookup(dir, dentry, nd); +		if (dentry->d_name.len > NAME_MAX) +			return ERR_PTR(-ENAMETOOLONG); +		d_add(dentry, NULL); +		return NULL;  	}  out: @@ -671,19 +662,15 @@ static int create_default_group(struct config_group *parent_group,  				struct config_group *group)  {  	int ret; -	struct qstr name;  	struct configfs_dirent *sd;  	/* We trust the caller holds a reference to parent */  	struct dentry *child, *parent = parent_group->cg_item.ci_dentry;  	if (!group->cg_item.ci_name)  		group->cg_item.ci_name = group->cg_item.ci_namebuf; -	name.name = group->cg_item.ci_name; -	name.len = strlen(name.name); -	name.hash = full_name_hash(name.name, name.len);  	ret = -ENOMEM; -	child = d_alloc(parent, &name); +	child = d_alloc_name(parent, group->cg_item.ci_name);  	if (child) {  		d_add(child, NULL); @@ -693,7 +680,8 @@ static int create_default_group(struct config_group *parent_group,  			sd = child->d_fsdata;  			sd->s_type |= CONFIGFS_USET_DEFAULT;  		} else { -			d_delete(child); +			BUG_ON(child->d_inode); +			d_drop(child);  			dput(child);  		}  	} @@ -952,9 +940,9 @@ static void client_drop_item(struct config_item *parent_item,  #ifdef DEBUG  static void configfs_dump_one(struct configfs_dirent *sd, int level)  { -	printk(KERN_INFO "%*s\"%s\":\n", level, " ", configfs_get_name(sd)); +	pr_info("%*s\"%s\":\n", level, " ", configfs_get_name(sd)); -#define type_print(_type) if (sd->s_type & _type) printk(KERN_INFO "%*s %s\n", level, " ", #_type); +#define type_print(_type) if (sd->s_type & _type) pr_info("%*s %s\n", level, " ", #_type);  	type_print(CONFIGFS_ROOT);  	type_print(CONFIGFS_DIR);  	type_print(CONFIGFS_ITEM_ATTR); @@ -994,7 +982,7 @@ static int configfs_dump(struct configfs_dirent *sd, int level)   * This describes these functions and their helpers.   *   * Allow another kernel system to depend on a config_item.  If this - * happens, the item cannot go away until the dependant can live without + * happens, the item cannot go away until the dependent can live without   * it.  The idea is to give client modules as simple an interface as   * possible.  When a system asks them to depend on an item, they just   * call configfs_depend_item().  If the item is live and the client @@ -1047,10 +1035,11 @@ static int configfs_dump(struct configfs_dirent *sd, int level)  static int configfs_depend_prep(struct dentry *origin,  				struct config_item *target)  { -	struct configfs_dirent *child_sd, *sd = origin->d_fsdata; +	struct configfs_dirent *child_sd, *sd;  	int ret = 0; -	BUG_ON(!origin || !sd); +	BUG_ON(!origin || !origin->d_fsdata); +	sd = origin->d_fsdata;  	if (sd->s_element == target)  /* Boo-yah */  		goto out; @@ -1079,23 +1068,24 @@ int configfs_depend_item(struct configfs_subsystem *subsys,  	int ret;  	struct configfs_dirent *p, *root_sd, *subsys_sd = NULL;  	struct config_item *s_item = &subsys->su_group.cg_item; +	struct dentry *root;  	/*  	 * Pin the configfs filesystem.  This means we can safely access  	 * the root of the configfs filesystem.  	 */ -	ret = configfs_pin_fs(); -	if (ret) -		return ret; +	root = configfs_pin_fs(); +	if (IS_ERR(root)) +		return PTR_ERR(root);  	/*  	 * Next, lock the root directory.  We're going to check that the  	 * subsystem is really registered, and so we need to lock out  	 * configfs_[un]register_subsystem().  	 */ -	mutex_lock(&configfs_sb->s_root->d_inode->i_mutex); +	mutex_lock(&root->d_inode->i_mutex); -	root_sd = configfs_sb->s_root->d_fsdata; +	root_sd = root->d_fsdata;  	list_for_each_entry(p, &root_sd->s_children, s_sibling) {  		if (p->s_type & CONFIGFS_DIR) { @@ -1129,7 +1119,7 @@ int configfs_depend_item(struct configfs_subsystem *subsys,  out_unlock_dirent_lock:  	spin_unlock(&configfs_dirent_lock);  out_unlock_fs: -	mutex_unlock(&configfs_sb->s_root->d_inode->i_mutex); +	mutex_unlock(&root->d_inode->i_mutex);  	/*  	 * If we succeeded, the fs is pinned via other methods.  If not, @@ -1170,7 +1160,7 @@ void configfs_undepend_item(struct configfs_subsystem *subsys,  }  EXPORT_SYMBOL(configfs_undepend_item); -static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +static int configfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)  {  	int ret = 0;  	int module_got = 0; @@ -1183,11 +1173,6 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)  	struct module *subsys_owner = NULL, *new_item_owner = NULL;  	char *name; -	if (dentry->d_parent == configfs_sb->s_root) { -		ret = -EPERM; -		goto out; -	} -  	sd = dentry->d_parent->d_fsdata;  	/* @@ -1359,9 +1344,6 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)  	struct module *subsys_owner = NULL, *dead_item_owner = NULL;  	int ret; -	if (dentry->d_parent == configfs_sb->s_root) -		return -EPERM; -  	sd = dentry->d_fsdata;  	if (sd->s_type & CONFIGFS_USET_DEFAULT)  		return -EPERM; @@ -1459,6 +1441,11 @@ const struct inode_operations configfs_dir_inode_operations = {  	.setattr	= configfs_setattr,  }; +const struct inode_operations configfs_root_inode_operations = { +	.lookup		= configfs_lookup, +	.setattr	= configfs_setattr, +}; +  #if 0  int configfs_rename_dir(struct config_item * item, const char *new_name)  { @@ -1543,80 +1530,83 @@ static inline unsigned char dt_type(struct configfs_dirent *sd)  	return (sd->s_mode >> 12) & 15;  } -static int configfs_readdir(struct file * filp, void * dirent, filldir_t filldir) +static int configfs_readdir(struct file *file, struct dir_context *ctx)  { -	struct dentry *dentry = filp->f_path.dentry; +	struct dentry *dentry = file->f_path.dentry; +	struct super_block *sb = dentry->d_sb;  	struct configfs_dirent * parent_sd = dentry->d_fsdata; -	struct configfs_dirent *cursor = filp->private_data; +	struct configfs_dirent *cursor = file->private_data;  	struct list_head *p, *q = &cursor->s_sibling; -	ino_t ino; -	int i = filp->f_pos; +	ino_t ino = 0; -	switch (i) { -		case 0: -			ino = dentry->d_inode->i_ino; -			if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) -				break; -			filp->f_pos++; -			i++; -			/* fallthrough */ -		case 1: -			ino = parent_ino(dentry); -			if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) -				break; -			filp->f_pos++; -			i++; -			/* fallthrough */ -		default: -			if (filp->f_pos == 2) { -				spin_lock(&configfs_dirent_lock); -				list_move(q, &parent_sd->s_children); -				spin_unlock(&configfs_dirent_lock); -			} -			for (p=q->next; p!= &parent_sd->s_children; p=p->next) { -				struct configfs_dirent *next; -				const char * name; -				int len; +	if (!dir_emit_dots(file, ctx)) +		return 0; +	if (ctx->pos == 2) { +		spin_lock(&configfs_dirent_lock); +		list_move(q, &parent_sd->s_children); +		spin_unlock(&configfs_dirent_lock); +	} +	for (p = q->next; p != &parent_sd->s_children; p = p->next) { +		struct configfs_dirent *next; +		const char *name; +		int len; +		struct inode *inode = NULL; + +		next = list_entry(p, struct configfs_dirent, s_sibling); +		if (!next->s_element) +			continue; -				next = list_entry(p, struct configfs_dirent, -						   s_sibling); -				if (!next->s_element) -					continue; +		name = configfs_get_name(next); +		len = strlen(name); -				name = configfs_get_name(next); -				len = strlen(name); -				if (next->s_dentry) -					ino = next->s_dentry->d_inode->i_ino; -				else -					ino = iunique(configfs_sb, 2); +		/* +		 * We'll have a dentry and an inode for +		 * PINNED items and for open attribute +		 * files.  We lock here to prevent a race +		 * with configfs_d_iput() clearing +		 * s_dentry before calling iput(). +		 * +		 * Why do we go to the trouble?  If +		 * someone has an attribute file open, +		 * the inode number should match until +		 * they close it.  Beyond that, we don't +		 * care. +		 */ +		spin_lock(&configfs_dirent_lock); +		dentry = next->s_dentry; +		if (dentry) +			inode = dentry->d_inode; +		if (inode) +			ino = inode->i_ino; +		spin_unlock(&configfs_dirent_lock); +		if (!inode) +			ino = iunique(sb, 2); -				if (filldir(dirent, name, len, filp->f_pos, ino, -						 dt_type(next)) < 0) -					return 0; +		if (!dir_emit(ctx, name, len, ino, dt_type(next))) +			return 0; -				spin_lock(&configfs_dirent_lock); -				list_move(q, p); -				spin_unlock(&configfs_dirent_lock); -				p = q; -				filp->f_pos++; -			} +		spin_lock(&configfs_dirent_lock); +		list_move(q, p); +		spin_unlock(&configfs_dirent_lock); +		p = q; +		ctx->pos++;  	}  	return 0;  } -static loff_t configfs_dir_lseek(struct file * file, loff_t offset, int origin) +static loff_t configfs_dir_lseek(struct file *file, loff_t offset, int whence)  {  	struct dentry * dentry = file->f_path.dentry;  	mutex_lock(&dentry->d_inode->i_mutex); -	switch (origin) { +	switch (whence) {  		case 1:  			offset += file->f_pos;  		case 0:  			if (offset >= 0)  				break;  		default: -			mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); +			mutex_unlock(&file_inode(file)->i_mutex);  			return -EINVAL;  	}  	if (offset != file->f_pos) { @@ -1651,43 +1641,39 @@ const struct file_operations configfs_dir_operations = {  	.release	= configfs_dir_close,  	.llseek		= configfs_dir_lseek,  	.read		= generic_read_dir, -	.readdir	= configfs_readdir, +	.iterate	= configfs_readdir,  };  int configfs_register_subsystem(struct configfs_subsystem *subsys)  {  	int err;  	struct config_group *group = &subsys->su_group; -	struct qstr name;  	struct dentry *dentry; +	struct dentry *root;  	struct configfs_dirent *sd; -	err = configfs_pin_fs(); -	if (err) -		return err; +	root = configfs_pin_fs(); +	if (IS_ERR(root)) +		return PTR_ERR(root);  	if (!group->cg_item.ci_name)  		group->cg_item.ci_name = group->cg_item.ci_namebuf; -	sd = configfs_sb->s_root->d_fsdata; +	sd = root->d_fsdata;  	link_group(to_config_group(sd->s_element), group); -	mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, -			I_MUTEX_PARENT); - -	name.name = group->cg_item.ci_name; -	name.len = strlen(name.name); -	name.hash = full_name_hash(name.name, name.len); +	mutex_lock_nested(&root->d_inode->i_mutex, I_MUTEX_PARENT);  	err = -ENOMEM; -	dentry = d_alloc(configfs_sb->s_root, &name); +	dentry = d_alloc_name(root, group->cg_item.ci_name);  	if (dentry) {  		d_add(dentry, NULL);  		err = configfs_attach_group(sd->s_element, &group->cg_item,  					    dentry);  		if (err) { -			d_delete(dentry); +			BUG_ON(dentry->d_inode); +			d_drop(dentry);  			dput(dentry);  		} else {  			spin_lock(&configfs_dirent_lock); @@ -1696,7 +1682,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)  		}  	} -	mutex_unlock(&configfs_sb->s_root->d_inode->i_mutex); +	mutex_unlock(&root->d_inode->i_mutex);  	if (err) {  		unlink_group(group); @@ -1710,19 +1696,20 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)  {  	struct config_group *group = &subsys->su_group;  	struct dentry *dentry = group->cg_item.ci_dentry; +	struct dentry *root = dentry->d_sb->s_root; -	if (dentry->d_parent != configfs_sb->s_root) { -		printk(KERN_ERR "configfs: Tried to unregister non-subsystem!\n"); +	if (dentry->d_parent != root) { +		pr_err("Tried to unregister non-subsystem!\n");  		return;  	} -	mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, +	mutex_lock_nested(&root->d_inode->i_mutex,  			  I_MUTEX_PARENT);  	mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD);  	mutex_lock(&configfs_symlink_mutex);  	spin_lock(&configfs_dirent_lock);  	if (configfs_detach_prep(dentry, NULL)) { -		printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); +		pr_err("Tried to unregister non-empty subsystem!\n");  	}  	spin_unlock(&configfs_dirent_lock);  	mutex_unlock(&configfs_symlink_mutex); @@ -1733,7 +1720,7 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)  	d_delete(dentry); -	mutex_unlock(&configfs_sb->s_root->d_inode->i_mutex); +	mutex_unlock(&root->d_inode->i_mutex);  	dput(dentry);  | 
