diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/sysfs/file.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/sysfs/file.c')
-rw-r--r-- | fs/sysfs/file.c | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c new file mode 100644 index 000000000000..352f966a1174 --- /dev/null +++ b/fs/sysfs/file.c | |||
@@ -0,0 +1,447 @@ | |||
1 | /* | ||
2 | * file.c - operations for regular (text) files. | ||
3 | */ | ||
4 | |||
5 | #include <linux/module.h> | ||
6 | #include <linux/dnotify.h> | ||
7 | #include <linux/kobject.h> | ||
8 | #include <asm/uaccess.h> | ||
9 | #include <asm/semaphore.h> | ||
10 | |||
11 | #include "sysfs.h" | ||
12 | |||
13 | #define to_subsys(k) container_of(k,struct subsystem,kset.kobj) | ||
14 | #define to_sattr(a) container_of(a,struct subsys_attribute,attr) | ||
15 | |||
16 | /** | ||
17 | * Subsystem file operations. | ||
18 | * These operations allow subsystems to have files that can be | ||
19 | * read/written. | ||
20 | */ | ||
21 | static ssize_t | ||
22 | subsys_attr_show(struct kobject * kobj, struct attribute * attr, char * page) | ||
23 | { | ||
24 | struct subsystem * s = to_subsys(kobj); | ||
25 | struct subsys_attribute * sattr = to_sattr(attr); | ||
26 | ssize_t ret = 0; | ||
27 | |||
28 | if (sattr->show) | ||
29 | ret = sattr->show(s,page); | ||
30 | return ret; | ||
31 | } | ||
32 | |||
33 | static ssize_t | ||
34 | subsys_attr_store(struct kobject * kobj, struct attribute * attr, | ||
35 | const char * page, size_t count) | ||
36 | { | ||
37 | struct subsystem * s = to_subsys(kobj); | ||
38 | struct subsys_attribute * sattr = to_sattr(attr); | ||
39 | ssize_t ret = 0; | ||
40 | |||
41 | if (sattr->store) | ||
42 | ret = sattr->store(s,page,count); | ||
43 | return ret; | ||
44 | } | ||
45 | |||
46 | static struct sysfs_ops subsys_sysfs_ops = { | ||
47 | .show = subsys_attr_show, | ||
48 | .store = subsys_attr_store, | ||
49 | }; | ||
50 | |||
51 | |||
52 | struct sysfs_buffer { | ||
53 | size_t count; | ||
54 | loff_t pos; | ||
55 | char * page; | ||
56 | struct sysfs_ops * ops; | ||
57 | struct semaphore sem; | ||
58 | int needs_read_fill; | ||
59 | }; | ||
60 | |||
61 | |||
62 | /** | ||
63 | * fill_read_buffer - allocate and fill buffer from object. | ||
64 | * @dentry: dentry pointer. | ||
65 | * @buffer: data buffer for file. | ||
66 | * | ||
67 | * Allocate @buffer->page, if it hasn't been already, then call the | ||
68 | * kobject's show() method to fill the buffer with this attribute's | ||
69 | * data. | ||
70 | * This is called only once, on the file's first read. | ||
71 | */ | ||
72 | static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) | ||
73 | { | ||
74 | struct attribute * attr = to_attr(dentry); | ||
75 | struct kobject * kobj = to_kobj(dentry->d_parent); | ||
76 | struct sysfs_ops * ops = buffer->ops; | ||
77 | int ret = 0; | ||
78 | ssize_t count; | ||
79 | |||
80 | if (!buffer->page) | ||
81 | buffer->page = (char *) get_zeroed_page(GFP_KERNEL); | ||
82 | if (!buffer->page) | ||
83 | return -ENOMEM; | ||
84 | |||
85 | count = ops->show(kobj,attr,buffer->page); | ||
86 | buffer->needs_read_fill = 0; | ||
87 | BUG_ON(count > (ssize_t)PAGE_SIZE); | ||
88 | if (count >= 0) | ||
89 | buffer->count = count; | ||
90 | else | ||
91 | ret = count; | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * flush_read_buffer - push buffer to userspace. | ||
98 | * @buffer: data buffer for file. | ||
99 | * @userbuf: user-passed buffer. | ||
100 | * @count: number of bytes requested. | ||
101 | * @ppos: file position. | ||
102 | * | ||
103 | * Copy the buffer we filled in fill_read_buffer() to userspace. | ||
104 | * This is done at the reader's leisure, copying and advancing | ||
105 | * the amount they specify each time. | ||
106 | * This may be called continuously until the buffer is empty. | ||
107 | */ | ||
108 | static int flush_read_buffer(struct sysfs_buffer * buffer, char __user * buf, | ||
109 | size_t count, loff_t * ppos) | ||
110 | { | ||
111 | int error; | ||
112 | |||
113 | if (*ppos > buffer->count) | ||
114 | return 0; | ||
115 | |||
116 | if (count > (buffer->count - *ppos)) | ||
117 | count = buffer->count - *ppos; | ||
118 | |||
119 | error = copy_to_user(buf,buffer->page + *ppos,count); | ||
120 | if (!error) | ||
121 | *ppos += count; | ||
122 | return error ? -EFAULT : count; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * sysfs_read_file - read an attribute. | ||
127 | * @file: file pointer. | ||
128 | * @buf: buffer to fill. | ||
129 | * @count: number of bytes to read. | ||
130 | * @ppos: starting offset in file. | ||
131 | * | ||
132 | * Userspace wants to read an attribute file. The attribute descriptor | ||
133 | * is in the file's ->d_fsdata. The target object is in the directory's | ||
134 | * ->d_fsdata. | ||
135 | * | ||
136 | * We call fill_read_buffer() to allocate and fill the buffer from the | ||
137 | * object's show() method exactly once (if the read is happening from | ||
138 | * the beginning of the file). That should fill the entire buffer with | ||
139 | * all the data the object has to offer for that attribute. | ||
140 | * We then call flush_read_buffer() to copy the buffer to userspace | ||
141 | * in the increments specified. | ||
142 | */ | ||
143 | |||
144 | static ssize_t | ||
145 | sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
146 | { | ||
147 | struct sysfs_buffer * buffer = file->private_data; | ||
148 | ssize_t retval = 0; | ||
149 | |||
150 | down(&buffer->sem); | ||
151 | if (buffer->needs_read_fill) { | ||
152 | if ((retval = fill_read_buffer(file->f_dentry,buffer))) | ||
153 | goto out; | ||
154 | } | ||
155 | pr_debug("%s: count = %d, ppos = %lld, buf = %s\n", | ||
156 | __FUNCTION__,count,*ppos,buffer->page); | ||
157 | retval = flush_read_buffer(buffer,buf,count,ppos); | ||
158 | out: | ||
159 | up(&buffer->sem); | ||
160 | return retval; | ||
161 | } | ||
162 | |||
163 | |||
164 | /** | ||
165 | * fill_write_buffer - copy buffer from userspace. | ||
166 | * @buffer: data buffer for file. | ||
167 | * @userbuf: data from user. | ||
168 | * @count: number of bytes in @userbuf. | ||
169 | * | ||
170 | * Allocate @buffer->page if it hasn't been already, then | ||
171 | * copy the user-supplied buffer into it. | ||
172 | */ | ||
173 | |||
174 | static int | ||
175 | fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t count) | ||
176 | { | ||
177 | int error; | ||
178 | |||
179 | if (!buffer->page) | ||
180 | buffer->page = (char *)get_zeroed_page(GFP_KERNEL); | ||
181 | if (!buffer->page) | ||
182 | return -ENOMEM; | ||
183 | |||
184 | if (count >= PAGE_SIZE) | ||
185 | count = PAGE_SIZE - 1; | ||
186 | error = copy_from_user(buffer->page,buf,count); | ||
187 | buffer->needs_read_fill = 1; | ||
188 | return error ? -EFAULT : count; | ||
189 | } | ||
190 | |||
191 | |||
192 | /** | ||
193 | * flush_write_buffer - push buffer to kobject. | ||
194 | * @file: file pointer. | ||
195 | * @buffer: data buffer for file. | ||
196 | * | ||
197 | * Get the correct pointers for the kobject and the attribute we're | ||
198 | * dealing with, then call the store() method for the attribute, | ||
199 | * passing the buffer that we acquired in fill_write_buffer(). | ||
200 | */ | ||
201 | |||
202 | static int | ||
203 | flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) | ||
204 | { | ||
205 | struct attribute * attr = to_attr(dentry); | ||
206 | struct kobject * kobj = to_kobj(dentry->d_parent); | ||
207 | struct sysfs_ops * ops = buffer->ops; | ||
208 | |||
209 | return ops->store(kobj,attr,buffer->page,count); | ||
210 | } | ||
211 | |||
212 | |||
213 | /** | ||
214 | * sysfs_write_file - write an attribute. | ||
215 | * @file: file pointer | ||
216 | * @buf: data to write | ||
217 | * @count: number of bytes | ||
218 | * @ppos: starting offset | ||
219 | * | ||
220 | * Similar to sysfs_read_file(), though working in the opposite direction. | ||
221 | * We allocate and fill the data from the user in fill_write_buffer(), | ||
222 | * then push it to the kobject in flush_write_buffer(). | ||
223 | * There is no easy way for us to know if userspace is only doing a partial | ||
224 | * write, so we don't support them. We expect the entire buffer to come | ||
225 | * on the first write. | ||
226 | * Hint: if you're writing a value, first read the file, modify only the | ||
227 | * the value you're changing, then write entire buffer back. | ||
228 | */ | ||
229 | |||
230 | static ssize_t | ||
231 | sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||
232 | { | ||
233 | struct sysfs_buffer * buffer = file->private_data; | ||
234 | ssize_t len; | ||
235 | |||
236 | down(&buffer->sem); | ||
237 | len = fill_write_buffer(buffer, buf, count); | ||
238 | if (len > 0) | ||
239 | len = flush_write_buffer(file->f_dentry, buffer, len); | ||
240 | if (len > 0) | ||
241 | *ppos += len; | ||
242 | up(&buffer->sem); | ||
243 | return len; | ||
244 | } | ||
245 | |||
246 | static int check_perm(struct inode * inode, struct file * file) | ||
247 | { | ||
248 | struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); | ||
249 | struct attribute * attr = to_attr(file->f_dentry); | ||
250 | struct sysfs_buffer * buffer; | ||
251 | struct sysfs_ops * ops = NULL; | ||
252 | int error = 0; | ||
253 | |||
254 | if (!kobj || !attr) | ||
255 | goto Einval; | ||
256 | |||
257 | /* Grab the module reference for this attribute if we have one */ | ||
258 | if (!try_module_get(attr->owner)) { | ||
259 | error = -ENODEV; | ||
260 | goto Done; | ||
261 | } | ||
262 | |||
263 | /* if the kobject has no ktype, then we assume that it is a subsystem | ||
264 | * itself, and use ops for it. | ||
265 | */ | ||
266 | if (kobj->kset && kobj->kset->ktype) | ||
267 | ops = kobj->kset->ktype->sysfs_ops; | ||
268 | else if (kobj->ktype) | ||
269 | ops = kobj->ktype->sysfs_ops; | ||
270 | else | ||
271 | ops = &subsys_sysfs_ops; | ||
272 | |||
273 | /* No sysfs operations, either from having no subsystem, | ||
274 | * or the subsystem have no operations. | ||
275 | */ | ||
276 | if (!ops) | ||
277 | goto Eaccess; | ||
278 | |||
279 | /* File needs write support. | ||
280 | * The inode's perms must say it's ok, | ||
281 | * and we must have a store method. | ||
282 | */ | ||
283 | if (file->f_mode & FMODE_WRITE) { | ||
284 | |||
285 | if (!(inode->i_mode & S_IWUGO) || !ops->store) | ||
286 | goto Eaccess; | ||
287 | |||
288 | } | ||
289 | |||
290 | /* File needs read support. | ||
291 | * The inode's perms must say it's ok, and we there | ||
292 | * must be a show method for it. | ||
293 | */ | ||
294 | if (file->f_mode & FMODE_READ) { | ||
295 | if (!(inode->i_mode & S_IRUGO) || !ops->show) | ||
296 | goto Eaccess; | ||
297 | } | ||
298 | |||
299 | /* No error? Great, allocate a buffer for the file, and store it | ||
300 | * it in file->private_data for easy access. | ||
301 | */ | ||
302 | buffer = kmalloc(sizeof(struct sysfs_buffer),GFP_KERNEL); | ||
303 | if (buffer) { | ||
304 | memset(buffer,0,sizeof(struct sysfs_buffer)); | ||
305 | init_MUTEX(&buffer->sem); | ||
306 | buffer->needs_read_fill = 1; | ||
307 | buffer->ops = ops; | ||
308 | file->private_data = buffer; | ||
309 | } else | ||
310 | error = -ENOMEM; | ||
311 | goto Done; | ||
312 | |||
313 | Einval: | ||
314 | error = -EINVAL; | ||
315 | goto Done; | ||
316 | Eaccess: | ||
317 | error = -EACCES; | ||
318 | module_put(attr->owner); | ||
319 | Done: | ||
320 | if (error && kobj) | ||
321 | kobject_put(kobj); | ||
322 | return error; | ||
323 | } | ||
324 | |||
325 | static int sysfs_open_file(struct inode * inode, struct file * filp) | ||
326 | { | ||
327 | return check_perm(inode,filp); | ||
328 | } | ||
329 | |||
330 | static int sysfs_release(struct inode * inode, struct file * filp) | ||
331 | { | ||
332 | struct kobject * kobj = to_kobj(filp->f_dentry->d_parent); | ||
333 | struct attribute * attr = to_attr(filp->f_dentry); | ||
334 | struct module * owner = attr->owner; | ||
335 | struct sysfs_buffer * buffer = filp->private_data; | ||
336 | |||
337 | if (kobj) | ||
338 | kobject_put(kobj); | ||
339 | /* After this point, attr should not be accessed. */ | ||
340 | module_put(owner); | ||
341 | |||
342 | if (buffer) { | ||
343 | if (buffer->page) | ||
344 | free_page((unsigned long)buffer->page); | ||
345 | kfree(buffer); | ||
346 | } | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | struct file_operations sysfs_file_operations = { | ||
351 | .read = sysfs_read_file, | ||
352 | .write = sysfs_write_file, | ||
353 | .llseek = generic_file_llseek, | ||
354 | .open = sysfs_open_file, | ||
355 | .release = sysfs_release, | ||
356 | }; | ||
357 | |||
358 | |||
359 | int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) | ||
360 | { | ||
361 | struct sysfs_dirent * parent_sd = dir->d_fsdata; | ||
362 | umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; | ||
363 | int error = 0; | ||
364 | |||
365 | down(&dir->d_inode->i_sem); | ||
366 | error = sysfs_make_dirent(parent_sd, NULL, (void *) attr, mode, type); | ||
367 | up(&dir->d_inode->i_sem); | ||
368 | |||
369 | return error; | ||
370 | } | ||
371 | |||
372 | |||
373 | /** | ||
374 | * sysfs_create_file - create an attribute file for an object. | ||
375 | * @kobj: object we're creating for. | ||
376 | * @attr: atrribute descriptor. | ||
377 | */ | ||
378 | |||
379 | int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) | ||
380 | { | ||
381 | BUG_ON(!kobj || !kobj->dentry || !attr); | ||
382 | |||
383 | return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR); | ||
384 | |||
385 | } | ||
386 | |||
387 | |||
388 | /** | ||
389 | * sysfs_update_file - update the modified timestamp on an object attribute. | ||
390 | * @kobj: object we're acting for. | ||
391 | * @attr: attribute descriptor. | ||
392 | * | ||
393 | * Also call dnotify for the dentry, which lots of userspace programs | ||
394 | * use. | ||
395 | */ | ||
396 | int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) | ||
397 | { | ||
398 | struct dentry * dir = kobj->dentry; | ||
399 | struct dentry * victim; | ||
400 | int res = -ENOENT; | ||
401 | |||
402 | down(&dir->d_inode->i_sem); | ||
403 | victim = sysfs_get_dentry(dir, attr->name); | ||
404 | if (!IS_ERR(victim)) { | ||
405 | /* make sure dentry is really there */ | ||
406 | if (victim->d_inode && | ||
407 | (victim->d_parent->d_inode == dir->d_inode)) { | ||
408 | victim->d_inode->i_mtime = CURRENT_TIME; | ||
409 | dnotify_parent(victim, DN_MODIFY); | ||
410 | |||
411 | /** | ||
412 | * Drop reference from initial sysfs_get_dentry(). | ||
413 | */ | ||
414 | dput(victim); | ||
415 | res = 0; | ||
416 | } else | ||
417 | d_drop(victim); | ||
418 | |||
419 | /** | ||
420 | * Drop the reference acquired from sysfs_get_dentry() above. | ||
421 | */ | ||
422 | dput(victim); | ||
423 | } | ||
424 | up(&dir->d_inode->i_sem); | ||
425 | |||
426 | return res; | ||
427 | } | ||
428 | |||
429 | |||
430 | /** | ||
431 | * sysfs_remove_file - remove an object attribute. | ||
432 | * @kobj: object we're acting for. | ||
433 | * @attr: attribute descriptor. | ||
434 | * | ||
435 | * Hash the attribute name and kill the victim. | ||
436 | */ | ||
437 | |||
438 | void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) | ||
439 | { | ||
440 | sysfs_hash_and_remove(kobj->dentry,attr->name); | ||
441 | } | ||
442 | |||
443 | |||
444 | EXPORT_SYMBOL_GPL(sysfs_create_file); | ||
445 | EXPORT_SYMBOL_GPL(sysfs_remove_file); | ||
446 | EXPORT_SYMBOL_GPL(sysfs_update_file); | ||
447 | |||