]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - fs/sysfs/bin.c
sysfs: implement bin_buffer
[mirror_ubuntu-artful-kernel.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);