18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* -*- mode: c; c-basic-offset: 8; -*-
38c2ecf20Sopenharmony_ci * vim: noexpandtab sw=8 ts=8 sts=0:
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * file.c - operations for regular (text) files.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on sysfs:
88c2ecf20Sopenharmony_ci * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * configfs Copyright (C) 2005 Oracle.  All rights reserved.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/mutex.h>
178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
188c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/configfs.h>
218c2ecf20Sopenharmony_ci#include "configfs_internal.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * A simple attribute can only be 4096 characters.  Why 4k?  Because the
258c2ecf20Sopenharmony_ci * original code limited it to PAGE_SIZE.  That's a bad idea, though,
268c2ecf20Sopenharmony_ci * because an attribute of 16k on ia64 won't work on x86.  So we limit to
278c2ecf20Sopenharmony_ci * 4k, our minimum common page size.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#define SIMPLE_ATTR_SIZE 4096
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct configfs_buffer {
328c2ecf20Sopenharmony_ci	size_t			count;
338c2ecf20Sopenharmony_ci	loff_t			pos;
348c2ecf20Sopenharmony_ci	char			* page;
358c2ecf20Sopenharmony_ci	struct configfs_item_operations	* ops;
368c2ecf20Sopenharmony_ci	struct mutex		mutex;
378c2ecf20Sopenharmony_ci	int			needs_read_fill;
388c2ecf20Sopenharmony_ci	bool			read_in_progress;
398c2ecf20Sopenharmony_ci	bool			write_in_progress;
408c2ecf20Sopenharmony_ci	char			*bin_buffer;
418c2ecf20Sopenharmony_ci	int			bin_buffer_size;
428c2ecf20Sopenharmony_ci	int			cb_max_size;
438c2ecf20Sopenharmony_ci	struct config_item	*item;
448c2ecf20Sopenharmony_ci	struct module		*owner;
458c2ecf20Sopenharmony_ci	union {
468c2ecf20Sopenharmony_ci		struct configfs_attribute	*attr;
478c2ecf20Sopenharmony_ci		struct configfs_bin_attribute	*bin_attr;
488c2ecf20Sopenharmony_ci	};
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic inline struct configfs_fragment *to_frag(struct file *file)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct configfs_dirent *sd = file->f_path.dentry->d_fsdata;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return sd->s_frag;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int fill_read_buffer(struct file *file, struct configfs_buffer *buffer)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct configfs_fragment *frag = to_frag(file);
618c2ecf20Sopenharmony_ci	ssize_t count = -ENOENT;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (!buffer->page)
648c2ecf20Sopenharmony_ci		buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
658c2ecf20Sopenharmony_ci	if (!buffer->page)
668c2ecf20Sopenharmony_ci		return -ENOMEM;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	down_read(&frag->frag_sem);
698c2ecf20Sopenharmony_ci	if (!frag->frag_dead)
708c2ecf20Sopenharmony_ci		count = buffer->attr->show(buffer->item, buffer->page);
718c2ecf20Sopenharmony_ci	up_read(&frag->frag_sem);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (count < 0)
748c2ecf20Sopenharmony_ci		return count;
758c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(count > (ssize_t)SIMPLE_ATTR_SIZE))
768c2ecf20Sopenharmony_ci		return -EIO;
778c2ecf20Sopenharmony_ci	buffer->needs_read_fill = 0;
788c2ecf20Sopenharmony_ci	buffer->count = count;
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/**
838c2ecf20Sopenharmony_ci *	configfs_read_file - read an attribute.
848c2ecf20Sopenharmony_ci *	@file:	file pointer.
858c2ecf20Sopenharmony_ci *	@buf:	buffer to fill.
868c2ecf20Sopenharmony_ci *	@count:	number of bytes to read.
878c2ecf20Sopenharmony_ci *	@ppos:	starting offset in file.
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci *	Userspace wants to read an attribute file. The attribute descriptor
908c2ecf20Sopenharmony_ci *	is in the file's ->d_fsdata. The target item is in the directory's
918c2ecf20Sopenharmony_ci *	->d_fsdata.
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci *	We call fill_read_buffer() to allocate and fill the buffer from the
948c2ecf20Sopenharmony_ci *	item's show() method exactly once (if the read is happening from
958c2ecf20Sopenharmony_ci *	the beginning of the file). That should fill the entire buffer with
968c2ecf20Sopenharmony_ci *	all the data the item has to offer for that attribute.
978c2ecf20Sopenharmony_ci *	We then call flush_read_buffer() to copy the buffer to userspace
988c2ecf20Sopenharmony_ci *	in the increments specified.
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic ssize_t
1028c2ecf20Sopenharmony_ciconfigfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct configfs_buffer *buffer = file->private_data;
1058c2ecf20Sopenharmony_ci	ssize_t retval = 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	mutex_lock(&buffer->mutex);
1088c2ecf20Sopenharmony_ci	if (buffer->needs_read_fill) {
1098c2ecf20Sopenharmony_ci		retval = fill_read_buffer(file, buffer);
1108c2ecf20Sopenharmony_ci		if (retval)
1118c2ecf20Sopenharmony_ci			goto out;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci	pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",
1148c2ecf20Sopenharmony_ci		 __func__, count, *ppos, buffer->page);
1158c2ecf20Sopenharmony_ci	retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
1168c2ecf20Sopenharmony_ci					 buffer->count);
1178c2ecf20Sopenharmony_ciout:
1188c2ecf20Sopenharmony_ci	mutex_unlock(&buffer->mutex);
1198c2ecf20Sopenharmony_ci	return retval;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/**
1238c2ecf20Sopenharmony_ci *	configfs_read_bin_file - read a binary attribute.
1248c2ecf20Sopenharmony_ci *	@file:	file pointer.
1258c2ecf20Sopenharmony_ci *	@buf:	buffer to fill.
1268c2ecf20Sopenharmony_ci *	@count:	number of bytes to read.
1278c2ecf20Sopenharmony_ci *	@ppos:	starting offset in file.
1288c2ecf20Sopenharmony_ci *
1298c2ecf20Sopenharmony_ci *	Userspace wants to read a binary attribute file. The attribute
1308c2ecf20Sopenharmony_ci *	descriptor is in the file's ->d_fsdata. The target item is in the
1318c2ecf20Sopenharmony_ci *	directory's ->d_fsdata.
1328c2ecf20Sopenharmony_ci *
1338c2ecf20Sopenharmony_ci *	We check whether we need to refill the buffer. If so we will
1348c2ecf20Sopenharmony_ci *	call the attributes' attr->read() twice. The first time we
1358c2ecf20Sopenharmony_ci *	will pass a NULL as a buffer pointer, which the attributes' method
1368c2ecf20Sopenharmony_ci *	will use to return the size of the buffer required. If no error
1378c2ecf20Sopenharmony_ci *	occurs we will allocate the buffer using vmalloc and call
1388c2ecf20Sopenharmony_ci *	attr->read() again passing that buffer as an argument.
1398c2ecf20Sopenharmony_ci *	Then we just copy to user-space using simple_read_from_buffer.
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic ssize_t
1438c2ecf20Sopenharmony_ciconfigfs_read_bin_file(struct file *file, char __user *buf,
1448c2ecf20Sopenharmony_ci		       size_t count, loff_t *ppos)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct configfs_fragment *frag = to_frag(file);
1478c2ecf20Sopenharmony_ci	struct configfs_buffer *buffer = file->private_data;
1488c2ecf20Sopenharmony_ci	ssize_t retval = 0;
1498c2ecf20Sopenharmony_ci	ssize_t len = min_t(size_t, count, PAGE_SIZE);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	mutex_lock(&buffer->mutex);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* we don't support switching read/write modes */
1548c2ecf20Sopenharmony_ci	if (buffer->write_in_progress) {
1558c2ecf20Sopenharmony_ci		retval = -ETXTBSY;
1568c2ecf20Sopenharmony_ci		goto out;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	buffer->read_in_progress = true;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (buffer->needs_read_fill) {
1618c2ecf20Sopenharmony_ci		/* perform first read with buf == NULL to get extent */
1628c2ecf20Sopenharmony_ci		down_read(&frag->frag_sem);
1638c2ecf20Sopenharmony_ci		if (!frag->frag_dead)
1648c2ecf20Sopenharmony_ci			len = buffer->bin_attr->read(buffer->item, NULL, 0);
1658c2ecf20Sopenharmony_ci		else
1668c2ecf20Sopenharmony_ci			len = -ENOENT;
1678c2ecf20Sopenharmony_ci		up_read(&frag->frag_sem);
1688c2ecf20Sopenharmony_ci		if (len <= 0) {
1698c2ecf20Sopenharmony_ci			retval = len;
1708c2ecf20Sopenharmony_ci			goto out;
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		/* do not exceed the maximum value */
1748c2ecf20Sopenharmony_ci		if (buffer->cb_max_size && len > buffer->cb_max_size) {
1758c2ecf20Sopenharmony_ci			retval = -EFBIG;
1768c2ecf20Sopenharmony_ci			goto out;
1778c2ecf20Sopenharmony_ci		}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		buffer->bin_buffer = vmalloc(len);
1808c2ecf20Sopenharmony_ci		if (buffer->bin_buffer == NULL) {
1818c2ecf20Sopenharmony_ci			retval = -ENOMEM;
1828c2ecf20Sopenharmony_ci			goto out;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci		buffer->bin_buffer_size = len;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		/* perform second read to fill buffer */
1878c2ecf20Sopenharmony_ci		down_read(&frag->frag_sem);
1888c2ecf20Sopenharmony_ci		if (!frag->frag_dead)
1898c2ecf20Sopenharmony_ci			len = buffer->bin_attr->read(buffer->item,
1908c2ecf20Sopenharmony_ci						     buffer->bin_buffer, len);
1918c2ecf20Sopenharmony_ci		else
1928c2ecf20Sopenharmony_ci			len = -ENOENT;
1938c2ecf20Sopenharmony_ci		up_read(&frag->frag_sem);
1948c2ecf20Sopenharmony_ci		if (len < 0) {
1958c2ecf20Sopenharmony_ci			retval = len;
1968c2ecf20Sopenharmony_ci			vfree(buffer->bin_buffer);
1978c2ecf20Sopenharmony_ci			buffer->bin_buffer_size = 0;
1988c2ecf20Sopenharmony_ci			buffer->bin_buffer = NULL;
1998c2ecf20Sopenharmony_ci			goto out;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		buffer->needs_read_fill = 0;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	retval = simple_read_from_buffer(buf, count, ppos, buffer->bin_buffer,
2068c2ecf20Sopenharmony_ci					buffer->bin_buffer_size);
2078c2ecf20Sopenharmony_ciout:
2088c2ecf20Sopenharmony_ci	mutex_unlock(&buffer->mutex);
2098c2ecf20Sopenharmony_ci	return retval;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/**
2148c2ecf20Sopenharmony_ci *	fill_write_buffer - copy buffer from userspace.
2158c2ecf20Sopenharmony_ci *	@buffer:	data buffer for file.
2168c2ecf20Sopenharmony_ci *	@buf:		data from user.
2178c2ecf20Sopenharmony_ci *	@count:		number of bytes in @userbuf.
2188c2ecf20Sopenharmony_ci *
2198c2ecf20Sopenharmony_ci *	Allocate @buffer->page if it hasn't been already, then
2208c2ecf20Sopenharmony_ci *	copy the user-supplied buffer into it.
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic int
2248c2ecf20Sopenharmony_cifill_write_buffer(struct configfs_buffer * buffer, const char __user * buf, size_t count)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	int error;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (!buffer->page)
2298c2ecf20Sopenharmony_ci		buffer->page = (char *)__get_free_pages(GFP_KERNEL, 0);
2308c2ecf20Sopenharmony_ci	if (!buffer->page)
2318c2ecf20Sopenharmony_ci		return -ENOMEM;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (count >= SIMPLE_ATTR_SIZE)
2348c2ecf20Sopenharmony_ci		count = SIMPLE_ATTR_SIZE - 1;
2358c2ecf20Sopenharmony_ci	error = copy_from_user(buffer->page,buf,count);
2368c2ecf20Sopenharmony_ci	buffer->needs_read_fill = 1;
2378c2ecf20Sopenharmony_ci	/* if buf is assumed to contain a string, terminate it by \0,
2388c2ecf20Sopenharmony_ci	 * so e.g. sscanf() can scan the string easily */
2398c2ecf20Sopenharmony_ci	buffer->page[count] = 0;
2408c2ecf20Sopenharmony_ci	return error ? -EFAULT : count;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int
2448c2ecf20Sopenharmony_ciflush_write_buffer(struct file *file, struct configfs_buffer *buffer, size_t count)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct configfs_fragment *frag = to_frag(file);
2478c2ecf20Sopenharmony_ci	int res = -ENOENT;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	down_read(&frag->frag_sem);
2508c2ecf20Sopenharmony_ci	if (!frag->frag_dead)
2518c2ecf20Sopenharmony_ci		res = buffer->attr->store(buffer->item, buffer->page, count);
2528c2ecf20Sopenharmony_ci	up_read(&frag->frag_sem);
2538c2ecf20Sopenharmony_ci	return res;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/**
2588c2ecf20Sopenharmony_ci *	configfs_write_file - write an attribute.
2598c2ecf20Sopenharmony_ci *	@file:	file pointer
2608c2ecf20Sopenharmony_ci *	@buf:	data to write
2618c2ecf20Sopenharmony_ci *	@count:	number of bytes
2628c2ecf20Sopenharmony_ci *	@ppos:	starting offset
2638c2ecf20Sopenharmony_ci *
2648c2ecf20Sopenharmony_ci *	Similar to configfs_read_file(), though working in the opposite direction.
2658c2ecf20Sopenharmony_ci *	We allocate and fill the data from the user in fill_write_buffer(),
2668c2ecf20Sopenharmony_ci *	then push it to the config_item in flush_write_buffer().
2678c2ecf20Sopenharmony_ci *	There is no easy way for us to know if userspace is only doing a partial
2688c2ecf20Sopenharmony_ci *	write, so we don't support them. We expect the entire buffer to come
2698c2ecf20Sopenharmony_ci *	on the first write.
2708c2ecf20Sopenharmony_ci *	Hint: if you're writing a value, first read the file, modify only
2718c2ecf20Sopenharmony_ci *	the value you're changing, then write entire buffer back.
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic ssize_t
2758c2ecf20Sopenharmony_ciconfigfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct configfs_buffer *buffer = file->private_data;
2788c2ecf20Sopenharmony_ci	ssize_t len;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	mutex_lock(&buffer->mutex);
2818c2ecf20Sopenharmony_ci	len = fill_write_buffer(buffer, buf, count);
2828c2ecf20Sopenharmony_ci	if (len > 0)
2838c2ecf20Sopenharmony_ci		len = flush_write_buffer(file, buffer, len);
2848c2ecf20Sopenharmony_ci	if (len > 0)
2858c2ecf20Sopenharmony_ci		*ppos += len;
2868c2ecf20Sopenharmony_ci	mutex_unlock(&buffer->mutex);
2878c2ecf20Sopenharmony_ci	return len;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/**
2918c2ecf20Sopenharmony_ci *	configfs_write_bin_file - write a binary attribute.
2928c2ecf20Sopenharmony_ci *	@file:	file pointer
2938c2ecf20Sopenharmony_ci *	@buf:	data to write
2948c2ecf20Sopenharmony_ci *	@count:	number of bytes
2958c2ecf20Sopenharmony_ci *	@ppos:	starting offset
2968c2ecf20Sopenharmony_ci *
2978c2ecf20Sopenharmony_ci *	Writing to a binary attribute file is similar to a normal read.
2988c2ecf20Sopenharmony_ci *	We buffer the consecutive writes (binary attribute files do not
2998c2ecf20Sopenharmony_ci *	support lseek) in a continuously growing buffer, but we don't
3008c2ecf20Sopenharmony_ci *	commit until the close of the file.
3018c2ecf20Sopenharmony_ci */
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic ssize_t
3048c2ecf20Sopenharmony_ciconfigfs_write_bin_file(struct file *file, const char __user *buf,
3058c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct configfs_buffer *buffer = file->private_data;
3088c2ecf20Sopenharmony_ci	void *tbuf = NULL;
3098c2ecf20Sopenharmony_ci	ssize_t len;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	mutex_lock(&buffer->mutex);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* we don't support switching read/write modes */
3148c2ecf20Sopenharmony_ci	if (buffer->read_in_progress) {
3158c2ecf20Sopenharmony_ci		len = -ETXTBSY;
3168c2ecf20Sopenharmony_ci		goto out;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	buffer->write_in_progress = true;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* buffer grows? */
3218c2ecf20Sopenharmony_ci	if (*ppos + count > buffer->bin_buffer_size) {
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		if (buffer->cb_max_size &&
3248c2ecf20Sopenharmony_ci			*ppos + count > buffer->cb_max_size) {
3258c2ecf20Sopenharmony_ci			len = -EFBIG;
3268c2ecf20Sopenharmony_ci			goto out;
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		tbuf = vmalloc(*ppos + count);
3308c2ecf20Sopenharmony_ci		if (tbuf == NULL) {
3318c2ecf20Sopenharmony_ci			len = -ENOMEM;
3328c2ecf20Sopenharmony_ci			goto out;
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		/* copy old contents */
3368c2ecf20Sopenharmony_ci		if (buffer->bin_buffer) {
3378c2ecf20Sopenharmony_ci			memcpy(tbuf, buffer->bin_buffer,
3388c2ecf20Sopenharmony_ci				buffer->bin_buffer_size);
3398c2ecf20Sopenharmony_ci			vfree(buffer->bin_buffer);
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		/* clear the new area */
3438c2ecf20Sopenharmony_ci		memset(tbuf + buffer->bin_buffer_size, 0,
3448c2ecf20Sopenharmony_ci			*ppos + count - buffer->bin_buffer_size);
3458c2ecf20Sopenharmony_ci		buffer->bin_buffer = tbuf;
3468c2ecf20Sopenharmony_ci		buffer->bin_buffer_size = *ppos + count;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	len = simple_write_to_buffer(buffer->bin_buffer,
3508c2ecf20Sopenharmony_ci			buffer->bin_buffer_size, ppos, buf, count);
3518c2ecf20Sopenharmony_ciout:
3528c2ecf20Sopenharmony_ci	mutex_unlock(&buffer->mutex);
3538c2ecf20Sopenharmony_ci	return len;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic int __configfs_open_file(struct inode *inode, struct file *file, int type)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct dentry *dentry = file->f_path.dentry;
3598c2ecf20Sopenharmony_ci	struct configfs_fragment *frag = to_frag(file);
3608c2ecf20Sopenharmony_ci	struct configfs_attribute *attr;
3618c2ecf20Sopenharmony_ci	struct configfs_buffer *buffer;
3628c2ecf20Sopenharmony_ci	int error;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	error = -ENOMEM;
3658c2ecf20Sopenharmony_ci	buffer = kzalloc(sizeof(struct configfs_buffer), GFP_KERNEL);
3668c2ecf20Sopenharmony_ci	if (!buffer)
3678c2ecf20Sopenharmony_ci		goto out;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	error = -ENOENT;
3708c2ecf20Sopenharmony_ci	down_read(&frag->frag_sem);
3718c2ecf20Sopenharmony_ci	if (unlikely(frag->frag_dead))
3728c2ecf20Sopenharmony_ci		goto out_free_buffer;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	error = -EINVAL;
3758c2ecf20Sopenharmony_ci	buffer->item = to_item(dentry->d_parent);
3768c2ecf20Sopenharmony_ci	if (!buffer->item)
3778c2ecf20Sopenharmony_ci		goto out_free_buffer;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	attr = to_attr(dentry);
3808c2ecf20Sopenharmony_ci	if (!attr)
3818c2ecf20Sopenharmony_ci		goto out_free_buffer;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (type & CONFIGFS_ITEM_BIN_ATTR) {
3848c2ecf20Sopenharmony_ci		buffer->bin_attr = to_bin_attr(dentry);
3858c2ecf20Sopenharmony_ci		buffer->cb_max_size = buffer->bin_attr->cb_max_size;
3868c2ecf20Sopenharmony_ci	} else {
3878c2ecf20Sopenharmony_ci		buffer->attr = attr;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	buffer->owner = attr->ca_owner;
3918c2ecf20Sopenharmony_ci	/* Grab the module reference for this attribute if we have one */
3928c2ecf20Sopenharmony_ci	error = -ENODEV;
3938c2ecf20Sopenharmony_ci	if (!try_module_get(buffer->owner))
3948c2ecf20Sopenharmony_ci		goto out_free_buffer;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	error = -EACCES;
3978c2ecf20Sopenharmony_ci	if (!buffer->item->ci_type)
3988c2ecf20Sopenharmony_ci		goto out_put_module;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	buffer->ops = buffer->item->ci_type->ct_item_ops;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* File needs write support.
4038c2ecf20Sopenharmony_ci	 * The inode's perms must say it's ok,
4048c2ecf20Sopenharmony_ci	 * and we must have a store method.
4058c2ecf20Sopenharmony_ci	 */
4068c2ecf20Sopenharmony_ci	if (file->f_mode & FMODE_WRITE) {
4078c2ecf20Sopenharmony_ci		if (!(inode->i_mode & S_IWUGO))
4088c2ecf20Sopenharmony_ci			goto out_put_module;
4098c2ecf20Sopenharmony_ci		if ((type & CONFIGFS_ITEM_ATTR) && !attr->store)
4108c2ecf20Sopenharmony_ci			goto out_put_module;
4118c2ecf20Sopenharmony_ci		if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->write)
4128c2ecf20Sopenharmony_ci			goto out_put_module;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* File needs read support.
4168c2ecf20Sopenharmony_ci	 * The inode's perms must say it's ok, and we there
4178c2ecf20Sopenharmony_ci	 * must be a show method for it.
4188c2ecf20Sopenharmony_ci	 */
4198c2ecf20Sopenharmony_ci	if (file->f_mode & FMODE_READ) {
4208c2ecf20Sopenharmony_ci		if (!(inode->i_mode & S_IRUGO))
4218c2ecf20Sopenharmony_ci			goto out_put_module;
4228c2ecf20Sopenharmony_ci		if ((type & CONFIGFS_ITEM_ATTR) && !attr->show)
4238c2ecf20Sopenharmony_ci			goto out_put_module;
4248c2ecf20Sopenharmony_ci		if ((type & CONFIGFS_ITEM_BIN_ATTR) && !buffer->bin_attr->read)
4258c2ecf20Sopenharmony_ci			goto out_put_module;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	mutex_init(&buffer->mutex);
4298c2ecf20Sopenharmony_ci	buffer->needs_read_fill = 1;
4308c2ecf20Sopenharmony_ci	buffer->read_in_progress = false;
4318c2ecf20Sopenharmony_ci	buffer->write_in_progress = false;
4328c2ecf20Sopenharmony_ci	file->private_data = buffer;
4338c2ecf20Sopenharmony_ci	up_read(&frag->frag_sem);
4348c2ecf20Sopenharmony_ci	return 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ciout_put_module:
4378c2ecf20Sopenharmony_ci	module_put(buffer->owner);
4388c2ecf20Sopenharmony_ciout_free_buffer:
4398c2ecf20Sopenharmony_ci	up_read(&frag->frag_sem);
4408c2ecf20Sopenharmony_ci	kfree(buffer);
4418c2ecf20Sopenharmony_ciout:
4428c2ecf20Sopenharmony_ci	return error;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic int configfs_release(struct inode *inode, struct file *filp)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct configfs_buffer *buffer = filp->private_data;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	module_put(buffer->owner);
4508c2ecf20Sopenharmony_ci	if (buffer->page)
4518c2ecf20Sopenharmony_ci		free_page((unsigned long)buffer->page);
4528c2ecf20Sopenharmony_ci	mutex_destroy(&buffer->mutex);
4538c2ecf20Sopenharmony_ci	kfree(buffer);
4548c2ecf20Sopenharmony_ci	return 0;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic int configfs_open_file(struct inode *inode, struct file *filp)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	return __configfs_open_file(inode, filp, CONFIGFS_ITEM_ATTR);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int configfs_open_bin_file(struct inode *inode, struct file *filp)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	return __configfs_open_file(inode, filp, CONFIGFS_ITEM_BIN_ATTR);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic int configfs_release_bin_file(struct inode *inode, struct file *file)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct configfs_buffer *buffer = file->private_data;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	buffer->read_in_progress = false;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (buffer->write_in_progress) {
4748c2ecf20Sopenharmony_ci		struct configfs_fragment *frag = to_frag(file);
4758c2ecf20Sopenharmony_ci		buffer->write_in_progress = false;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci		down_read(&frag->frag_sem);
4788c2ecf20Sopenharmony_ci		if (!frag->frag_dead) {
4798c2ecf20Sopenharmony_ci			/* result of ->release() is ignored */
4808c2ecf20Sopenharmony_ci			buffer->bin_attr->write(buffer->item,
4818c2ecf20Sopenharmony_ci					buffer->bin_buffer,
4828c2ecf20Sopenharmony_ci					buffer->bin_buffer_size);
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci		up_read(&frag->frag_sem);
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	vfree(buffer->bin_buffer);
4888c2ecf20Sopenharmony_ci	buffer->bin_buffer = NULL;
4898c2ecf20Sopenharmony_ci	buffer->bin_buffer_size = 0;
4908c2ecf20Sopenharmony_ci	buffer->needs_read_fill = 1;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	configfs_release(inode, file);
4938c2ecf20Sopenharmony_ci	return 0;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ciconst struct file_operations configfs_file_operations = {
4988c2ecf20Sopenharmony_ci	.read		= configfs_read_file,
4998c2ecf20Sopenharmony_ci	.write		= configfs_write_file,
5008c2ecf20Sopenharmony_ci	.llseek		= generic_file_llseek,
5018c2ecf20Sopenharmony_ci	.open		= configfs_open_file,
5028c2ecf20Sopenharmony_ci	.release	= configfs_release,
5038c2ecf20Sopenharmony_ci};
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ciconst struct file_operations configfs_bin_file_operations = {
5068c2ecf20Sopenharmony_ci	.read		= configfs_read_bin_file,
5078c2ecf20Sopenharmony_ci	.write		= configfs_write_bin_file,
5088c2ecf20Sopenharmony_ci	.llseek		= NULL,		/* bin file is not seekable */
5098c2ecf20Sopenharmony_ci	.open		= configfs_open_bin_file,
5108c2ecf20Sopenharmony_ci	.release	= configfs_release_bin_file,
5118c2ecf20Sopenharmony_ci};
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci/**
5148c2ecf20Sopenharmony_ci *	configfs_create_file - create an attribute file for an item.
5158c2ecf20Sopenharmony_ci *	@item:	item we're creating for.
5168c2ecf20Sopenharmony_ci *	@attr:	atrribute descriptor.
5178c2ecf20Sopenharmony_ci */
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ciint configfs_create_file(struct config_item * item, const struct configfs_attribute * attr)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct dentry *dir = item->ci_dentry;
5228c2ecf20Sopenharmony_ci	struct configfs_dirent *parent_sd = dir->d_fsdata;
5238c2ecf20Sopenharmony_ci	umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG;
5248c2ecf20Sopenharmony_ci	int error = 0;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	inode_lock_nested(d_inode(dir), I_MUTEX_NORMAL);
5278c2ecf20Sopenharmony_ci	error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode,
5288c2ecf20Sopenharmony_ci				     CONFIGFS_ITEM_ATTR, parent_sd->s_frag);
5298c2ecf20Sopenharmony_ci	inode_unlock(d_inode(dir));
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return error;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci/**
5358c2ecf20Sopenharmony_ci *	configfs_create_bin_file - create a binary attribute file for an item.
5368c2ecf20Sopenharmony_ci *	@item:	item we're creating for.
5378c2ecf20Sopenharmony_ci *	@attr:	atrribute descriptor.
5388c2ecf20Sopenharmony_ci */
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ciint configfs_create_bin_file(struct config_item *item,
5418c2ecf20Sopenharmony_ci		const struct configfs_bin_attribute *bin_attr)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct dentry *dir = item->ci_dentry;
5448c2ecf20Sopenharmony_ci	struct configfs_dirent *parent_sd = dir->d_fsdata;
5458c2ecf20Sopenharmony_ci	umode_t mode = (bin_attr->cb_attr.ca_mode & S_IALLUGO) | S_IFREG;
5468c2ecf20Sopenharmony_ci	int error = 0;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	inode_lock_nested(dir->d_inode, I_MUTEX_NORMAL);
5498c2ecf20Sopenharmony_ci	error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode,
5508c2ecf20Sopenharmony_ci				     CONFIGFS_ITEM_BIN_ATTR, parent_sd->s_frag);
5518c2ecf20Sopenharmony_ci	inode_unlock(dir->d_inode);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return error;
5548c2ecf20Sopenharmony_ci}
555