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