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 |
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')
-rw-r--r-- | fs/sysfs/Makefile | 6 | ||||
-rw-r--r-- | fs/sysfs/bin.c | 204 | ||||
-rw-r--r-- | fs/sysfs/dir.c | 475 | ||||
-rw-r--r-- | fs/sysfs/file.c | 447 | ||||
-rw-r--r-- | fs/sysfs/group.c | 84 | ||||
-rw-r--r-- | fs/sysfs/inode.c | 165 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 107 | ||||
-rw-r--r-- | fs/sysfs/symlink.c | 180 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 95 |
9 files changed, 1763 insertions, 0 deletions
diff --git a/fs/sysfs/Makefile b/fs/sysfs/Makefile new file mode 100644 index 000000000000..7a1ceb946b80 --- /dev/null +++ b/fs/sysfs/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for the sysfs virtual filesystem | ||
3 | # | ||
4 | |||
5 | obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \ | ||
6 | group.o | ||
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c new file mode 100644 index 000000000000..d4aaa88d0214 --- /dev/null +++ b/fs/sysfs/bin.c | |||
@@ -0,0 +1,204 @@ | |||
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/kobject.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #include <asm/uaccess.h> | ||
18 | |||
19 | #include "sysfs.h" | ||
20 | |||
21 | static int | ||
22 | fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) | ||
23 | { | ||
24 | struct bin_attribute * attr = to_bin_attr(dentry); | ||
25 | struct kobject * kobj = to_kobj(dentry->d_parent); | ||
26 | |||
27 | if (!attr->read) | ||
28 | return -EINVAL; | ||
29 | |||
30 | return attr->read(kobj, buffer, off, count); | ||
31 | } | ||
32 | |||
33 | static ssize_t | ||
34 | read(struct file * file, char __user * userbuf, size_t count, loff_t * off) | ||
35 | { | ||
36 | char *buffer = file->private_data; | ||
37 | struct dentry *dentry = file->f_dentry; | ||
38 | int size = dentry->d_inode->i_size; | ||
39 | loff_t offs = *off; | ||
40 | int ret; | ||
41 | |||
42 | if (count > PAGE_SIZE) | ||
43 | count = PAGE_SIZE; | ||
44 | |||
45 | if (size) { | ||
46 | if (offs > size) | ||
47 | return 0; | ||
48 | if (offs + count > size) | ||
49 | count = size - offs; | ||
50 | } | ||
51 | |||
52 | ret = fill_read(dentry, buffer, offs, count); | ||
53 | if (ret < 0) | ||
54 | return ret; | ||
55 | count = ret; | ||
56 | |||
57 | if (copy_to_user(userbuf, buffer, count)) | ||
58 | return -EFAULT; | ||
59 | |||
60 | pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count); | ||
61 | |||
62 | *off = offs + count; | ||
63 | |||
64 | return count; | ||
65 | } | ||
66 | |||
67 | static int | ||
68 | flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) | ||
69 | { | ||
70 | struct bin_attribute *attr = to_bin_attr(dentry); | ||
71 | struct kobject *kobj = to_kobj(dentry->d_parent); | ||
72 | |||
73 | if (!attr->write) | ||
74 | return -EINVAL; | ||
75 | |||
76 | return attr->write(kobj, buffer, offset, count); | ||
77 | } | ||
78 | |||
79 | static ssize_t write(struct file * file, const char __user * userbuf, | ||
80 | size_t count, loff_t * off) | ||
81 | { | ||
82 | char *buffer = file->private_data; | ||
83 | struct dentry *dentry = file->f_dentry; | ||
84 | int size = dentry->d_inode->i_size; | ||
85 | loff_t offs = *off; | ||
86 | |||
87 | if (count > PAGE_SIZE) | ||
88 | count = PAGE_SIZE; | ||
89 | if (size) { | ||
90 | if (offs > size) | ||
91 | return 0; | ||
92 | if (offs + count > size) | ||
93 | count = size - offs; | ||
94 | } | ||
95 | |||
96 | if (copy_from_user(buffer, userbuf, count)) | ||
97 | return -EFAULT; | ||
98 | |||
99 | count = flush_write(dentry, buffer, offs, count); | ||
100 | if (count > 0) | ||
101 | *off = offs + count; | ||
102 | return count; | ||
103 | } | ||
104 | |||
105 | static int mmap(struct file *file, struct vm_area_struct *vma) | ||
106 | { | ||
107 | struct dentry *dentry = file->f_dentry; | ||
108 | struct bin_attribute *attr = to_bin_attr(dentry); | ||
109 | struct kobject *kobj = to_kobj(dentry->d_parent); | ||
110 | |||
111 | if (!attr->mmap) | ||
112 | return -EINVAL; | ||
113 | |||
114 | return attr->mmap(kobj, attr, vma); | ||
115 | } | ||
116 | |||
117 | static int open(struct inode * inode, struct file * file) | ||
118 | { | ||
119 | struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); | ||
120 | struct bin_attribute * attr = to_bin_attr(file->f_dentry); | ||
121 | int error = -EINVAL; | ||
122 | |||
123 | if (!kobj || !attr) | ||
124 | goto Done; | ||
125 | |||
126 | /* Grab the module reference for this attribute if we have one */ | ||
127 | error = -ENODEV; | ||
128 | if (!try_module_get(attr->attr.owner)) | ||
129 | goto Done; | ||
130 | |||
131 | error = -EACCES; | ||
132 | if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) | ||
133 | goto Error; | ||
134 | if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) | ||
135 | goto Error; | ||
136 | |||
137 | error = -ENOMEM; | ||
138 | file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
139 | if (!file->private_data) | ||
140 | goto Error; | ||
141 | |||
142 | error = 0; | ||
143 | goto Done; | ||
144 | |||
145 | Error: | ||
146 | module_put(attr->attr.owner); | ||
147 | Done: | ||
148 | if (error && kobj) | ||
149 | kobject_put(kobj); | ||
150 | return error; | ||
151 | } | ||
152 | |||
153 | static int release(struct inode * inode, struct file * file) | ||
154 | { | ||
155 | struct kobject * kobj = to_kobj(file->f_dentry->d_parent); | ||
156 | struct bin_attribute * attr = to_bin_attr(file->f_dentry); | ||
157 | u8 * buffer = file->private_data; | ||
158 | |||
159 | if (kobj) | ||
160 | kobject_put(kobj); | ||
161 | module_put(attr->attr.owner); | ||
162 | kfree(buffer); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | struct file_operations bin_fops = { | ||
167 | .read = read, | ||
168 | .write = write, | ||
169 | .mmap = mmap, | ||
170 | .llseek = generic_file_llseek, | ||
171 | .open = open, | ||
172 | .release = release, | ||
173 | }; | ||
174 | |||
175 | /** | ||
176 | * sysfs_create_bin_file - create binary file for object. | ||
177 | * @kobj: object. | ||
178 | * @attr: attribute descriptor. | ||
179 | * | ||
180 | */ | ||
181 | |||
182 | int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) | ||
183 | { | ||
184 | BUG_ON(!kobj || !kobj->dentry || !attr); | ||
185 | |||
186 | return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR); | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * sysfs_remove_bin_file - remove binary file for object. | ||
192 | * @kobj: object. | ||
193 | * @attr: attribute descriptor. | ||
194 | * | ||
195 | */ | ||
196 | |||
197 | int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr) | ||
198 | { | ||
199 | sysfs_hash_and_remove(kobj->dentry,attr->attr.name); | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | EXPORT_SYMBOL_GPL(sysfs_create_bin_file); | ||
204 | EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); | ||
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c new file mode 100644 index 000000000000..fe198210bc2d --- /dev/null +++ b/fs/sysfs/dir.c | |||
@@ -0,0 +1,475 @@ | |||
1 | /* | ||
2 | * dir.c - Operations for sysfs directories. | ||
3 | */ | ||
4 | |||
5 | #undef DEBUG | ||
6 | |||
7 | #include <linux/fs.h> | ||
8 | #include <linux/mount.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/kobject.h> | ||
11 | #include "sysfs.h" | ||
12 | |||
13 | DECLARE_RWSEM(sysfs_rename_sem); | ||
14 | |||
15 | static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) | ||
16 | { | ||
17 | struct sysfs_dirent * sd = dentry->d_fsdata; | ||
18 | |||
19 | if (sd) { | ||
20 | BUG_ON(sd->s_dentry != dentry); | ||
21 | sd->s_dentry = NULL; | ||
22 | sysfs_put(sd); | ||
23 | } | ||
24 | iput(inode); | ||
25 | } | ||
26 | |||
27 | static struct dentry_operations sysfs_dentry_ops = { | ||
28 | .d_iput = sysfs_d_iput, | ||
29 | }; | ||
30 | |||
31 | /* | ||
32 | * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent | ||
33 | */ | ||
34 | static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, | ||
35 | void * element) | ||
36 | { | ||
37 | struct sysfs_dirent * sd; | ||
38 | |||
39 | sd = kmem_cache_alloc(sysfs_dir_cachep, GFP_KERNEL); | ||
40 | if (!sd) | ||
41 | return NULL; | ||
42 | |||
43 | memset(sd, 0, sizeof(*sd)); | ||
44 | atomic_set(&sd->s_count, 1); | ||
45 | INIT_LIST_HEAD(&sd->s_children); | ||
46 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
47 | sd->s_element = element; | ||
48 | |||
49 | return sd; | ||
50 | } | ||
51 | |||
52 | int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | ||
53 | void * element, umode_t mode, int type) | ||
54 | { | ||
55 | struct sysfs_dirent * sd; | ||
56 | |||
57 | sd = sysfs_new_dirent(parent_sd, element); | ||
58 | if (!sd) | ||
59 | return -ENOMEM; | ||
60 | |||
61 | sd->s_mode = mode; | ||
62 | sd->s_type = type; | ||
63 | sd->s_dentry = dentry; | ||
64 | if (dentry) { | ||
65 | dentry->d_fsdata = sysfs_get(sd); | ||
66 | dentry->d_op = &sysfs_dentry_ops; | ||
67 | } | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int init_dir(struct inode * inode) | ||
73 | { | ||
74 | inode->i_op = &sysfs_dir_inode_operations; | ||
75 | inode->i_fop = &sysfs_dir_operations; | ||
76 | |||
77 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | ||
78 | inode->i_nlink++; | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int init_file(struct inode * inode) | ||
83 | { | ||
84 | inode->i_size = PAGE_SIZE; | ||
85 | inode->i_fop = &sysfs_file_operations; | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int init_symlink(struct inode * inode) | ||
90 | { | ||
91 | inode->i_op = &sysfs_symlink_inode_operations; | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int create_dir(struct kobject * k, struct dentry * p, | ||
96 | const char * n, struct dentry ** d) | ||
97 | { | ||
98 | int error; | ||
99 | umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; | ||
100 | |||
101 | down(&p->d_inode->i_sem); | ||
102 | *d = sysfs_get_dentry(p,n); | ||
103 | if (!IS_ERR(*d)) { | ||
104 | error = sysfs_create(*d, mode, init_dir); | ||
105 | if (!error) { | ||
106 | error = sysfs_make_dirent(p->d_fsdata, *d, k, mode, | ||
107 | SYSFS_DIR); | ||
108 | if (!error) { | ||
109 | p->d_inode->i_nlink++; | ||
110 | (*d)->d_op = &sysfs_dentry_ops; | ||
111 | d_rehash(*d); | ||
112 | } | ||
113 | } | ||
114 | if (error && (error != -EEXIST)) | ||
115 | d_drop(*d); | ||
116 | dput(*d); | ||
117 | } else | ||
118 | error = PTR_ERR(*d); | ||
119 | up(&p->d_inode->i_sem); | ||
120 | return error; | ||
121 | } | ||
122 | |||
123 | |||
124 | int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d) | ||
125 | { | ||
126 | return create_dir(k,k->dentry,n,d); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * sysfs_create_dir - create a directory for an object. | ||
131 | * @parent: parent parent object. | ||
132 | * @kobj: object we're creating directory for. | ||
133 | */ | ||
134 | |||
135 | int sysfs_create_dir(struct kobject * kobj) | ||
136 | { | ||
137 | struct dentry * dentry = NULL; | ||
138 | struct dentry * parent; | ||
139 | int error = 0; | ||
140 | |||
141 | BUG_ON(!kobj); | ||
142 | |||
143 | if (kobj->parent) | ||
144 | parent = kobj->parent->dentry; | ||
145 | else if (sysfs_mount && sysfs_mount->mnt_sb) | ||
146 | parent = sysfs_mount->mnt_sb->s_root; | ||
147 | else | ||
148 | return -EFAULT; | ||
149 | |||
150 | error = create_dir(kobj,parent,kobject_name(kobj),&dentry); | ||
151 | if (!error) | ||
152 | kobj->dentry = dentry; | ||
153 | return error; | ||
154 | } | ||
155 | |||
156 | /* attaches attribute's sysfs_dirent to the dentry corresponding to the | ||
157 | * attribute file | ||
158 | */ | ||
159 | static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) | ||
160 | { | ||
161 | struct attribute * attr = NULL; | ||
162 | struct bin_attribute * bin_attr = NULL; | ||
163 | int (* init) (struct inode *) = NULL; | ||
164 | int error = 0; | ||
165 | |||
166 | if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { | ||
167 | bin_attr = sd->s_element; | ||
168 | attr = &bin_attr->attr; | ||
169 | } else { | ||
170 | attr = sd->s_element; | ||
171 | init = init_file; | ||
172 | } | ||
173 | |||
174 | error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init); | ||
175 | if (error) | ||
176 | return error; | ||
177 | |||
178 | if (bin_attr) { | ||
179 | dentry->d_inode->i_size = bin_attr->size; | ||
180 | dentry->d_inode->i_fop = &bin_fops; | ||
181 | } | ||
182 | dentry->d_op = &sysfs_dentry_ops; | ||
183 | dentry->d_fsdata = sysfs_get(sd); | ||
184 | sd->s_dentry = dentry; | ||
185 | d_rehash(dentry); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry) | ||
191 | { | ||
192 | int err = 0; | ||
193 | |||
194 | err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink); | ||
195 | if (!err) { | ||
196 | dentry->d_op = &sysfs_dentry_ops; | ||
197 | dentry->d_fsdata = sysfs_get(sd); | ||
198 | sd->s_dentry = dentry; | ||
199 | d_rehash(dentry); | ||
200 | } | ||
201 | return err; | ||
202 | } | ||
203 | |||
204 | static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, | ||
205 | struct nameidata *nd) | ||
206 | { | ||
207 | struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata; | ||
208 | struct sysfs_dirent * sd; | ||
209 | int err = 0; | ||
210 | |||
211 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | ||
212 | if (sd->s_type & SYSFS_NOT_PINNED) { | ||
213 | const unsigned char * name = sysfs_get_name(sd); | ||
214 | |||
215 | if (strcmp(name, dentry->d_name.name)) | ||
216 | continue; | ||
217 | |||
218 | if (sd->s_type & SYSFS_KOBJ_LINK) | ||
219 | err = sysfs_attach_link(sd, dentry); | ||
220 | else | ||
221 | err = sysfs_attach_attr(sd, dentry); | ||
222 | break; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | return ERR_PTR(err); | ||
227 | } | ||
228 | |||
229 | struct inode_operations sysfs_dir_inode_operations = { | ||
230 | .lookup = sysfs_lookup, | ||
231 | }; | ||
232 | |||
233 | static void remove_dir(struct dentry * d) | ||
234 | { | ||
235 | struct dentry * parent = dget(d->d_parent); | ||
236 | struct sysfs_dirent * sd; | ||
237 | |||
238 | down(&parent->d_inode->i_sem); | ||
239 | d_delete(d); | ||
240 | sd = d->d_fsdata; | ||
241 | list_del_init(&sd->s_sibling); | ||
242 | sysfs_put(sd); | ||
243 | if (d->d_inode) | ||
244 | simple_rmdir(parent->d_inode,d); | ||
245 | |||
246 | pr_debug(" o %s removing done (%d)\n",d->d_name.name, | ||
247 | atomic_read(&d->d_count)); | ||
248 | |||
249 | up(&parent->d_inode->i_sem); | ||
250 | dput(parent); | ||
251 | } | ||
252 | |||
253 | void sysfs_remove_subdir(struct dentry * d) | ||
254 | { | ||
255 | remove_dir(d); | ||
256 | } | ||
257 | |||
258 | |||
259 | /** | ||
260 | * sysfs_remove_dir - remove an object's directory. | ||
261 | * @kobj: object. | ||
262 | * | ||
263 | * The only thing special about this is that we remove any files in | ||
264 | * the directory before we remove the directory, and we've inlined | ||
265 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
266 | */ | ||
267 | |||
268 | void sysfs_remove_dir(struct kobject * kobj) | ||
269 | { | ||
270 | struct dentry * dentry = dget(kobj->dentry); | ||
271 | struct sysfs_dirent * parent_sd; | ||
272 | struct sysfs_dirent * sd, * tmp; | ||
273 | |||
274 | if (!dentry) | ||
275 | return; | ||
276 | |||
277 | pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); | ||
278 | down(&dentry->d_inode->i_sem); | ||
279 | parent_sd = dentry->d_fsdata; | ||
280 | list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { | ||
281 | if (!sd->s_element || !(sd->s_type & SYSFS_NOT_PINNED)) | ||
282 | continue; | ||
283 | list_del_init(&sd->s_sibling); | ||
284 | sysfs_drop_dentry(sd, dentry); | ||
285 | sysfs_put(sd); | ||
286 | } | ||
287 | up(&dentry->d_inode->i_sem); | ||
288 | |||
289 | remove_dir(dentry); | ||
290 | /** | ||
291 | * Drop reference from dget() on entrance. | ||
292 | */ | ||
293 | dput(dentry); | ||
294 | } | ||
295 | |||
296 | int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | ||
297 | { | ||
298 | int error = 0; | ||
299 | struct dentry * new_dentry, * parent; | ||
300 | |||
301 | if (!strcmp(kobject_name(kobj), new_name)) | ||
302 | return -EINVAL; | ||
303 | |||
304 | if (!kobj->parent) | ||
305 | return -EINVAL; | ||
306 | |||
307 | down_write(&sysfs_rename_sem); | ||
308 | parent = kobj->parent->dentry; | ||
309 | |||
310 | down(&parent->d_inode->i_sem); | ||
311 | |||
312 | new_dentry = sysfs_get_dentry(parent, new_name); | ||
313 | if (!IS_ERR(new_dentry)) { | ||
314 | if (!new_dentry->d_inode) { | ||
315 | error = kobject_set_name(kobj, "%s", new_name); | ||
316 | if (!error) { | ||
317 | d_add(new_dentry, NULL); | ||
318 | d_move(kobj->dentry, new_dentry); | ||
319 | } | ||
320 | else | ||
321 | d_drop(new_dentry); | ||
322 | } else | ||
323 | error = -EEXIST; | ||
324 | dput(new_dentry); | ||
325 | } | ||
326 | up(&parent->d_inode->i_sem); | ||
327 | up_write(&sysfs_rename_sem); | ||
328 | |||
329 | return error; | ||
330 | } | ||
331 | |||
332 | static int sysfs_dir_open(struct inode *inode, struct file *file) | ||
333 | { | ||
334 | struct dentry * dentry = file->f_dentry; | ||
335 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; | ||
336 | |||
337 | down(&dentry->d_inode->i_sem); | ||
338 | file->private_data = sysfs_new_dirent(parent_sd, NULL); | ||
339 | up(&dentry->d_inode->i_sem); | ||
340 | |||
341 | return file->private_data ? 0 : -ENOMEM; | ||
342 | |||
343 | } | ||
344 | |||
345 | static int sysfs_dir_close(struct inode *inode, struct file *file) | ||
346 | { | ||
347 | struct dentry * dentry = file->f_dentry; | ||
348 | struct sysfs_dirent * cursor = file->private_data; | ||
349 | |||
350 | down(&dentry->d_inode->i_sem); | ||
351 | list_del_init(&cursor->s_sibling); | ||
352 | up(&dentry->d_inode->i_sem); | ||
353 | |||
354 | release_sysfs_dirent(cursor); | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | /* Relationship between s_mode and the DT_xxx types */ | ||
360 | static inline unsigned char dt_type(struct sysfs_dirent *sd) | ||
361 | { | ||
362 | return (sd->s_mode >> 12) & 15; | ||
363 | } | ||
364 | |||
365 | static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | ||
366 | { | ||
367 | struct dentry *dentry = filp->f_dentry; | ||
368 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; | ||
369 | struct sysfs_dirent *cursor = filp->private_data; | ||
370 | struct list_head *p, *q = &cursor->s_sibling; | ||
371 | ino_t ino; | ||
372 | int i = filp->f_pos; | ||
373 | |||
374 | switch (i) { | ||
375 | case 0: | ||
376 | ino = dentry->d_inode->i_ino; | ||
377 | if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) | ||
378 | break; | ||
379 | filp->f_pos++; | ||
380 | i++; | ||
381 | /* fallthrough */ | ||
382 | case 1: | ||
383 | ino = parent_ino(dentry); | ||
384 | if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) | ||
385 | break; | ||
386 | filp->f_pos++; | ||
387 | i++; | ||
388 | /* fallthrough */ | ||
389 | default: | ||
390 | if (filp->f_pos == 2) { | ||
391 | list_del(q); | ||
392 | list_add(q, &parent_sd->s_children); | ||
393 | } | ||
394 | for (p=q->next; p!= &parent_sd->s_children; p=p->next) { | ||
395 | struct sysfs_dirent *next; | ||
396 | const char * name; | ||
397 | int len; | ||
398 | |||
399 | next = list_entry(p, struct sysfs_dirent, | ||
400 | s_sibling); | ||
401 | if (!next->s_element) | ||
402 | continue; | ||
403 | |||
404 | name = sysfs_get_name(next); | ||
405 | len = strlen(name); | ||
406 | if (next->s_dentry) | ||
407 | ino = next->s_dentry->d_inode->i_ino; | ||
408 | else | ||
409 | ino = iunique(sysfs_sb, 2); | ||
410 | |||
411 | if (filldir(dirent, name, len, filp->f_pos, ino, | ||
412 | dt_type(next)) < 0) | ||
413 | return 0; | ||
414 | |||
415 | list_del(q); | ||
416 | list_add(q, p); | ||
417 | p = q; | ||
418 | filp->f_pos++; | ||
419 | } | ||
420 | } | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) | ||
425 | { | ||
426 | struct dentry * dentry = file->f_dentry; | ||
427 | |||
428 | down(&dentry->d_inode->i_sem); | ||
429 | switch (origin) { | ||
430 | case 1: | ||
431 | offset += file->f_pos; | ||
432 | case 0: | ||
433 | if (offset >= 0) | ||
434 | break; | ||
435 | default: | ||
436 | up(&file->f_dentry->d_inode->i_sem); | ||
437 | return -EINVAL; | ||
438 | } | ||
439 | if (offset != file->f_pos) { | ||
440 | file->f_pos = offset; | ||
441 | if (file->f_pos >= 2) { | ||
442 | struct sysfs_dirent *sd = dentry->d_fsdata; | ||
443 | struct sysfs_dirent *cursor = file->private_data; | ||
444 | struct list_head *p; | ||
445 | loff_t n = file->f_pos - 2; | ||
446 | |||
447 | list_del(&cursor->s_sibling); | ||
448 | p = sd->s_children.next; | ||
449 | while (n && p != &sd->s_children) { | ||
450 | struct sysfs_dirent *next; | ||
451 | next = list_entry(p, struct sysfs_dirent, | ||
452 | s_sibling); | ||
453 | if (next->s_element) | ||
454 | n--; | ||
455 | p = p->next; | ||
456 | } | ||
457 | list_add_tail(&cursor->s_sibling, p); | ||
458 | } | ||
459 | } | ||
460 | up(&dentry->d_inode->i_sem); | ||
461 | return offset; | ||
462 | } | ||
463 | |||
464 | struct file_operations sysfs_dir_operations = { | ||
465 | .open = sysfs_dir_open, | ||
466 | .release = sysfs_dir_close, | ||
467 | .llseek = sysfs_dir_lseek, | ||
468 | .read = generic_read_dir, | ||
469 | .readdir = sysfs_readdir, | ||
470 | }; | ||
471 | |||
472 | EXPORT_SYMBOL_GPL(sysfs_create_dir); | ||
473 | EXPORT_SYMBOL_GPL(sysfs_remove_dir); | ||
474 | EXPORT_SYMBOL_GPL(sysfs_rename_dir); | ||
475 | |||
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 | |||
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c new file mode 100644 index 000000000000..f11ac5ea7021 --- /dev/null +++ b/fs/sysfs/group.c | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * fs/sysfs/group.c - Operations for adding/removing multiple files at once. | ||
3 | * | ||
4 | * Copyright (c) 2003 Patrick Mochel | ||
5 | * Copyright (c) 2003 Open Source Development Lab | ||
6 | * | ||
7 | * This file is released undert the GPL v2. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/kobject.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/dcache.h> | ||
14 | #include <linux/err.h> | ||
15 | #include "sysfs.h" | ||
16 | |||
17 | |||
18 | static void remove_files(struct dentry * dir, | ||
19 | const struct attribute_group * grp) | ||
20 | { | ||
21 | struct attribute *const* attr; | ||
22 | |||
23 | for (attr = grp->attrs; *attr; attr++) | ||
24 | sysfs_hash_and_remove(dir,(*attr)->name); | ||
25 | } | ||
26 | |||
27 | static int create_files(struct dentry * dir, | ||
28 | const struct attribute_group * grp) | ||
29 | { | ||
30 | struct attribute *const* attr; | ||
31 | int error = 0; | ||
32 | |||
33 | for (attr = grp->attrs; *attr && !error; attr++) { | ||
34 | error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR); | ||
35 | } | ||
36 | if (error) | ||
37 | remove_files(dir,grp); | ||
38 | return error; | ||
39 | } | ||
40 | |||
41 | |||
42 | int sysfs_create_group(struct kobject * kobj, | ||
43 | const struct attribute_group * grp) | ||
44 | { | ||
45 | struct dentry * dir; | ||
46 | int error; | ||
47 | |||
48 | BUG_ON(!kobj || !kobj->dentry); | ||
49 | |||
50 | if (grp->name) { | ||
51 | error = sysfs_create_subdir(kobj,grp->name,&dir); | ||
52 | if (error) | ||
53 | return error; | ||
54 | } else | ||
55 | dir = kobj->dentry; | ||
56 | dir = dget(dir); | ||
57 | if ((error = create_files(dir,grp))) { | ||
58 | if (grp->name) | ||
59 | sysfs_remove_subdir(dir); | ||
60 | } | ||
61 | dput(dir); | ||
62 | return error; | ||
63 | } | ||
64 | |||
65 | void sysfs_remove_group(struct kobject * kobj, | ||
66 | const struct attribute_group * grp) | ||
67 | { | ||
68 | struct dentry * dir; | ||
69 | |||
70 | if (grp->name) | ||
71 | dir = sysfs_get_dentry(kobj->dentry,grp->name); | ||
72 | else | ||
73 | dir = dget(kobj->dentry); | ||
74 | |||
75 | remove_files(dir,grp); | ||
76 | if (grp->name) | ||
77 | sysfs_remove_subdir(dir); | ||
78 | /* release the ref. taken in this routine */ | ||
79 | dput(dir); | ||
80 | } | ||
81 | |||
82 | |||
83 | EXPORT_SYMBOL_GPL(sysfs_create_group); | ||
84 | EXPORT_SYMBOL_GPL(sysfs_remove_group); | ||
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c new file mode 100644 index 000000000000..aff7b2dfa8ee --- /dev/null +++ b/fs/sysfs/inode.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * inode.c - basic inode and dentry operations. | ||
3 | * | ||
4 | * sysfs is Copyright (c) 2001-3 Patrick Mochel | ||
5 | * | ||
6 | * Please see Documentation/filesystems/sysfs.txt for more information. | ||
7 | */ | ||
8 | |||
9 | #undef DEBUG | ||
10 | |||
11 | #include <linux/pagemap.h> | ||
12 | #include <linux/namei.h> | ||
13 | #include <linux/backing-dev.h> | ||
14 | #include "sysfs.h" | ||
15 | |||
16 | extern struct super_block * sysfs_sb; | ||
17 | |||
18 | static struct address_space_operations sysfs_aops = { | ||
19 | .readpage = simple_readpage, | ||
20 | .prepare_write = simple_prepare_write, | ||
21 | .commit_write = simple_commit_write | ||
22 | }; | ||
23 | |||
24 | static struct backing_dev_info sysfs_backing_dev_info = { | ||
25 | .ra_pages = 0, /* No readahead */ | ||
26 | .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | ||
27 | }; | ||
28 | |||
29 | struct inode * sysfs_new_inode(mode_t mode) | ||
30 | { | ||
31 | struct inode * inode = new_inode(sysfs_sb); | ||
32 | if (inode) { | ||
33 | inode->i_mode = mode; | ||
34 | inode->i_uid = 0; | ||
35 | inode->i_gid = 0; | ||
36 | inode->i_blksize = PAGE_CACHE_SIZE; | ||
37 | inode->i_blocks = 0; | ||
38 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
39 | inode->i_mapping->a_ops = &sysfs_aops; | ||
40 | inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; | ||
41 | } | ||
42 | return inode; | ||
43 | } | ||
44 | |||
45 | int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) | ||
46 | { | ||
47 | int error = 0; | ||
48 | struct inode * inode = NULL; | ||
49 | if (dentry) { | ||
50 | if (!dentry->d_inode) { | ||
51 | if ((inode = sysfs_new_inode(mode))) { | ||
52 | if (dentry->d_parent && dentry->d_parent->d_inode) { | ||
53 | struct inode *p_inode = dentry->d_parent->d_inode; | ||
54 | p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; | ||
55 | } | ||
56 | goto Proceed; | ||
57 | } | ||
58 | else | ||
59 | error = -ENOMEM; | ||
60 | } else | ||
61 | error = -EEXIST; | ||
62 | } else | ||
63 | error = -ENOENT; | ||
64 | goto Done; | ||
65 | |||
66 | Proceed: | ||
67 | if (init) | ||
68 | error = init(inode); | ||
69 | if (!error) { | ||
70 | d_instantiate(dentry, inode); | ||
71 | if (S_ISDIR(mode)) | ||
72 | dget(dentry); /* pin only directory dentry in core */ | ||
73 | } else | ||
74 | iput(inode); | ||
75 | Done: | ||
76 | return error; | ||
77 | } | ||
78 | |||
79 | struct dentry * sysfs_get_dentry(struct dentry * parent, const char * name) | ||
80 | { | ||
81 | struct qstr qstr; | ||
82 | |||
83 | qstr.name = name; | ||
84 | qstr.len = strlen(name); | ||
85 | qstr.hash = full_name_hash(name,qstr.len); | ||
86 | return lookup_hash(&qstr,parent); | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Get the name for corresponding element represented by the given sysfs_dirent | ||
91 | */ | ||
92 | const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) | ||
93 | { | ||
94 | struct attribute * attr; | ||
95 | struct bin_attribute * bin_attr; | ||
96 | struct sysfs_symlink * sl; | ||
97 | |||
98 | if (!sd || !sd->s_element) | ||
99 | BUG(); | ||
100 | |||
101 | switch (sd->s_type) { | ||
102 | case SYSFS_DIR: | ||
103 | /* Always have a dentry so use that */ | ||
104 | return sd->s_dentry->d_name.name; | ||
105 | |||
106 | case SYSFS_KOBJ_ATTR: | ||
107 | attr = sd->s_element; | ||
108 | return attr->name; | ||
109 | |||
110 | case SYSFS_KOBJ_BIN_ATTR: | ||
111 | bin_attr = sd->s_element; | ||
112 | return bin_attr->attr.name; | ||
113 | |||
114 | case SYSFS_KOBJ_LINK: | ||
115 | sl = sd->s_element; | ||
116 | return sl->link_name; | ||
117 | } | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | |||
122 | /* | ||
123 | * Unhashes the dentry corresponding to given sysfs_dirent | ||
124 | * Called with parent inode's i_sem held. | ||
125 | */ | ||
126 | void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | ||
127 | { | ||
128 | struct dentry * dentry = sd->s_dentry; | ||
129 | |||
130 | if (dentry) { | ||
131 | spin_lock(&dcache_lock); | ||
132 | spin_lock(&dentry->d_lock); | ||
133 | if (!(d_unhashed(dentry) && dentry->d_inode)) { | ||
134 | dget_locked(dentry); | ||
135 | __d_drop(dentry); | ||
136 | spin_unlock(&dentry->d_lock); | ||
137 | spin_unlock(&dcache_lock); | ||
138 | simple_unlink(parent->d_inode, dentry); | ||
139 | } else { | ||
140 | spin_unlock(&dentry->d_lock); | ||
141 | spin_unlock(&dcache_lock); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | void sysfs_hash_and_remove(struct dentry * dir, const char * name) | ||
147 | { | ||
148 | struct sysfs_dirent * sd; | ||
149 | struct sysfs_dirent * parent_sd = dir->d_fsdata; | ||
150 | |||
151 | down(&dir->d_inode->i_sem); | ||
152 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | ||
153 | if (!sd->s_element) | ||
154 | continue; | ||
155 | if (!strcmp(sysfs_get_name(sd), name)) { | ||
156 | list_del_init(&sd->s_sibling); | ||
157 | sysfs_drop_dentry(sd, dir); | ||
158 | sysfs_put(sd); | ||
159 | break; | ||
160 | } | ||
161 | } | ||
162 | up(&dir->d_inode->i_sem); | ||
163 | } | ||
164 | |||
165 | |||
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c new file mode 100644 index 000000000000..5c805bb1a4b7 --- /dev/null +++ b/fs/sysfs/mount.c | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * mount.c - operations for initializing and mounting sysfs. | ||
3 | */ | ||
4 | |||
5 | #define DEBUG | ||
6 | |||
7 | #include <linux/fs.h> | ||
8 | #include <linux/mount.h> | ||
9 | #include <linux/pagemap.h> | ||
10 | #include <linux/init.h> | ||
11 | |||
12 | #include "sysfs.h" | ||
13 | |||
14 | /* Random magic number */ | ||
15 | #define SYSFS_MAGIC 0x62656572 | ||
16 | |||
17 | struct vfsmount *sysfs_mount; | ||
18 | struct super_block * sysfs_sb = NULL; | ||
19 | kmem_cache_t *sysfs_dir_cachep; | ||
20 | |||
21 | static struct super_operations sysfs_ops = { | ||
22 | .statfs = simple_statfs, | ||
23 | .drop_inode = generic_delete_inode, | ||
24 | }; | ||
25 | |||
26 | static struct sysfs_dirent sysfs_root = { | ||
27 | .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), | ||
28 | .s_children = LIST_HEAD_INIT(sysfs_root.s_children), | ||
29 | .s_element = NULL, | ||
30 | .s_type = SYSFS_ROOT, | ||
31 | }; | ||
32 | |||
33 | static int sysfs_fill_super(struct super_block *sb, void *data, int silent) | ||
34 | { | ||
35 | struct inode *inode; | ||
36 | struct dentry *root; | ||
37 | |||
38 | sb->s_blocksize = PAGE_CACHE_SIZE; | ||
39 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | ||
40 | sb->s_magic = SYSFS_MAGIC; | ||
41 | sb->s_op = &sysfs_ops; | ||
42 | sb->s_time_gran = 1; | ||
43 | sysfs_sb = sb; | ||
44 | |||
45 | inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); | ||
46 | if (inode) { | ||
47 | inode->i_op = &sysfs_dir_inode_operations; | ||
48 | inode->i_fop = &sysfs_dir_operations; | ||
49 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | ||
50 | inode->i_nlink++; | ||
51 | } else { | ||
52 | pr_debug("sysfs: could not get root inode\n"); | ||
53 | return -ENOMEM; | ||
54 | } | ||
55 | |||
56 | root = d_alloc_root(inode); | ||
57 | if (!root) { | ||
58 | pr_debug("%s: could not get root dentry!\n",__FUNCTION__); | ||
59 | iput(inode); | ||
60 | return -ENOMEM; | ||
61 | } | ||
62 | root->d_fsdata = &sysfs_root; | ||
63 | sb->s_root = root; | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static struct super_block *sysfs_get_sb(struct file_system_type *fs_type, | ||
68 | int flags, const char *dev_name, void *data) | ||
69 | { | ||
70 | return get_sb_single(fs_type, flags, data, sysfs_fill_super); | ||
71 | } | ||
72 | |||
73 | static struct file_system_type sysfs_fs_type = { | ||
74 | .name = "sysfs", | ||
75 | .get_sb = sysfs_get_sb, | ||
76 | .kill_sb = kill_litter_super, | ||
77 | }; | ||
78 | |||
79 | int __init sysfs_init(void) | ||
80 | { | ||
81 | int err = -ENOMEM; | ||
82 | |||
83 | sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", | ||
84 | sizeof(struct sysfs_dirent), | ||
85 | 0, 0, NULL, NULL); | ||
86 | if (!sysfs_dir_cachep) | ||
87 | goto out; | ||
88 | |||
89 | err = register_filesystem(&sysfs_fs_type); | ||
90 | if (!err) { | ||
91 | sysfs_mount = kern_mount(&sysfs_fs_type); | ||
92 | if (IS_ERR(sysfs_mount)) { | ||
93 | printk(KERN_ERR "sysfs: could not mount!\n"); | ||
94 | err = PTR_ERR(sysfs_mount); | ||
95 | sysfs_mount = NULL; | ||
96 | unregister_filesystem(&sysfs_fs_type); | ||
97 | goto out_err; | ||
98 | } | ||
99 | } else | ||
100 | goto out_err; | ||
101 | out: | ||
102 | return err; | ||
103 | out_err: | ||
104 | kmem_cache_destroy(sysfs_dir_cachep); | ||
105 | sysfs_dir_cachep = NULL; | ||
106 | goto out; | ||
107 | } | ||
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c new file mode 100644 index 000000000000..dfdf70174354 --- /dev/null +++ b/fs/sysfs/symlink.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * symlink.c - operations for sysfs symlinks. | ||
3 | */ | ||
4 | |||
5 | #include <linux/fs.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/kobject.h> | ||
8 | #include <linux/namei.h> | ||
9 | |||
10 | #include "sysfs.h" | ||
11 | |||
12 | static int object_depth(struct kobject * kobj) | ||
13 | { | ||
14 | struct kobject * p = kobj; | ||
15 | int depth = 0; | ||
16 | do { depth++; } while ((p = p->parent)); | ||
17 | return depth; | ||
18 | } | ||
19 | |||
20 | static int object_path_length(struct kobject * kobj) | ||
21 | { | ||
22 | struct kobject * p = kobj; | ||
23 | int length = 1; | ||
24 | do { | ||
25 | length += strlen(kobject_name(p)) + 1; | ||
26 | p = p->parent; | ||
27 | } while (p); | ||
28 | return length; | ||
29 | } | ||
30 | |||
31 | static void fill_object_path(struct kobject * kobj, char * buffer, int length) | ||
32 | { | ||
33 | struct kobject * p; | ||
34 | |||
35 | --length; | ||
36 | for (p = kobj; p; p = p->parent) { | ||
37 | int cur = strlen(kobject_name(p)); | ||
38 | |||
39 | /* back up enough to print this bus id with '/' */ | ||
40 | length -= cur; | ||
41 | strncpy(buffer + length,kobject_name(p),cur); | ||
42 | *(buffer + --length) = '/'; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | static int sysfs_add_link(struct dentry * parent, char * name, struct kobject * target) | ||
47 | { | ||
48 | struct sysfs_dirent * parent_sd = parent->d_fsdata; | ||
49 | struct sysfs_symlink * sl; | ||
50 | int error = 0; | ||
51 | |||
52 | error = -ENOMEM; | ||
53 | sl = kmalloc(sizeof(*sl), GFP_KERNEL); | ||
54 | if (!sl) | ||
55 | goto exit1; | ||
56 | |||
57 | sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); | ||
58 | if (!sl->link_name) | ||
59 | goto exit2; | ||
60 | |||
61 | strcpy(sl->link_name, name); | ||
62 | sl->target_kobj = kobject_get(target); | ||
63 | |||
64 | error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, | ||
65 | SYSFS_KOBJ_LINK); | ||
66 | if (!error) | ||
67 | return 0; | ||
68 | |||
69 | kfree(sl->link_name); | ||
70 | exit2: | ||
71 | kfree(sl); | ||
72 | exit1: | ||
73 | return error; | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * sysfs_create_link - create symlink between two objects. | ||
78 | * @kobj: object whose directory we're creating the link in. | ||
79 | * @target: object we're pointing to. | ||
80 | * @name: name of the symlink. | ||
81 | */ | ||
82 | int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name) | ||
83 | { | ||
84 | struct dentry * dentry = kobj->dentry; | ||
85 | int error = 0; | ||
86 | |||
87 | BUG_ON(!kobj || !kobj->dentry || !name); | ||
88 | |||
89 | down(&dentry->d_inode->i_sem); | ||
90 | error = sysfs_add_link(dentry, name, target); | ||
91 | up(&dentry->d_inode->i_sem); | ||
92 | return error; | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * sysfs_remove_link - remove symlink in object's directory. | ||
98 | * @kobj: object we're acting for. | ||
99 | * @name: name of the symlink to remove. | ||
100 | */ | ||
101 | |||
102 | void sysfs_remove_link(struct kobject * kobj, char * name) | ||
103 | { | ||
104 | sysfs_hash_and_remove(kobj->dentry,name); | ||
105 | } | ||
106 | |||
107 | static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, | ||
108 | char *path) | ||
109 | { | ||
110 | char * s; | ||
111 | int depth, size; | ||
112 | |||
113 | depth = object_depth(kobj); | ||
114 | size = object_path_length(target) + depth * 3 - 1; | ||
115 | if (size > PATH_MAX) | ||
116 | return -ENAMETOOLONG; | ||
117 | |||
118 | pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size); | ||
119 | |||
120 | for (s = path; depth--; s += 3) | ||
121 | strcpy(s,"../"); | ||
122 | |||
123 | fill_object_path(target, path, size); | ||
124 | pr_debug("%s: path = '%s'\n", __FUNCTION__, path); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static int sysfs_getlink(struct dentry *dentry, char * path) | ||
130 | { | ||
131 | struct kobject *kobj, *target_kobj; | ||
132 | int error = 0; | ||
133 | |||
134 | kobj = sysfs_get_kobject(dentry->d_parent); | ||
135 | if (!kobj) | ||
136 | return -EINVAL; | ||
137 | |||
138 | target_kobj = sysfs_get_kobject(dentry); | ||
139 | if (!target_kobj) { | ||
140 | kobject_put(kobj); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | down_read(&sysfs_rename_sem); | ||
145 | error = sysfs_get_target_path(kobj, target_kobj, path); | ||
146 | up_read(&sysfs_rename_sem); | ||
147 | |||
148 | kobject_put(kobj); | ||
149 | kobject_put(target_kobj); | ||
150 | return error; | ||
151 | |||
152 | } | ||
153 | |||
154 | static int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
155 | { | ||
156 | int error = -ENOMEM; | ||
157 | unsigned long page = get_zeroed_page(GFP_KERNEL); | ||
158 | if (page) | ||
159 | error = sysfs_getlink(dentry, (char *) page); | ||
160 | nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd) | ||
165 | { | ||
166 | char *page = nd_get_link(nd); | ||
167 | if (!IS_ERR(page)) | ||
168 | free_page((unsigned long)page); | ||
169 | } | ||
170 | |||
171 | struct inode_operations sysfs_symlink_inode_operations = { | ||
172 | .readlink = generic_readlink, | ||
173 | .follow_link = sysfs_follow_link, | ||
174 | .put_link = sysfs_put_link, | ||
175 | }; | ||
176 | |||
177 | |||
178 | EXPORT_SYMBOL_GPL(sysfs_create_link); | ||
179 | EXPORT_SYMBOL_GPL(sysfs_remove_link); | ||
180 | |||
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h new file mode 100644 index 000000000000..a8a24a0c0b3b --- /dev/null +++ b/fs/sysfs/sysfs.h | |||
@@ -0,0 +1,95 @@ | |||
1 | |||
2 | extern struct vfsmount * sysfs_mount; | ||
3 | extern kmem_cache_t *sysfs_dir_cachep; | ||
4 | |||
5 | extern struct inode * sysfs_new_inode(mode_t mode); | ||
6 | extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); | ||
7 | |||
8 | extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, | ||
9 | umode_t, int); | ||
10 | extern struct dentry * sysfs_get_dentry(struct dentry *, const char *); | ||
11 | |||
12 | extern int sysfs_add_file(struct dentry *, const struct attribute *, int); | ||
13 | extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); | ||
14 | |||
15 | extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); | ||
16 | extern void sysfs_remove_subdir(struct dentry *); | ||
17 | |||
18 | extern const unsigned char * sysfs_get_name(struct sysfs_dirent *sd); | ||
19 | extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); | ||
20 | |||
21 | extern struct rw_semaphore sysfs_rename_sem; | ||
22 | extern struct super_block * sysfs_sb; | ||
23 | extern struct file_operations sysfs_dir_operations; | ||
24 | extern struct file_operations sysfs_file_operations; | ||
25 | extern struct file_operations bin_fops; | ||
26 | extern struct inode_operations sysfs_dir_inode_operations; | ||
27 | extern struct inode_operations sysfs_symlink_inode_operations; | ||
28 | |||
29 | struct sysfs_symlink { | ||
30 | char * link_name; | ||
31 | struct kobject * target_kobj; | ||
32 | }; | ||
33 | |||
34 | static inline struct kobject * to_kobj(struct dentry * dentry) | ||
35 | { | ||
36 | struct sysfs_dirent * sd = dentry->d_fsdata; | ||
37 | return ((struct kobject *) sd->s_element); | ||
38 | } | ||
39 | |||
40 | static inline struct attribute * to_attr(struct dentry * dentry) | ||
41 | { | ||
42 | struct sysfs_dirent * sd = dentry->d_fsdata; | ||
43 | return ((struct attribute *) sd->s_element); | ||
44 | } | ||
45 | |||
46 | static inline struct bin_attribute * to_bin_attr(struct dentry * dentry) | ||
47 | { | ||
48 | struct sysfs_dirent * sd = dentry->d_fsdata; | ||
49 | return ((struct bin_attribute *) sd->s_element); | ||
50 | } | ||
51 | |||
52 | static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) | ||
53 | { | ||
54 | struct kobject * kobj = NULL; | ||
55 | |||
56 | spin_lock(&dcache_lock); | ||
57 | if (!d_unhashed(dentry)) { | ||
58 | struct sysfs_dirent * sd = dentry->d_fsdata; | ||
59 | if (sd->s_type & SYSFS_KOBJ_LINK) { | ||
60 | struct sysfs_symlink * sl = sd->s_element; | ||
61 | kobj = kobject_get(sl->target_kobj); | ||
62 | } else | ||
63 | kobj = kobject_get(sd->s_element); | ||
64 | } | ||
65 | spin_unlock(&dcache_lock); | ||
66 | |||
67 | return kobj; | ||
68 | } | ||
69 | |||
70 | static inline void release_sysfs_dirent(struct sysfs_dirent * sd) | ||
71 | { | ||
72 | if (sd->s_type & SYSFS_KOBJ_LINK) { | ||
73 | struct sysfs_symlink * sl = sd->s_element; | ||
74 | kfree(sl->link_name); | ||
75 | kobject_put(sl->target_kobj); | ||
76 | kfree(sl); | ||
77 | } | ||
78 | kmem_cache_free(sysfs_dir_cachep, sd); | ||
79 | } | ||
80 | |||
81 | static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) | ||
82 | { | ||
83 | if (sd) { | ||
84 | WARN_ON(!atomic_read(&sd->s_count)); | ||
85 | atomic_inc(&sd->s_count); | ||
86 | } | ||
87 | return sd; | ||
88 | } | ||
89 | |||
90 | static inline void sysfs_put(struct sysfs_dirent * sd) | ||
91 | { | ||
92 | if (atomic_dec_and_test(&sd->s_count)) | ||
93 | release_sysfs_dirent(sd); | ||
94 | } | ||
95 | |||