diff options
Diffstat (limited to 'fs/configfs/file.c')
| -rw-r--r-- | fs/configfs/file.c | 280 | 
1 files changed, 140 insertions, 140 deletions
diff --git a/fs/configfs/file.c b/fs/configfs/file.c index 61e4db4390a1..fb65b706cc0d 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c @@ -39,40 +39,44 @@ struct configfs_buffer {  	bool			write_in_progress;  	char			*bin_buffer;  	int			bin_buffer_size; +	int			cb_max_size; +	struct config_item	*item; +	struct module		*owner; +	union { +		struct configfs_attribute	*attr; +		struct configfs_bin_attribute	*bin_attr; +	};  }; +static inline struct configfs_fragment *to_frag(struct file *file) +{ +	struct configfs_dirent *sd = file->f_path.dentry->d_fsdata; -/** - *	fill_read_buffer - allocate and fill buffer from item. - *	@dentry:	dentry pointer. - *	@buffer:	data buffer for file. - * - *	Allocate @buffer->page, if it hasn't been already, then call the - *	config_item's show() method to fill the buffer with this attribute's - *	data. - *	This is called only once, on the file's first read. - */ -static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buffer) +	return sd->s_frag; +} + +static int fill_read_buffer(struct file *file, struct configfs_buffer *buffer)  { -	struct configfs_attribute * attr = to_attr(dentry); -	struct config_item * item = to_item(dentry->d_parent); -	int ret = 0; -	ssize_t count; +	struct configfs_fragment *frag = to_frag(file); +	ssize_t count = -ENOENT;  	if (!buffer->page)  		buffer->page = (char *) get_zeroed_page(GFP_KERNEL);  	if (!buffer->page)  		return -ENOMEM; -	count = attr->show(item, buffer->page); - -	BUG_ON(count > (ssize_t)SIMPLE_ATTR_SIZE); -	if (count >= 0) { -		buffer->needs_read_fill = 0; -		buffer->count = count; -	} else -		ret = count; -	return ret; +	down_read(&frag->frag_sem); +	if (!frag->frag_dead) +		count = buffer->attr->show(buffer->item, buffer->page); +	up_read(&frag->frag_sem); + +	if (count < 0) +		return count; +	if (WARN_ON_ONCE(count > (ssize_t)SIMPLE_ATTR_SIZE)) +		return -EIO; +	buffer->needs_read_fill = 0; +	buffer->count = count; +	return 0;  }  /** @@ -97,12 +101,13 @@ static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buf  static ssize_t  configfs_read_file(struct file *file, 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 retval = 0;  	mutex_lock(&buffer->mutex);  	if (buffer->needs_read_fill) { -		if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) +		retval = fill_read_buffer(file, buffer); +		if (retval)  			goto out;  	}  	pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", @@ -138,10 +143,8 @@ static ssize_t  configfs_read_bin_file(struct file *file, char __user *buf,  		       size_t count, loff_t *ppos)  { +	struct configfs_fragment *frag = to_frag(file);  	struct configfs_buffer *buffer = file->private_data; -	struct dentry *dentry = file->f_path.dentry; -	struct config_item *item = to_item(dentry->d_parent); -	struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);  	ssize_t retval = 0;  	ssize_t len = min_t(size_t, count, PAGE_SIZE); @@ -156,14 +159,19 @@ configfs_read_bin_file(struct file *file, char __user *buf,  	if (buffer->needs_read_fill) {  		/* perform first read with buf == NULL to get extent */ -		len = bin_attr->read(item, NULL, 0); +		down_read(&frag->frag_sem); +		if (!frag->frag_dead) +			len = buffer->bin_attr->read(buffer->item, NULL, 0); +		else +			len = -ENOENT; +		up_read(&frag->frag_sem);  		if (len <= 0) {  			retval = len;  			goto out;  		}  		/* do not exceed the maximum value */ -		if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) { +		if (buffer->cb_max_size && len > buffer->cb_max_size) {  			retval = -EFBIG;  			goto out;  		} @@ -176,7 +184,13 @@ configfs_read_bin_file(struct file *file, char __user *buf,  		buffer->bin_buffer_size = len;  		/* perform second read to fill buffer */ -		len = bin_attr->read(item, buffer->bin_buffer, len); +		down_read(&frag->frag_sem); +		if (!frag->frag_dead) +			len = buffer->bin_attr->read(buffer->item, +						     buffer->bin_buffer, len); +		else +			len = -ENOENT; +		up_read(&frag->frag_sem);  		if (len < 0) {  			retval = len;  			vfree(buffer->bin_buffer); @@ -226,25 +240,17 @@ fill_write_buffer(struct configfs_buffer * buffer, const char __user * buf, size  	return error ? -EFAULT : count;  } - -/** - *	flush_write_buffer - push buffer to config_item. - *	@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, - *	passing the buffer that we acquired in fill_write_buffer(). - */ -  static int -flush_write_buffer(struct dentry * dentry, struct configfs_buffer * buffer, size_t count) +flush_write_buffer(struct file *file, struct configfs_buffer *buffer, size_t count)  { -	struct configfs_attribute * attr = to_attr(dentry); -	struct config_item * item = to_item(dentry->d_parent); - -	return attr->store(item, buffer->page, count); +	struct configfs_fragment *frag = to_frag(file); +	int res = -ENOENT; + +	down_read(&frag->frag_sem); +	if (!frag->frag_dead) +		res = buffer->attr->store(buffer->item, buffer->page, count); +	up_read(&frag->frag_sem); +	return res;  } @@ -268,13 +274,13 @@ flush_write_buffer(struct dentry * dentry, struct configfs_buffer * buffer, size  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; +	struct configfs_buffer *buffer = file->private_data;  	ssize_t len;  	mutex_lock(&buffer->mutex);  	len = fill_write_buffer(buffer, buf, count);  	if (len > 0) -		len = flush_write_buffer(file->f_path.dentry, buffer, len); +		len = flush_write_buffer(file, buffer, len);  	if (len > 0)  		*ppos += len;  	mutex_unlock(&buffer->mutex); @@ -299,8 +305,6 @@ configfs_write_bin_file(struct file *file, const char __user *buf,  			size_t count, loff_t *ppos)  {  	struct configfs_buffer *buffer = file->private_data; -	struct dentry *dentry = file->f_path.dentry; -	struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);  	void *tbuf = NULL;  	ssize_t len; @@ -316,8 +320,8 @@ configfs_write_bin_file(struct file *file, const char __user *buf,  	/* buffer grows? */  	if (*ppos + count > buffer->bin_buffer_size) { -		if (bin_attr->cb_max_size && -			*ppos + count > bin_attr->cb_max_size) { +		if (buffer->cb_max_size && +			*ppos + count > buffer->cb_max_size) {  			len = -EFBIG;  			goto out;  		} @@ -349,31 +353,51 @@ out:  	return len;  } -static int check_perm(struct inode * inode, struct file * file, int type) +static int __configfs_open_file(struct inode *inode, struct file *file, int type)  { -	struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); -	struct configfs_attribute * attr = to_attr(file->f_path.dentry); -	struct configfs_bin_attribute *bin_attr = NULL; -	struct configfs_buffer * buffer; -	struct configfs_item_operations * ops = NULL; -	int error = 0; +	struct dentry *dentry = file->f_path.dentry; +	struct configfs_fragment *frag = to_frag(file); +	struct configfs_attribute *attr; +	struct configfs_buffer *buffer; +	int error; -	if (!item || !attr) -		goto Einval; +	error = -ENOMEM; +	buffer = kzalloc(sizeof(struct configfs_buffer), GFP_KERNEL); +	if (!buffer) +		goto out; -	if (type & CONFIGFS_ITEM_BIN_ATTR) -		bin_attr = to_bin_attr(file->f_path.dentry); +	error = -ENOENT; +	down_read(&frag->frag_sem); +	if (unlikely(frag->frag_dead)) +		goto out_free_buffer; -	/* Grab the module reference for this attribute if we have one */ -	if (!try_module_get(attr->ca_owner)) { -		error = -ENODEV; -		goto Done; +	error = -EINVAL; +	buffer->item = to_item(dentry->d_parent); +	if (!buffer->item) +		goto out_free_buffer; + +	attr = to_attr(dentry); +	if (!attr) +		goto out_put_item; + +	if (type & CONFIGFS_ITEM_BIN_ATTR) { +		buffer->bin_attr = to_bin_attr(dentry); +		buffer->cb_max_size = buffer->bin_attr->cb_max_size; +	} else { +		buffer->attr = attr;  	} -	if (item->ci_type) -		ops = item->ci_type->ct_item_ops; -	else -		goto Eaccess; +	buffer->owner = attr->ca_owner; +	/* Grab the module reference for this attribute if we have one */ +	error = -ENODEV; +	if (!try_module_get(buffer->owner)) +		goto out_put_item; + +	error = -EACCES; +	if (!buffer->item->ci_type) +		goto out_put_module; + +	buffer->ops = buffer->item->ci_type->ct_item_ops;  	/* File needs write support.  	 * The inode's perms must say it's ok, @@ -381,13 +405,11 @@ static int check_perm(struct inode * inode, struct file * file, int type)  	 */  	if (file->f_mode & FMODE_WRITE) {  		if (!(inode->i_mode & S_IWUGO)) -			goto Eaccess; - +			goto out_put_module;  		if ((type & CONFIGFS_ITEM_ATTR) && !attr->store) -			goto Eaccess; - -		if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write) -			goto Eaccess; +			goto out_put_module; +		if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->write) +			goto out_put_module;  	}  	/* File needs read support. @@ -396,92 +418,72 @@ static int check_perm(struct inode * inode, struct file * file, int type)  	 */  	if (file->f_mode & FMODE_READ) {  		if (!(inode->i_mode & S_IRUGO)) -			goto Eaccess; - +			goto out_put_module;  		if ((type & CONFIGFS_ITEM_ATTR) && !attr->show) -			goto Eaccess; - -		if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read) -			goto Eaccess; +			goto out_put_module; +		if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->read) +			goto out_put_module;  	} -	/* No error? Great, allocate a buffer for the file, and store it -	 * it in file->private_data for easy access. -	 */ -	buffer = kzalloc(sizeof(struct configfs_buffer),GFP_KERNEL); -	if (!buffer) { -		error = -ENOMEM; -		goto Enomem; -	}  	mutex_init(&buffer->mutex);  	buffer->needs_read_fill = 1;  	buffer->read_in_progress = false;  	buffer->write_in_progress = false; -	buffer->ops = ops;  	file->private_data = buffer; -	goto Done; +	up_read(&frag->frag_sem); +	return 0; - Einval: -	error = -EINVAL; -	goto Done; - Eaccess: -	error = -EACCES; - Enomem: -	module_put(attr->ca_owner); - Done: -	if (error && item) -		config_item_put(item); +out_put_module: +	module_put(buffer->owner); +out_put_item: +	config_item_put(buffer->item); +out_free_buffer: +	up_read(&frag->frag_sem); +	kfree(buffer); +out:  	return error;  }  static int configfs_release(struct inode *inode, struct file *filp)  { -	struct config_item * item = to_item(filp->f_path.dentry->d_parent); -	struct configfs_attribute * attr = to_attr(filp->f_path.dentry); -	struct module * owner = attr->ca_owner; -	struct configfs_buffer * buffer = filp->private_data; - -	if (item) -		config_item_put(item); -	/* After this point, attr should not be accessed. */ -	module_put(owner); - -	if (buffer) { -		if (buffer->page) -			free_page((unsigned long)buffer->page); -		mutex_destroy(&buffer->mutex); -		kfree(buffer); -	} +	struct configfs_buffer *buffer = filp->private_data; + +	module_put(buffer->owner); +	if (buffer->page) +		free_page((unsigned long)buffer->page); +	mutex_destroy(&buffer->mutex); +	kfree(buffer);  	return 0;  }  static int configfs_open_file(struct inode *inode, struct file *filp)  { -	return check_perm(inode, filp, CONFIGFS_ITEM_ATTR); +	return __configfs_open_file(inode, filp, CONFIGFS_ITEM_ATTR);  }  static int configfs_open_bin_file(struct inode *inode, struct file *filp)  { -	return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR); +	return __configfs_open_file(inode, filp, CONFIGFS_ITEM_BIN_ATTR);  } -static int configfs_release_bin_file(struct inode *inode, struct file *filp) +static int configfs_release_bin_file(struct inode *inode, struct file *file)  { -	struct configfs_buffer *buffer = filp->private_data; -	struct dentry *dentry = filp->f_path.dentry; -	struct config_item *item = to_item(dentry->d_parent); -	struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); -	ssize_t len = 0; -	int ret; +	struct configfs_buffer *buffer = file->private_data;  	buffer->read_in_progress = false;  	if (buffer->write_in_progress) { +		struct configfs_fragment *frag = to_frag(file);  		buffer->write_in_progress = false; -		len = bin_attr->write(item, buffer->bin_buffer, -				buffer->bin_buffer_size); - +		down_read(&frag->frag_sem); +		if (!frag->frag_dead) { +			/* result of ->release() is ignored */ +			buffer->bin_attr->write(buffer->item, +					buffer->bin_buffer, +					buffer->bin_buffer_size); +		} +		up_read(&frag->frag_sem);  		/* vfree on NULL is safe */  		vfree(buffer->bin_buffer);  		buffer->bin_buffer = NULL; @@ -489,10 +491,8 @@ static int configfs_release_bin_file(struct inode *inode, struct file *filp)  		buffer->needs_read_fill = 1;  	} -	ret = configfs_release(inode, filp); -	if (len < 0) -		return len; -	return ret; +	configfs_release(inode, file); +	return 0;  } @@ -527,7 +527,7 @@ int configfs_create_file(struct config_item * item, const struct configfs_attrib  	inode_lock_nested(d_inode(dir), I_MUTEX_NORMAL);  	error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode, -				     CONFIGFS_ITEM_ATTR); +				     CONFIGFS_ITEM_ATTR, parent_sd->s_frag);  	inode_unlock(d_inode(dir));  	return error; @@ -549,7 +549,7 @@ int configfs_create_bin_file(struct config_item *item,  	inode_lock_nested(dir->d_inode, I_MUTEX_NORMAL);  	error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode, -				     CONFIGFS_ITEM_BIN_ATTR); +				     CONFIGFS_ITEM_BIN_ATTR, parent_sd->s_frag);  	inode_unlock(dir->d_inode);  	return error;  |