sysfs: implement bin_buffer
[linux-3.10.git] / fs / sysfs / bin.c
1 /*
2  * bin.c - binary file operations for sysfs.
3  *
4  * Copyright (c) 2003 Patrick Mochel
5  * Copyright (c) 2003 Matthew Wilcox
6  * Copyright (c) 2004 Silicon Graphics, Inc.
7  */
8
9 #undef DEBUG
10
11 #include <linux/errno.h>
12 #include <linux/fs.h>
13 #include <linux/kernel.h>
14 #include <linux/kobject.h>
15 #include <linux/module.h>
16 #include <linux/slab.h>
17
18 #include <asm/uaccess.h>
19 #include <asm/semaphore.h>
20
21 #include "sysfs.h"
22
23 struct bin_buffer {
24         struct mutex    mutex;
25         void            *buffer;
26 };
27
28 static int
29 fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
30 {
31         struct sysfs_dirent *attr_sd = dentry->d_fsdata;
32         struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
33         struct kobject * kobj = to_kobj(dentry->d_parent);
34
35         if (!attr->read)
36                 return -EIO;
37
38         return attr->read(kobj, buffer, off, count);
39 }
40
41 static ssize_t
42 read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
43 {
44         struct bin_buffer *bb = file->private_data;
45         struct dentry *dentry = file->f_path.dentry;
46         int size = dentry->d_inode->i_size;
47         loff_t offs = *off;
48         int count = min_t(size_t, bytes, PAGE_SIZE);
49
50         if (size) {
51                 if (offs > size)
52                         return 0;
53                 if (offs + count > size)
54                         count = size - offs;
55         }
56
57         mutex_lock(&bb->mutex);
58
59         count = fill_read(dentry, bb->buffer, offs, count);
60         if (count < 0)
61                 goto out_unlock;
62
63         if (copy_to_user(userbuf, bb->buffer, count)) {
64                 count = -EFAULT;
65                 goto out_unlock;
66         }
67
68         pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
69
70         *off = offs + count;
71
72  out_unlock:
73         mutex_unlock(&bb->mutex);
74         return count;
75 }
76
77 static int
78 flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
79 {
80         struct sysfs_dirent *attr_sd = dentry->d_fsdata;
81         struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
82         struct kobject *kobj = to_kobj(dentry->d_parent);
83
84         if (!attr->write)
85                 return -EIO;
86
87         return attr->write(kobj, buffer, offset, count);
88 }
89
90 static ssize_t write(struct file *file, const char __user *userbuf,
91                      size_t bytes, loff_t *off)
92 {
93         struct bin_buffer *bb = file->private_data;
94         struct dentry *dentry = file->f_path.dentry;
95         int size = dentry->d_inode->i_size;
96         loff_t offs = *off;
97         int count = min_t(size_t, bytes, PAGE_SIZE);
98
99         if (size) {
100                 if (offs > size)
101                         return 0;
102                 if (offs + count > size)
103                         count = size - offs;
104         }
105
106         mutex_lock(&bb->mutex);
107
108         if (copy_from_user(bb->buffer, userbuf, count)) {
109                 count = -EFAULT;
110                 goto out_unlock;
111         }
112
113         count = flush_write(dentry, bb->buffer, offs, count);
114         if (count > 0)
115                 *off = offs + count;
116
117  out_unlock:
118         mutex_unlock(&bb->mutex);
119         return count;
120 }
121
122 static int mmap(struct file *file, struct vm_area_struct *vma)
123 {
124         struct bin_buffer *bb = file->private_data;
125         struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
126         struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
127         struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent);
128         int rc;
129
130         if (!attr->mmap)
131                 return -EINVAL;
132
133         mutex_lock(&bb->mutex);
134         rc = attr->mmap(kobj, attr, vma);
135         mutex_unlock(&bb->mutex);
136
137         return rc;
138 }
139
140 static int open(struct inode * inode, struct file * file)
141 {
142         struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
143         struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
144         struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
145         struct bin_buffer *bb = NULL;
146         int error = -EINVAL;
147
148         if (!kobj || !attr)
149                 goto Done;
150
151         /* Grab the module reference for this attribute if we have one */
152         error = -ENODEV;
153         if (!try_module_get(attr->attr.owner)) 
154                 goto Done;
155
156         error = -EACCES;
157         if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
158                 goto Error;
159         if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
160                 goto Error;
161
162         error = -ENOMEM;
163         bb = kzalloc(sizeof(*bb), GFP_KERNEL);
164         if (!bb)
165                 goto Error;
166
167         bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
168         if (!bb->buffer)
169                 goto Error;
170
171         mutex_init(&bb->mutex);
172         file->private_data = bb;
173
174         error = 0;
175         goto Done;
176
177  Error:
178         kfree(bb);
179         module_put(attr->attr.owner);
180  Done:
181         if (error)
182                 kobject_put(kobj);
183         return error;
184 }
185
186 static int release(struct inode * inode, struct file * file)
187 {
188         struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent);
189         struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
190         struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
191         struct bin_buffer *bb = file->private_data;
192
193         kobject_put(kobj);
194         module_put(attr->attr.owner);
195         kfree(bb->buffer);
196         kfree(bb);
197         return 0;
198 }
199
200 const struct file_operations bin_fops = {
201         .read           = read,
202         .write          = write,
203         .mmap           = mmap,
204         .llseek         = generic_file_llseek,
205         .open           = open,
206         .release        = release,
207 };
208
209 /**
210  *      sysfs_create_bin_file - create binary file for object.
211  *      @kobj:  object.
212  *      @attr:  attribute descriptor.
213  */
214
215 int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
216 {
217         BUG_ON(!kobj || !kobj->dentry || !attr);
218
219         return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
220 }
221
222
223 /**
224  *      sysfs_remove_bin_file - remove binary file for object.
225  *      @kobj:  object.
226  *      @attr:  attribute descriptor.
227  */
228
229 void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
230 {
231         if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
232                 printk(KERN_ERR "%s: "
233                         "bad dentry or inode or no such file: \"%s\"\n",
234                         __FUNCTION__, attr->attr.name);
235                 dump_stack();
236         }
237 }
238
239 EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
240 EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);