606267a362757ba2927d9acba7b2e9398ac6cd4b
[safe/jmp/linux-2.6] / 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 static int
24 fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
25 {
26         struct bin_attribute * attr = to_bin_attr(dentry);
27         struct kobject * kobj = to_kobj(dentry->d_parent);
28
29         if (!attr->read)
30                 return -EIO;
31
32         return attr->read(kobj, buffer, off, count);
33 }
34
35 static ssize_t
36 read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
37 {
38         char *buffer = file->private_data;
39         struct dentry *dentry = file->f_path.dentry;
40         int size = dentry->d_inode->i_size;
41         loff_t offs = *off;
42         int count = min_t(size_t, bytes, PAGE_SIZE);
43
44         if (size) {
45                 if (offs > size)
46                         return 0;
47                 if (offs + count > size)
48                         count = size - offs;
49         }
50
51         count = fill_read(dentry, buffer, offs, count);
52         if (count < 0)
53                 return count;
54
55         if (copy_to_user(userbuf, buffer, count))
56                 return -EFAULT;
57
58         pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
59
60         *off = offs + count;
61
62         return count;
63 }
64
65 static int
66 flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
67 {
68         struct bin_attribute *attr = to_bin_attr(dentry);
69         struct kobject *kobj = to_kobj(dentry->d_parent);
70
71         if (!attr->write)
72                 return -EIO;
73
74         return attr->write(kobj, buffer, offset, count);
75 }
76
77 static ssize_t write(struct file *file, const char __user *userbuf,
78                      size_t bytes, loff_t *off)
79 {
80         char *buffer = file->private_data;
81         struct dentry *dentry = file->f_path.dentry;
82         int size = dentry->d_inode->i_size;
83         loff_t offs = *off;
84         int count = min_t(size_t, bytes, PAGE_SIZE);
85
86         if (size) {
87                 if (offs > size)
88                         return 0;
89                 if (offs + count > size)
90                         count = size - offs;
91         }
92
93         if (copy_from_user(buffer, userbuf, count))
94                 return -EFAULT;
95
96         count = flush_write(dentry, buffer, offs, count);
97         if (count > 0)
98                 *off = offs + count;
99         return count;
100 }
101
102 static int mmap(struct file *file, struct vm_area_struct *vma)
103 {
104         struct dentry *dentry = file->f_path.dentry;
105         struct bin_attribute *attr = to_bin_attr(dentry);
106         struct kobject *kobj = to_kobj(dentry->d_parent);
107
108         if (!attr->mmap)
109                 return -EINVAL;
110
111         return attr->mmap(kobj, attr, vma);
112 }
113
114 static int open(struct inode * inode, struct file * file)
115 {
116         struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
117         struct bin_attribute * attr = to_bin_attr(file->f_path.dentry);
118         int error = -EINVAL;
119
120         if (!kobj || !attr)
121                 goto Done;
122
123         /* Grab the module reference for this attribute if we have one */
124         error = -ENODEV;
125         if (!try_module_get(attr->attr.owner)) 
126                 goto Done;
127
128         error = -EACCES;
129         if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
130                 goto Error;
131         if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
132                 goto Error;
133
134         error = -ENOMEM;
135         file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
136         if (!file->private_data)
137                 goto Error;
138
139         error = 0;
140     goto Done;
141
142  Error:
143         module_put(attr->attr.owner);
144  Done:
145         if (error)
146                 kobject_put(kobj);
147         return error;
148 }
149
150 static int release(struct inode * inode, struct file * file)
151 {
152         struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent);
153         struct bin_attribute * attr = to_bin_attr(file->f_path.dentry);
154         u8 * buffer = file->private_data;
155
156         kobject_put(kobj);
157         module_put(attr->attr.owner);
158         kfree(buffer);
159         return 0;
160 }
161
162 const struct file_operations bin_fops = {
163         .read           = read,
164         .write          = write,
165         .mmap           = mmap,
166         .llseek         = generic_file_llseek,
167         .open           = open,
168         .release        = release,
169 };
170
171 /**
172  *      sysfs_create_bin_file - create binary file for object.
173  *      @kobj:  object.
174  *      @attr:  attribute descriptor.
175  */
176
177 int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
178 {
179         BUG_ON(!kobj || !kobj->dentry || !attr);
180
181         return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
182 }
183
184
185 /**
186  *      sysfs_remove_bin_file - remove binary file for object.
187  *      @kobj:  object.
188  *      @attr:  attribute descriptor.
189  */
190
191 void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
192 {
193         if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
194                 printk(KERN_ERR "%s: "
195                         "bad dentry or inode or no such file: \"%s\"\n",
196                         __FUNCTION__, attr->attr.name);
197                 dump_stack();
198         }
199 }
200
201 EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
202 EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);