Commit 7063fbf2261194f72ee75afca67b3b38b554b5fa
1 parent
88026842b0
Exists in
master
and in
4 other branches
[PATCH] configfs: User-driven configuration filesystem
Configfs, a file system for userspace-driven kernel object configuration. The OCFS2 stack makes extensive use of this for propagation of cluster configuration information into kernel. Signed-off-by: Joel Becker <joel.becker@oracle.com>
Showing 15 changed files with 3575 additions and 0 deletions Side-by-side Diff
- Documentation/filesystems/00-INDEX
- Documentation/filesystems/configfs/configfs.txt
- Documentation/filesystems/configfs/configfs_example.c
- MAINTAINERS
- fs/Kconfig
- fs/Makefile
- fs/configfs/Makefile
- fs/configfs/configfs_internal.h
- fs/configfs/dir.c
- fs/configfs/file.c
- fs/configfs/inode.c
- fs/configfs/item.c
- fs/configfs/mount.c
- fs/configfs/symlink.c
- include/linux/configfs.h
Documentation/filesystems/00-INDEX
... | ... | @@ -12,6 +12,8 @@ |
12 | 12 | - description of the CIFS filesystem |
13 | 13 | coda.txt |
14 | 14 | - description of the CODA filesystem. |
15 | +configfs/ | |
16 | + - directory containing configfs documentation and example code. | |
15 | 17 | cramfs.txt |
16 | 18 | - info on the cram filesystem for small storage (ROMs etc) |
17 | 19 | devfs/ |
Documentation/filesystems/configfs/configfs.txt
1 | + | |
2 | +configfs - Userspace-driven kernel object configuation. | |
3 | + | |
4 | +Joel Becker <joel.becker@oracle.com> | |
5 | + | |
6 | +Updated: 31 March 2005 | |
7 | + | |
8 | +Copyright (c) 2005 Oracle Corporation, | |
9 | + Joel Becker <joel.becker@oracle.com> | |
10 | + | |
11 | + | |
12 | +[What is configfs?] | |
13 | + | |
14 | +configfs is a ram-based filesystem that provides the converse of | |
15 | +sysfs's functionality. Where sysfs is a filesystem-based view of | |
16 | +kernel objects, configfs is a filesystem-based manager of kernel | |
17 | +objects, or config_items. | |
18 | + | |
19 | +With sysfs, an object is created in kernel (for example, when a device | |
20 | +is discovered) and it is registered with sysfs. Its attributes then | |
21 | +appear in sysfs, allowing userspace to read the attributes via | |
22 | +readdir(3)/read(2). It may allow some attributes to be modified via | |
23 | +write(2). The important point is that the object is created and | |
24 | +destroyed in kernel, the kernel controls the lifecycle of the sysfs | |
25 | +representation, and sysfs is merely a window on all this. | |
26 | + | |
27 | +A configfs config_item is created via an explicit userspace operation: | |
28 | +mkdir(2). It is destroyed via rmdir(2). The attributes appear at | |
29 | +mkdir(2) time, and can be read or modified via read(2) and write(2). | |
30 | +As with sysfs, readdir(3) queries the list of items and/or attributes. | |
31 | +symlink(2) can be used to group items together. Unlike sysfs, the | |
32 | +lifetime of the representation is completely driven by userspace. The | |
33 | +kernel modules backing the items must respond to this. | |
34 | + | |
35 | +Both sysfs and configfs can and should exist together on the same | |
36 | +system. One is not a replacement for the other. | |
37 | + | |
38 | +[Using configfs] | |
39 | + | |
40 | +configfs can be compiled as a module or into the kernel. You can access | |
41 | +it by doing | |
42 | + | |
43 | + mount -t configfs none /config | |
44 | + | |
45 | +The configfs tree will be empty unless client modules are also loaded. | |
46 | +These are modules that register their item types with configfs as | |
47 | +subsystems. Once a client subsystem is loaded, it will appear as a | |
48 | +subdirectory (or more than one) under /config. Like sysfs, the | |
49 | +configfs tree is always there, whether mounted on /config or not. | |
50 | + | |
51 | +An item is created via mkdir(2). The item's attributes will also | |
52 | +appear at this time. readdir(3) can determine what the attributes are, | |
53 | +read(2) can query their default values, and write(2) can store new | |
54 | +values. Like sysfs, attributes should be ASCII text files, preferably | |
55 | +with only one value per file. The same efficiency caveats from sysfs | |
56 | +apply. Don't mix more than one attribute in one attribute file. | |
57 | + | |
58 | +Like sysfs, configfs expects write(2) to store the entire buffer at | |
59 | +once. When writing to configfs attributes, userspace processes should | |
60 | +first read the entire file, modify the portions they wish to change, and | |
61 | +then write the entire buffer back. Attribute files have a maximum size | |
62 | +of one page (PAGE_SIZE, 4096 on i386). | |
63 | + | |
64 | +When an item needs to be destroyed, remove it with rmdir(2). An | |
65 | +item cannot be destroyed if any other item has a link to it (via | |
66 | +symlink(2)). Links can be removed via unlink(2). | |
67 | + | |
68 | +[Configuring FakeNBD: an Example] | |
69 | + | |
70 | +Imagine there's a Network Block Device (NBD) driver that allows you to | |
71 | +access remote block devices. Call it FakeNBD. FakeNBD uses configfs | |
72 | +for its configuration. Obviously, there will be a nice program that | |
73 | +sysadmins use to configure FakeNBD, but somehow that program has to tell | |
74 | +the driver about it. Here's where configfs comes in. | |
75 | + | |
76 | +When the FakeNBD driver is loaded, it registers itself with configfs. | |
77 | +readdir(3) sees this just fine: | |
78 | + | |
79 | + # ls /config | |
80 | + fakenbd | |
81 | + | |
82 | +A fakenbd connection can be created with mkdir(2). The name is | |
83 | +arbitrary, but likely the tool will make some use of the name. Perhaps | |
84 | +it is a uuid or a disk name: | |
85 | + | |
86 | + # mkdir /config/fakenbd/disk1 | |
87 | + # ls /config/fakenbd/disk1 | |
88 | + target device rw | |
89 | + | |
90 | +The target attribute contains the IP address of the server FakeNBD will | |
91 | +connect to. The device attribute is the device on the server. | |
92 | +Predictably, the rw attribute determines whether the connection is | |
93 | +read-only or read-write. | |
94 | + | |
95 | + # echo 10.0.0.1 > /config/fakenbd/disk1/target | |
96 | + # echo /dev/sda1 > /config/fakenbd/disk1/device | |
97 | + # echo 1 > /config/fakenbd/disk1/rw | |
98 | + | |
99 | +That's it. That's all there is. Now the device is configured, via the | |
100 | +shell no less. | |
101 | + | |
102 | +[Coding With configfs] | |
103 | + | |
104 | +Every object in configfs is a config_item. A config_item reflects an | |
105 | +object in the subsystem. It has attributes that match values on that | |
106 | +object. configfs handles the filesystem representation of that object | |
107 | +and its attributes, allowing the subsystem to ignore all but the | |
108 | +basic show/store interaction. | |
109 | + | |
110 | +Items are created and destroyed inside a config_group. A group is a | |
111 | +collection of items that share the same attributes and operations. | |
112 | +Items are created by mkdir(2) and removed by rmdir(2), but configfs | |
113 | +handles that. The group has a set of operations to perform these tasks | |
114 | + | |
115 | +A subsystem is the top level of a client module. During initialization, | |
116 | +the client module registers the subsystem with configfs, the subsystem | |
117 | +appears as a directory at the top of the configfs filesystem. A | |
118 | +subsystem is also a config_group, and can do everything a config_group | |
119 | +can. | |
120 | + | |
121 | +[struct config_item] | |
122 | + | |
123 | + struct config_item { | |
124 | + char *ci_name; | |
125 | + char ci_namebuf[UOBJ_NAME_LEN]; | |
126 | + struct kref ci_kref; | |
127 | + struct list_head ci_entry; | |
128 | + struct config_item *ci_parent; | |
129 | + struct config_group *ci_group; | |
130 | + struct config_item_type *ci_type; | |
131 | + struct dentry *ci_dentry; | |
132 | + }; | |
133 | + | |
134 | + void config_item_init(struct config_item *); | |
135 | + void config_item_init_type_name(struct config_item *, | |
136 | + const char *name, | |
137 | + struct config_item_type *type); | |
138 | + struct config_item *config_item_get(struct config_item *); | |
139 | + void config_item_put(struct config_item *); | |
140 | + | |
141 | +Generally, struct config_item is embedded in a container structure, a | |
142 | +structure that actually represents what the subsystem is doing. The | |
143 | +config_item portion of that structure is how the object interacts with | |
144 | +configfs. | |
145 | + | |
146 | +Whether statically defined in a source file or created by a parent | |
147 | +config_group, a config_item must have one of the _init() functions | |
148 | +called on it. This initializes the reference count and sets up the | |
149 | +appropriate fields. | |
150 | + | |
151 | +All users of a config_item should have a reference on it via | |
152 | +config_item_get(), and drop the reference when they are done via | |
153 | +config_item_put(). | |
154 | + | |
155 | +By itself, a config_item cannot do much more than appear in configfs. | |
156 | +Usually a subsystem wants the item to display and/or store attributes, | |
157 | +among other things. For that, it needs a type. | |
158 | + | |
159 | +[struct config_item_type] | |
160 | + | |
161 | + struct configfs_item_operations { | |
162 | + void (*release)(struct config_item *); | |
163 | + ssize_t (*show_attribute)(struct config_item *, | |
164 | + struct configfs_attribute *, | |
165 | + char *); | |
166 | + ssize_t (*store_attribute)(struct config_item *, | |
167 | + struct configfs_attribute *, | |
168 | + const char *, size_t); | |
169 | + int (*allow_link)(struct config_item *src, | |
170 | + struct config_item *target); | |
171 | + int (*drop_link)(struct config_item *src, | |
172 | + struct config_item *target); | |
173 | + }; | |
174 | + | |
175 | + struct config_item_type { | |
176 | + struct module *ct_owner; | |
177 | + struct configfs_item_operations *ct_item_ops; | |
178 | + struct configfs_group_operations *ct_group_ops; | |
179 | + struct configfs_attribute **ct_attrs; | |
180 | + }; | |
181 | + | |
182 | +The most basic function of a config_item_type is to define what | |
183 | +operations can be performed on a config_item. All items that have been | |
184 | +allocated dynamically will need to provide the ct_item_ops->release() | |
185 | +method. This method is called when the config_item's reference count | |
186 | +reaches zero. Items that wish to display an attribute need to provide | |
187 | +the ct_item_ops->show_attribute() method. Similarly, storing a new | |
188 | +attribute value uses the store_attribute() method. | |
189 | + | |
190 | +[struct configfs_attribute] | |
191 | + | |
192 | + struct configfs_attribute { | |
193 | + char *ca_name; | |
194 | + struct module *ca_owner; | |
195 | + mode_t ca_mode; | |
196 | + }; | |
197 | + | |
198 | +When a config_item wants an attribute to appear as a file in the item's | |
199 | +configfs directory, it must define a configfs_attribute describing it. | |
200 | +It then adds the attribute to the NULL-terminated array | |
201 | +config_item_type->ct_attrs. When the item appears in configfs, the | |
202 | +attribute file will appear with the configfs_attribute->ca_name | |
203 | +filename. configfs_attribute->ca_mode specifies the file permissions. | |
204 | + | |
205 | +If an attribute is readable and the config_item provides a | |
206 | +ct_item_ops->show_attribute() method, that method will be called | |
207 | +whenever userspace asks for a read(2) on the attribute. The converse | |
208 | +will happen for write(2). | |
209 | + | |
210 | +[struct config_group] | |
211 | + | |
212 | +A config_item cannot live in a vaccum. The only way one can be created | |
213 | +is via mkdir(2) on a config_group. This will trigger creation of a | |
214 | +child item. | |
215 | + | |
216 | + struct config_group { | |
217 | + struct config_item cg_item; | |
218 | + struct list_head cg_children; | |
219 | + struct configfs_subsystem *cg_subsys; | |
220 | + struct config_group **default_groups; | |
221 | + }; | |
222 | + | |
223 | + void config_group_init(struct config_group *group); | |
224 | + void config_group_init_type_name(struct config_group *group, | |
225 | + const char *name, | |
226 | + struct config_item_type *type); | |
227 | + | |
228 | + | |
229 | +The config_group structure contains a config_item. Properly configuring | |
230 | +that item means that a group can behave as an item in its own right. | |
231 | +However, it can do more: it can create child items or groups. This is | |
232 | +accomplished via the group operations specified on the group's | |
233 | +config_item_type. | |
234 | + | |
235 | + struct configfs_group_operations { | |
236 | + struct config_item *(*make_item)(struct config_group *group, | |
237 | + const char *name); | |
238 | + struct config_group *(*make_group)(struct config_group *group, | |
239 | + const char *name); | |
240 | + int (*commit_item)(struct config_item *item); | |
241 | + void (*drop_item)(struct config_group *group, | |
242 | + struct config_item *item); | |
243 | + }; | |
244 | + | |
245 | +A group creates child items by providing the | |
246 | +ct_group_ops->make_item() method. If provided, this method is called from mkdir(2) in the group's directory. The subsystem allocates a new | |
247 | +config_item (or more likely, its container structure), initializes it, | |
248 | +and returns it to configfs. Configfs will then populate the filesystem | |
249 | +tree to reflect the new item. | |
250 | + | |
251 | +If the subsystem wants the child to be a group itself, the subsystem | |
252 | +provides ct_group_ops->make_group(). Everything else behaves the same, | |
253 | +using the group _init() functions on the group. | |
254 | + | |
255 | +Finally, when userspace calls rmdir(2) on the item or group, | |
256 | +ct_group_ops->drop_item() is called. As a config_group is also a | |
257 | +config_item, it is not necessary for a seperate drop_group() method. | |
258 | +The subsystem must config_item_put() the reference that was initialized | |
259 | +upon item allocation. If a subsystem has no work to do, it may omit | |
260 | +the ct_group_ops->drop_item() method, and configfs will call | |
261 | +config_item_put() on the item on behalf of the subsystem. | |
262 | + | |
263 | +IMPORTANT: drop_item() is void, and as such cannot fail. When rmdir(2) | |
264 | +is called, configfs WILL remove the item from the filesystem tree | |
265 | +(assuming that it has no children to keep it busy). The subsystem is | |
266 | +responsible for responding to this. If the subsystem has references to | |
267 | +the item in other threads, the memory is safe. It may take some time | |
268 | +for the item to actually disappear from the subsystem's usage. But it | |
269 | +is gone from configfs. | |
270 | + | |
271 | +A config_group cannot be removed while it still has child items. This | |
272 | +is implemented in the configfs rmdir(2) code. ->drop_item() will not be | |
273 | +called, as the item has not been dropped. rmdir(2) will fail, as the | |
274 | +directory is not empty. | |
275 | + | |
276 | +[struct configfs_subsystem] | |
277 | + | |
278 | +A subsystem must register itself, ususally at module_init time. This | |
279 | +tells configfs to make the subsystem appear in the file tree. | |
280 | + | |
281 | + struct configfs_subsystem { | |
282 | + struct config_group su_group; | |
283 | + struct semaphore su_sem; | |
284 | + }; | |
285 | + | |
286 | + int configfs_register_subsystem(struct configfs_subsystem *subsys); | |
287 | + void configfs_unregister_subsystem(struct configfs_subsystem *subsys); | |
288 | + | |
289 | + A subsystem consists of a toplevel config_group and a semaphore. | |
290 | +The group is where child config_items are created. For a subsystem, | |
291 | +this group is usually defined statically. Before calling | |
292 | +configfs_register_subsystem(), the subsystem must have initialized the | |
293 | +group via the usual group _init() functions, and it must also have | |
294 | +initialized the semaphore. | |
295 | + When the register call returns, the subsystem is live, and it | |
296 | +will be visible via configfs. At that point, mkdir(2) can be called and | |
297 | +the subsystem must be ready for it. | |
298 | + | |
299 | +[An Example] | |
300 | + | |
301 | +The best example of these basic concepts is the simple_children | |
302 | +subsystem/group and the simple_child item in configfs_example.c It | |
303 | +shows a trivial object displaying and storing an attribute, and a simple | |
304 | +group creating and destroying these children. | |
305 | + | |
306 | +[Hierarchy Navigation and the Subsystem Semaphore] | |
307 | + | |
308 | +There is an extra bonus that configfs provides. The config_groups and | |
309 | +config_items are arranged in a hierarchy due to the fact that they | |
310 | +appear in a filesystem. A subsystem is NEVER to touch the filesystem | |
311 | +parts, but the subsystem might be interested in this hierarchy. For | |
312 | +this reason, the hierarchy is mirrored via the config_group->cg_children | |
313 | +and config_item->ci_parent structure members. | |
314 | + | |
315 | +A subsystem can navigate the cg_children list and the ci_parent pointer | |
316 | +to see the tree created by the subsystem. This can race with configfs' | |
317 | +management of the hierarchy, so configfs uses the subsystem semaphore to | |
318 | +protect modifications. Whenever a subsystem wants to navigate the | |
319 | +hierarchy, it must do so under the protection of the subsystem | |
320 | +semaphore. | |
321 | + | |
322 | +A subsystem will be prevented from acquiring the semaphore while a newly | |
323 | +allocated item has not been linked into this hierarchy. Similarly, it | |
324 | +will not be able to acquire the semaphore while a dropping item has not | |
325 | +yet been unlinked. This means that an item's ci_parent pointer will | |
326 | +never be NULL while the item is in configfs, and that an item will only | |
327 | +be in its parent's cg_children list for the same duration. This allows | |
328 | +a subsystem to trust ci_parent and cg_children while they hold the | |
329 | +semaphore. | |
330 | + | |
331 | +[Item Aggregation Via symlink(2)] | |
332 | + | |
333 | +configfs provides a simple group via the group->item parent/child | |
334 | +relationship. Often, however, a larger environment requires aggregation | |
335 | +outside of the parent/child connection. This is implemented via | |
336 | +symlink(2). | |
337 | + | |
338 | +A config_item may provide the ct_item_ops->allow_link() and | |
339 | +ct_item_ops->drop_link() methods. If the ->allow_link() method exists, | |
340 | +symlink(2) may be called with the config_item as the source of the link. | |
341 | +These links are only allowed between configfs config_items. Any | |
342 | +symlink(2) attempt outside the configfs filesystem will be denied. | |
343 | + | |
344 | +When symlink(2) is called, the source config_item's ->allow_link() | |
345 | +method is called with itself and a target item. If the source item | |
346 | +allows linking to target item, it returns 0. A source item may wish to | |
347 | +reject a link if it only wants links to a certain type of object (say, | |
348 | +in its own subsystem). | |
349 | + | |
350 | +When unlink(2) is called on the symbolic link, the source item is | |
351 | +notified via the ->drop_link() method. Like the ->drop_item() method, | |
352 | +this is a void function and cannot return failure. The subsystem is | |
353 | +responsible for responding to the change. | |
354 | + | |
355 | +A config_item cannot be removed while it links to any other item, nor | |
356 | +can it be removed while an item links to it. Dangling symlinks are not | |
357 | +allowed in configfs. | |
358 | + | |
359 | +[Automatically Created Subgroups] | |
360 | + | |
361 | +A new config_group may want to have two types of child config_items. | |
362 | +While this could be codified by magic names in ->make_item(), it is much | |
363 | +more explicit to have a method whereby userspace sees this divergence. | |
364 | + | |
365 | +Rather than have a group where some items behave differently than | |
366 | +others, configfs provides a method whereby one or many subgroups are | |
367 | +automatically created inside the parent at its creation. Thus, | |
368 | +mkdir("parent) results in "parent", "parent/subgroup1", up through | |
369 | +"parent/subgroupN". Items of type 1 can now be created in | |
370 | +"parent/subgroup1", and items of type N can be created in | |
371 | +"parent/subgroupN". | |
372 | + | |
373 | +These automatic subgroups, or default groups, do not preclude other | |
374 | +children of the parent group. If ct_group_ops->make_group() exists, | |
375 | +other child groups can be created on the parent group directly. | |
376 | + | |
377 | +A configfs subsystem specifies default groups by filling in the | |
378 | +NULL-terminated array default_groups on the config_group structure. | |
379 | +Each group in that array is populated in the configfs tree at the same | |
380 | +time as the parent group. Similarly, they are removed at the same time | |
381 | +as the parent. No extra notification is provided. When a ->drop_item() | |
382 | +method call notifies the subsystem the parent group is going away, it | |
383 | +also means every default group child associated with that parent group. | |
384 | + | |
385 | +As a consequence of this, default_groups cannot be removed directly via | |
386 | +rmdir(2). They also are not considered when rmdir(2) on the parent | |
387 | +group is checking for children. | |
388 | + | |
389 | +[Committable Items] | |
390 | + | |
391 | +NOTE: Committable items are currently unimplemented. | |
392 | + | |
393 | +Some config_items cannot have a valid initial state. That is, no | |
394 | +default values can be specified for the item's attributes such that the | |
395 | +item can do its work. Userspace must configure one or more attributes, | |
396 | +after which the subsystem can start whatever entity this item | |
397 | +represents. | |
398 | + | |
399 | +Consider the FakeNBD device from above. Without a target address *and* | |
400 | +a target device, the subsystem has no idea what block device to import. | |
401 | +The simple example assumes that the subsystem merely waits until all the | |
402 | +appropriate attributes are configured, and then connects. This will, | |
403 | +indeed, work, but now every attribute store must check if the attributes | |
404 | +are initialized. Every attribute store must fire off the connection if | |
405 | +that condition is met. | |
406 | + | |
407 | +Far better would be an explicit action notifying the subsystem that the | |
408 | +config_item is ready to go. More importantly, an explicit action allows | |
409 | +the subsystem to provide feedback as to whether the attibutes are | |
410 | +initialized in a way that makes sense. configfs provides this as | |
411 | +committable items. | |
412 | + | |
413 | +configfs still uses only normal filesystem operations. An item is | |
414 | +committed via rename(2). The item is moved from a directory where it | |
415 | +can be modified to a directory where it cannot. | |
416 | + | |
417 | +Any group that provides the ct_group_ops->commit_item() method has | |
418 | +committable items. When this group appears in configfs, mkdir(2) will | |
419 | +not work directly in the group. Instead, the group will have two | |
420 | +subdirectories: "live" and "pending". The "live" directory does not | |
421 | +support mkdir(2) or rmdir(2) either. It only allows rename(2). The | |
422 | +"pending" directory does allow mkdir(2) and rmdir(2). An item is | |
423 | +created in the "pending" directory. Its attributes can be modified at | |
424 | +will. Userspace commits the item by renaming it into the "live" | |
425 | +directory. At this point, the subsystem recieves the ->commit_item() | |
426 | +callback. If all required attributes are filled to satisfaction, the | |
427 | +method returns zero and the item is moved to the "live" directory. | |
428 | + | |
429 | +As rmdir(2) does not work in the "live" directory, an item must be | |
430 | +shutdown, or "uncommitted". Again, this is done via rename(2), this | |
431 | +time from the "live" directory back to the "pending" one. The subsystem | |
432 | +is notified by the ct_group_ops->uncommit_object() method. |
Documentation/filesystems/configfs/configfs_example.c
1 | +/* | |
2 | + * vim: noexpandtab ts=8 sts=0 sw=8: | |
3 | + * | |
4 | + * configfs_example.c - This file is a demonstration module containing | |
5 | + * a number of configfs subsystems. | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or | |
8 | + * modify it under the terms of the GNU General Public | |
9 | + * License as published by the Free Software Foundation; either | |
10 | + * version 2 of the License, or (at your option) any later version. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, | |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | + * General Public License for more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public | |
18 | + * License along with this program; if not, write to the | |
19 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
20 | + * Boston, MA 021110-1307, USA. | |
21 | + * | |
22 | + * Based on sysfs: | |
23 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
24 | + * | |
25 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
26 | + */ | |
27 | + | |
28 | +#include <linux/init.h> | |
29 | +#include <linux/module.h> | |
30 | +#include <linux/slab.h> | |
31 | + | |
32 | +#include <linux/configfs.h> | |
33 | + | |
34 | + | |
35 | + | |
36 | +/* | |
37 | + * 01-childless | |
38 | + * | |
39 | + * This first example is a childless subsystem. It cannot create | |
40 | + * any config_items. It just has attributes. | |
41 | + * | |
42 | + * Note that we are enclosing the configfs_subsystem inside a container. | |
43 | + * This is not necessary if a subsystem has no attributes directly | |
44 | + * on the subsystem. See the next example, 02-simple-children, for | |
45 | + * such a subsystem. | |
46 | + */ | |
47 | + | |
48 | +struct childless { | |
49 | + struct configfs_subsystem subsys; | |
50 | + int showme; | |
51 | + int storeme; | |
52 | +}; | |
53 | + | |
54 | +struct childless_attribute { | |
55 | + struct configfs_attribute attr; | |
56 | + ssize_t (*show)(struct childless *, char *); | |
57 | + ssize_t (*store)(struct childless *, const char *, size_t); | |
58 | +}; | |
59 | + | |
60 | +static inline struct childless *to_childless(struct config_item *item) | |
61 | +{ | |
62 | + return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct childless, subsys) : NULL; | |
63 | +} | |
64 | + | |
65 | +static ssize_t childless_showme_read(struct childless *childless, | |
66 | + char *page) | |
67 | +{ | |
68 | + ssize_t pos; | |
69 | + | |
70 | + pos = sprintf(page, "%d\n", childless->showme); | |
71 | + childless->showme++; | |
72 | + | |
73 | + return pos; | |
74 | +} | |
75 | + | |
76 | +static ssize_t childless_storeme_read(struct childless *childless, | |
77 | + char *page) | |
78 | +{ | |
79 | + return sprintf(page, "%d\n", childless->storeme); | |
80 | +} | |
81 | + | |
82 | +static ssize_t childless_storeme_write(struct childless *childless, | |
83 | + const char *page, | |
84 | + size_t count) | |
85 | +{ | |
86 | + unsigned long tmp; | |
87 | + char *p = (char *) page; | |
88 | + | |
89 | + tmp = simple_strtoul(p, &p, 10); | |
90 | + if (!p || (*p && (*p != '\n'))) | |
91 | + return -EINVAL; | |
92 | + | |
93 | + if (tmp > INT_MAX) | |
94 | + return -ERANGE; | |
95 | + | |
96 | + childless->storeme = tmp; | |
97 | + | |
98 | + return count; | |
99 | +} | |
100 | + | |
101 | +static ssize_t childless_description_read(struct childless *childless, | |
102 | + char *page) | |
103 | +{ | |
104 | + return sprintf(page, | |
105 | +"[01-childless]\n" | |
106 | +"\n" | |
107 | +"The childless subsystem is the simplest possible subsystem in\n" | |
108 | +"configfs. It does not support the creation of child config_items.\n" | |
109 | +"It only has a few attributes. In fact, it isn't much different\n" | |
110 | +"than a directory in /proc.\n"); | |
111 | +} | |
112 | + | |
113 | +static struct childless_attribute childless_attr_showme = { | |
114 | + .attr = { .ca_owner = THIS_MODULE, .ca_name = "showme", .ca_mode = S_IRUGO }, | |
115 | + .show = childless_showme_read, | |
116 | +}; | |
117 | +static struct childless_attribute childless_attr_storeme = { | |
118 | + .attr = { .ca_owner = THIS_MODULE, .ca_name = "storeme", .ca_mode = S_IRUGO | S_IWUSR }, | |
119 | + .show = childless_storeme_read, | |
120 | + .store = childless_storeme_write, | |
121 | +}; | |
122 | +static struct childless_attribute childless_attr_description = { | |
123 | + .attr = { .ca_owner = THIS_MODULE, .ca_name = "description", .ca_mode = S_IRUGO }, | |
124 | + .show = childless_description_read, | |
125 | +}; | |
126 | + | |
127 | +static struct configfs_attribute *childless_attrs[] = { | |
128 | + &childless_attr_showme.attr, | |
129 | + &childless_attr_storeme.attr, | |
130 | + &childless_attr_description.attr, | |
131 | + NULL, | |
132 | +}; | |
133 | + | |
134 | +static ssize_t childless_attr_show(struct config_item *item, | |
135 | + struct configfs_attribute *attr, | |
136 | + char *page) | |
137 | +{ | |
138 | + struct childless *childless = to_childless(item); | |
139 | + struct childless_attribute *childless_attr = | |
140 | + container_of(attr, struct childless_attribute, attr); | |
141 | + ssize_t ret = 0; | |
142 | + | |
143 | + if (childless_attr->show) | |
144 | + ret = childless_attr->show(childless, page); | |
145 | + return ret; | |
146 | +} | |
147 | + | |
148 | +static ssize_t childless_attr_store(struct config_item *item, | |
149 | + struct configfs_attribute *attr, | |
150 | + const char *page, size_t count) | |
151 | +{ | |
152 | + struct childless *childless = to_childless(item); | |
153 | + struct childless_attribute *childless_attr = | |
154 | + container_of(attr, struct childless_attribute, attr); | |
155 | + ssize_t ret = -EINVAL; | |
156 | + | |
157 | + if (childless_attr->store) | |
158 | + ret = childless_attr->store(childless, page, count); | |
159 | + return ret; | |
160 | +} | |
161 | + | |
162 | +static struct configfs_item_operations childless_item_ops = { | |
163 | + .show_attribute = childless_attr_show, | |
164 | + .store_attribute = childless_attr_store, | |
165 | +}; | |
166 | + | |
167 | +static struct config_item_type childless_type = { | |
168 | + .ct_item_ops = &childless_item_ops, | |
169 | + .ct_attrs = childless_attrs, | |
170 | + .ct_owner = THIS_MODULE, | |
171 | +}; | |
172 | + | |
173 | +static struct childless childless_subsys = { | |
174 | + .subsys = { | |
175 | + .su_group = { | |
176 | + .cg_item = { | |
177 | + .ci_namebuf = "01-childless", | |
178 | + .ci_type = &childless_type, | |
179 | + }, | |
180 | + }, | |
181 | + }, | |
182 | +}; | |
183 | + | |
184 | + | |
185 | +/* ----------------------------------------------------------------- */ | |
186 | + | |
187 | +/* | |
188 | + * 02-simple-children | |
189 | + * | |
190 | + * This example merely has a simple one-attribute child. Note that | |
191 | + * there is no extra attribute structure, as the child's attribute is | |
192 | + * known from the get-go. Also, there is no container for the | |
193 | + * subsystem, as it has no attributes of its own. | |
194 | + */ | |
195 | + | |
196 | +struct simple_child { | |
197 | + struct config_item item; | |
198 | + int storeme; | |
199 | +}; | |
200 | + | |
201 | +static inline struct simple_child *to_simple_child(struct config_item *item) | |
202 | +{ | |
203 | + return item ? container_of(item, struct simple_child, item) : NULL; | |
204 | +} | |
205 | + | |
206 | +static struct configfs_attribute simple_child_attr_storeme = { | |
207 | + .ca_owner = THIS_MODULE, | |
208 | + .ca_name = "storeme", | |
209 | + .ca_mode = S_IRUGO | S_IWUSR, | |
210 | +}; | |
211 | + | |
212 | +static struct configfs_attribute *simple_child_attrs[] = { | |
213 | + &simple_child_attr_storeme, | |
214 | + NULL, | |
215 | +}; | |
216 | + | |
217 | +static ssize_t simple_child_attr_show(struct config_item *item, | |
218 | + struct configfs_attribute *attr, | |
219 | + char *page) | |
220 | +{ | |
221 | + ssize_t count; | |
222 | + struct simple_child *simple_child = to_simple_child(item); | |
223 | + | |
224 | + count = sprintf(page, "%d\n", simple_child->storeme); | |
225 | + | |
226 | + return count; | |
227 | +} | |
228 | + | |
229 | +static ssize_t simple_child_attr_store(struct config_item *item, | |
230 | + struct configfs_attribute *attr, | |
231 | + const char *page, size_t count) | |
232 | +{ | |
233 | + struct simple_child *simple_child = to_simple_child(item); | |
234 | + unsigned long tmp; | |
235 | + char *p = (char *) page; | |
236 | + | |
237 | + tmp = simple_strtoul(p, &p, 10); | |
238 | + if (!p || (*p && (*p != '\n'))) | |
239 | + return -EINVAL; | |
240 | + | |
241 | + if (tmp > INT_MAX) | |
242 | + return -ERANGE; | |
243 | + | |
244 | + simple_child->storeme = tmp; | |
245 | + | |
246 | + return count; | |
247 | +} | |
248 | + | |
249 | +static void simple_child_release(struct config_item *item) | |
250 | +{ | |
251 | + kfree(to_simple_child(item)); | |
252 | +} | |
253 | + | |
254 | +static struct configfs_item_operations simple_child_item_ops = { | |
255 | + .release = simple_child_release, | |
256 | + .show_attribute = simple_child_attr_show, | |
257 | + .store_attribute = simple_child_attr_store, | |
258 | +}; | |
259 | + | |
260 | +static struct config_item_type simple_child_type = { | |
261 | + .ct_item_ops = &simple_child_item_ops, | |
262 | + .ct_attrs = simple_child_attrs, | |
263 | + .ct_owner = THIS_MODULE, | |
264 | +}; | |
265 | + | |
266 | + | |
267 | +static struct config_item *simple_children_make_item(struct config_group *group, const char *name) | |
268 | +{ | |
269 | + struct simple_child *simple_child; | |
270 | + | |
271 | + simple_child = kmalloc(sizeof(struct simple_child), GFP_KERNEL); | |
272 | + if (!simple_child) | |
273 | + return NULL; | |
274 | + | |
275 | + memset(simple_child, 0, sizeof(struct simple_child)); | |
276 | + | |
277 | + config_item_init_type_name(&simple_child->item, name, | |
278 | + &simple_child_type); | |
279 | + | |
280 | + simple_child->storeme = 0; | |
281 | + | |
282 | + return &simple_child->item; | |
283 | +} | |
284 | + | |
285 | +static struct configfs_attribute simple_children_attr_description = { | |
286 | + .ca_owner = THIS_MODULE, | |
287 | + .ca_name = "description", | |
288 | + .ca_mode = S_IRUGO, | |
289 | +}; | |
290 | + | |
291 | +static struct configfs_attribute *simple_children_attrs[] = { | |
292 | + &simple_children_attr_description, | |
293 | + NULL, | |
294 | +}; | |
295 | + | |
296 | +static ssize_t simple_children_attr_show(struct config_item *item, | |
297 | + struct configfs_attribute *attr, | |
298 | + char *page) | |
299 | +{ | |
300 | + return sprintf(page, | |
301 | +"[02-simple-children]\n" | |
302 | +"\n" | |
303 | +"This subsystem allows the creation of child config_items. These\n" | |
304 | +"items have only one attribute that is readable and writeable.\n"); | |
305 | +} | |
306 | + | |
307 | +static struct configfs_item_operations simple_children_item_ops = { | |
308 | + .show_attribute = simple_children_attr_show, | |
309 | +}; | |
310 | + | |
311 | +/* | |
312 | + * Note that, since no extra work is required on ->drop_item(), | |
313 | + * no ->drop_item() is provided. | |
314 | + */ | |
315 | +static struct configfs_group_operations simple_children_group_ops = { | |
316 | + .make_item = simple_children_make_item, | |
317 | +}; | |
318 | + | |
319 | +static struct config_item_type simple_children_type = { | |
320 | + .ct_item_ops = &simple_children_item_ops, | |
321 | + .ct_group_ops = &simple_children_group_ops, | |
322 | + .ct_attrs = simple_children_attrs, | |
323 | +}; | |
324 | + | |
325 | +static struct configfs_subsystem simple_children_subsys = { | |
326 | + .su_group = { | |
327 | + .cg_item = { | |
328 | + .ci_namebuf = "02-simple-children", | |
329 | + .ci_type = &simple_children_type, | |
330 | + }, | |
331 | + }, | |
332 | +}; | |
333 | + | |
334 | + | |
335 | +/* ----------------------------------------------------------------- */ | |
336 | + | |
337 | +/* | |
338 | + * 03-group-children | |
339 | + * | |
340 | + * This example reuses the simple_children group from above. However, | |
341 | + * the simple_children group is not the subsystem itself, it is a | |
342 | + * child of the subsystem. Creation of a group in the subsystem creates | |
343 | + * a new simple_children group. That group can then have simple_child | |
344 | + * children of its own. | |
345 | + */ | |
346 | + | |
347 | +struct simple_children { | |
348 | + struct config_group group; | |
349 | +}; | |
350 | + | |
351 | +static struct config_group *group_children_make_group(struct config_group *group, const char *name) | |
352 | +{ | |
353 | + struct simple_children *simple_children; | |
354 | + | |
355 | + simple_children = kmalloc(sizeof(struct simple_children), | |
356 | + GFP_KERNEL); | |
357 | + if (!simple_children) | |
358 | + return NULL; | |
359 | + | |
360 | + memset(simple_children, 0, sizeof(struct simple_children)); | |
361 | + | |
362 | + config_group_init_type_name(&simple_children->group, name, | |
363 | + &simple_children_type); | |
364 | + | |
365 | + return &simple_children->group; | |
366 | +} | |
367 | + | |
368 | +static struct configfs_attribute group_children_attr_description = { | |
369 | + .ca_owner = THIS_MODULE, | |
370 | + .ca_name = "description", | |
371 | + .ca_mode = S_IRUGO, | |
372 | +}; | |
373 | + | |
374 | +static struct configfs_attribute *group_children_attrs[] = { | |
375 | + &group_children_attr_description, | |
376 | + NULL, | |
377 | +}; | |
378 | + | |
379 | +static ssize_t group_children_attr_show(struct config_item *item, | |
380 | + struct configfs_attribute *attr, | |
381 | + char *page) | |
382 | +{ | |
383 | + return sprintf(page, | |
384 | +"[03-group-children]\n" | |
385 | +"\n" | |
386 | +"This subsystem allows the creation of child config_groups. These\n" | |
387 | +"groups are like the subsystem simple-children.\n"); | |
388 | +} | |
389 | + | |
390 | +static struct configfs_item_operations group_children_item_ops = { | |
391 | + .show_attribute = group_children_attr_show, | |
392 | +}; | |
393 | + | |
394 | +/* | |
395 | + * Note that, since no extra work is required on ->drop_item(), | |
396 | + * no ->drop_item() is provided. | |
397 | + */ | |
398 | +static struct configfs_group_operations group_children_group_ops = { | |
399 | + .make_group = group_children_make_group, | |
400 | +}; | |
401 | + | |
402 | +static struct config_item_type group_children_type = { | |
403 | + .ct_item_ops = &group_children_item_ops, | |
404 | + .ct_group_ops = &group_children_group_ops, | |
405 | + .ct_attrs = group_children_attrs, | |
406 | +}; | |
407 | + | |
408 | +static struct configfs_subsystem group_children_subsys = { | |
409 | + .su_group = { | |
410 | + .cg_item = { | |
411 | + .ci_namebuf = "03-group-children", | |
412 | + .ci_type = &group_children_type, | |
413 | + }, | |
414 | + }, | |
415 | +}; | |
416 | + | |
417 | +/* ----------------------------------------------------------------- */ | |
418 | + | |
419 | +/* | |
420 | + * We're now done with our subsystem definitions. | |
421 | + * For convenience in this module, here's a list of them all. It | |
422 | + * allows the init function to easily register them. Most modules | |
423 | + * will only have one subsystem, and will only call register_subsystem | |
424 | + * on it directly. | |
425 | + */ | |
426 | +static struct configfs_subsystem *example_subsys[] = { | |
427 | + &childless_subsys.subsys, | |
428 | + &simple_children_subsys, | |
429 | + &group_children_subsys, | |
430 | + NULL, | |
431 | +}; | |
432 | + | |
433 | +static int __init configfs_example_init(void) | |
434 | +{ | |
435 | + int ret; | |
436 | + int i; | |
437 | + struct configfs_subsystem *subsys; | |
438 | + | |
439 | + for (i = 0; example_subsys[i]; i++) { | |
440 | + subsys = example_subsys[i]; | |
441 | + | |
442 | + config_group_init(&subsys->su_group); | |
443 | + init_MUTEX(&subsys->su_sem); | |
444 | + ret = configfs_register_subsystem(subsys); | |
445 | + if (ret) { | |
446 | + printk(KERN_ERR "Error %d while registering subsystem %s\n", | |
447 | + ret, | |
448 | + subsys->su_group.cg_item.ci_namebuf); | |
449 | + goto out_unregister; | |
450 | + } | |
451 | + } | |
452 | + | |
453 | + return 0; | |
454 | + | |
455 | +out_unregister: | |
456 | + for (; i >= 0; i--) { | |
457 | + configfs_unregister_subsystem(example_subsys[i]); | |
458 | + } | |
459 | + | |
460 | + return ret; | |
461 | +} | |
462 | + | |
463 | +static void __exit configfs_example_exit(void) | |
464 | +{ | |
465 | + int i; | |
466 | + | |
467 | + for (i = 0; example_subsys[i]; i++) { | |
468 | + configfs_unregister_subsystem(example_subsys[i]); | |
469 | + } | |
470 | +} | |
471 | + | |
472 | +module_init(configfs_example_init); | |
473 | +module_exit(configfs_example_exit); | |
474 | +MODULE_LICENSE("GPL"); |
MAINTAINERS
... | ... | @@ -554,6 +554,11 @@ |
554 | 554 | T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git |
555 | 555 | S: Supported |
556 | 556 | |
557 | +CONFIGFS | |
558 | +P: Joel Becker | |
559 | +M: Joel Becker <joel.becker@oracle.com> | |
560 | +S: Supported | |
561 | + | |
557 | 562 | CIRRUS LOGIC GENERIC FBDEV DRIVER |
558 | 563 | P: Jeff Garzik |
559 | 564 | M: jgarzik@pobox.com |
fs/Kconfig
... | ... | @@ -841,6 +841,20 @@ |
841 | 841 | |
842 | 842 | If unsure, say N. |
843 | 843 | |
844 | +config CONFIGFS_FS | |
845 | + tristate "Userspace-driven configuration filesystem (EXPERIMENTAL)" | |
846 | + depends on EXPERIMENTAL | |
847 | + help | |
848 | + configfs is a ram-based filesystem that provides the converse | |
849 | + of sysfs's functionality. Where sysfs is a filesystem-based | |
850 | + view of kernel objects, configfs is a filesystem-based manager | |
851 | + of kernel objects, or config_items. | |
852 | + | |
853 | + Both sysfs and configfs can and should exist together on the | |
854 | + same system. One is not a replacement for the other. | |
855 | + | |
856 | + If unsure, say N. | |
857 | + | |
844 | 858 | endmenu |
845 | 859 | |
846 | 860 | menu "Miscellaneous filesystems" |
fs/Makefile
fs/configfs/Makefile
fs/configfs/configfs_internal.h
1 | +/* -*- mode: c; c-basic-offset:8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * configfs_internal.h - Internal stuff for configfs | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on sysfs: | |
22 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
23 | + * | |
24 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
25 | + */ | |
26 | + | |
27 | +#include <linux/slab.h> | |
28 | +#include <linux/list.h> | |
29 | + | |
30 | +struct configfs_dirent { | |
31 | + atomic_t s_count; | |
32 | + struct list_head s_sibling; | |
33 | + struct list_head s_children; | |
34 | + struct list_head s_links; | |
35 | + void * s_element; | |
36 | + int s_type; | |
37 | + umode_t s_mode; | |
38 | + struct dentry * s_dentry; | |
39 | +}; | |
40 | + | |
41 | +#define CONFIGFS_ROOT 0x0001 | |
42 | +#define CONFIGFS_DIR 0x0002 | |
43 | +#define CONFIGFS_ITEM_ATTR 0x0004 | |
44 | +#define CONFIGFS_ITEM_LINK 0x0020 | |
45 | +#define CONFIGFS_USET_DIR 0x0040 | |
46 | +#define CONFIGFS_USET_DEFAULT 0x0080 | |
47 | +#define CONFIGFS_USET_DROPPING 0x0100 | |
48 | +#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) | |
49 | + | |
50 | +extern struct vfsmount * configfs_mount; | |
51 | + | |
52 | +extern int configfs_is_root(struct config_item *item); | |
53 | + | |
54 | +extern struct inode * configfs_new_inode(mode_t mode); | |
55 | +extern int configfs_create(struct dentry *, int mode, int (*init)(struct inode *)); | |
56 | + | |
57 | +extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); | |
58 | +extern int configfs_make_dirent(struct configfs_dirent *, | |
59 | + struct dentry *, void *, umode_t, int); | |
60 | + | |
61 | +extern int configfs_add_file(struct dentry *, const struct configfs_attribute *, int); | |
62 | +extern void configfs_hash_and_remove(struct dentry * dir, const char * name); | |
63 | + | |
64 | +extern const unsigned char * configfs_get_name(struct configfs_dirent *sd); | |
65 | +extern void configfs_drop_dentry(struct configfs_dirent *sd, struct dentry *parent); | |
66 | + | |
67 | +extern int configfs_pin_fs(void); | |
68 | +extern void configfs_release_fs(void); | |
69 | + | |
70 | +extern struct rw_semaphore configfs_rename_sem; | |
71 | +extern struct super_block * configfs_sb; | |
72 | +extern struct file_operations configfs_dir_operations; | |
73 | +extern struct file_operations configfs_file_operations; | |
74 | +extern struct file_operations bin_fops; | |
75 | +extern struct inode_operations configfs_dir_inode_operations; | |
76 | +extern struct inode_operations configfs_symlink_inode_operations; | |
77 | + | |
78 | +extern int configfs_symlink(struct inode *dir, struct dentry *dentry, | |
79 | + const char *symname); | |
80 | +extern int configfs_unlink(struct inode *dir, struct dentry *dentry); | |
81 | + | |
82 | +struct configfs_symlink { | |
83 | + struct list_head sl_list; | |
84 | + struct config_item *sl_target; | |
85 | +}; | |
86 | + | |
87 | +extern int configfs_create_link(struct configfs_symlink *sl, | |
88 | + struct dentry *parent, | |
89 | + struct dentry *dentry); | |
90 | + | |
91 | +static inline struct config_item * to_item(struct dentry * dentry) | |
92 | +{ | |
93 | + struct configfs_dirent * sd = dentry->d_fsdata; | |
94 | + return ((struct config_item *) sd->s_element); | |
95 | +} | |
96 | + | |
97 | +static inline struct configfs_attribute * to_attr(struct dentry * dentry) | |
98 | +{ | |
99 | + struct configfs_dirent * sd = dentry->d_fsdata; | |
100 | + return ((struct configfs_attribute *) sd->s_element); | |
101 | +} | |
102 | + | |
103 | +static inline struct config_item *configfs_get_config_item(struct dentry *dentry) | |
104 | +{ | |
105 | + struct config_item * item = NULL; | |
106 | + | |
107 | + spin_lock(&dcache_lock); | |
108 | + if (!d_unhashed(dentry)) { | |
109 | + struct configfs_dirent * sd = dentry->d_fsdata; | |
110 | + if (sd->s_type & CONFIGFS_ITEM_LINK) { | |
111 | + struct configfs_symlink * sl = sd->s_element; | |
112 | + item = config_item_get(sl->sl_target); | |
113 | + } else | |
114 | + item = config_item_get(sd->s_element); | |
115 | + } | |
116 | + spin_unlock(&dcache_lock); | |
117 | + | |
118 | + return item; | |
119 | +} | |
120 | + | |
121 | +static inline void release_configfs_dirent(struct configfs_dirent * sd) | |
122 | +{ | |
123 | + if (!(sd->s_type & CONFIGFS_ROOT)) | |
124 | + kfree(sd); | |
125 | +} | |
126 | + | |
127 | +static inline struct configfs_dirent * configfs_get(struct configfs_dirent * sd) | |
128 | +{ | |
129 | + if (sd) { | |
130 | + WARN_ON(!atomic_read(&sd->s_count)); | |
131 | + atomic_inc(&sd->s_count); | |
132 | + } | |
133 | + return sd; | |
134 | +} | |
135 | + | |
136 | +static inline void configfs_put(struct configfs_dirent * sd) | |
137 | +{ | |
138 | + WARN_ON(!atomic_read(&sd->s_count)); | |
139 | + if (atomic_dec_and_test(&sd->s_count)) | |
140 | + release_configfs_dirent(sd); | |
141 | +} |
fs/configfs/dir.c
Changes suppressed. Click to show
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * dir.c - Operations for configfs directories. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on sysfs: | |
22 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
23 | + * | |
24 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
25 | + */ | |
26 | + | |
27 | +#undef DEBUG | |
28 | + | |
29 | +#include <linux/fs.h> | |
30 | +#include <linux/mount.h> | |
31 | +#include <linux/module.h> | |
32 | +#include <linux/slab.h> | |
33 | + | |
34 | +#include <linux/configfs.h> | |
35 | +#include "configfs_internal.h" | |
36 | + | |
37 | +DECLARE_RWSEM(configfs_rename_sem); | |
38 | + | |
39 | +static void configfs_d_iput(struct dentry * dentry, | |
40 | + struct inode * inode) | |
41 | +{ | |
42 | + struct configfs_dirent * sd = dentry->d_fsdata; | |
43 | + | |
44 | + if (sd) { | |
45 | + BUG_ON(sd->s_dentry != dentry); | |
46 | + sd->s_dentry = NULL; | |
47 | + configfs_put(sd); | |
48 | + } | |
49 | + iput(inode); | |
50 | +} | |
51 | + | |
52 | +/* | |
53 | + * We _must_ delete our dentries on last dput, as the chain-to-parent | |
54 | + * behavior is required to clear the parents of default_groups. | |
55 | + */ | |
56 | +static int configfs_d_delete(struct dentry *dentry) | |
57 | +{ | |
58 | + return 1; | |
59 | +} | |
60 | + | |
61 | +static struct dentry_operations configfs_dentry_ops = { | |
62 | + .d_iput = configfs_d_iput, | |
63 | + /* simple_delete_dentry() isn't exported */ | |
64 | + .d_delete = configfs_d_delete, | |
65 | +}; | |
66 | + | |
67 | +/* | |
68 | + * Allocates a new configfs_dirent and links it to the parent configfs_dirent | |
69 | + */ | |
70 | +static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent * parent_sd, | |
71 | + void * element) | |
72 | +{ | |
73 | + struct configfs_dirent * sd; | |
74 | + | |
75 | + sd = kmalloc(sizeof(*sd), GFP_KERNEL); | |
76 | + if (!sd) | |
77 | + return NULL; | |
78 | + | |
79 | + memset(sd, 0, sizeof(*sd)); | |
80 | + atomic_set(&sd->s_count, 1); | |
81 | + INIT_LIST_HEAD(&sd->s_links); | |
82 | + INIT_LIST_HEAD(&sd->s_children); | |
83 | + list_add(&sd->s_sibling, &parent_sd->s_children); | |
84 | + sd->s_element = element; | |
85 | + | |
86 | + return sd; | |
87 | +} | |
88 | + | |
89 | +int configfs_make_dirent(struct configfs_dirent * parent_sd, | |
90 | + struct dentry * dentry, void * element, | |
91 | + umode_t mode, int type) | |
92 | +{ | |
93 | + struct configfs_dirent * sd; | |
94 | + | |
95 | + sd = configfs_new_dirent(parent_sd, element); | |
96 | + if (!sd) | |
97 | + return -ENOMEM; | |
98 | + | |
99 | + sd->s_mode = mode; | |
100 | + sd->s_type = type; | |
101 | + sd->s_dentry = dentry; | |
102 | + if (dentry) { | |
103 | + dentry->d_fsdata = configfs_get(sd); | |
104 | + dentry->d_op = &configfs_dentry_ops; | |
105 | + } | |
106 | + | |
107 | + return 0; | |
108 | +} | |
109 | + | |
110 | +static int init_dir(struct inode * inode) | |
111 | +{ | |
112 | + inode->i_op = &configfs_dir_inode_operations; | |
113 | + inode->i_fop = &configfs_dir_operations; | |
114 | + | |
115 | + /* directory inodes start off with i_nlink == 2 (for "." entry) */ | |
116 | + inode->i_nlink++; | |
117 | + return 0; | |
118 | +} | |
119 | + | |
120 | +static int init_file(struct inode * inode) | |
121 | +{ | |
122 | + inode->i_size = PAGE_SIZE; | |
123 | + inode->i_fop = &configfs_file_operations; | |
124 | + return 0; | |
125 | +} | |
126 | + | |
127 | +static int init_symlink(struct inode * inode) | |
128 | +{ | |
129 | + inode->i_op = &configfs_symlink_inode_operations; | |
130 | + return 0; | |
131 | +} | |
132 | + | |
133 | +static int create_dir(struct config_item * k, struct dentry * p, | |
134 | + struct dentry * d) | |
135 | +{ | |
136 | + int error; | |
137 | + umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; | |
138 | + | |
139 | + error = configfs_create(d, mode, init_dir); | |
140 | + if (!error) { | |
141 | + error = configfs_make_dirent(p->d_fsdata, d, k, mode, | |
142 | + CONFIGFS_DIR); | |
143 | + if (!error) { | |
144 | + p->d_inode->i_nlink++; | |
145 | + (d)->d_op = &configfs_dentry_ops; | |
146 | + } | |
147 | + } | |
148 | + return error; | |
149 | +} | |
150 | + | |
151 | + | |
152 | +/** | |
153 | + * configfs_create_dir - create a directory for an config_item. | |
154 | + * @item: config_itemwe're creating directory for. | |
155 | + * @dentry: config_item's dentry. | |
156 | + */ | |
157 | + | |
158 | +static int configfs_create_dir(struct config_item * item, struct dentry *dentry) | |
159 | +{ | |
160 | + struct dentry * parent; | |
161 | + int error = 0; | |
162 | + | |
163 | + BUG_ON(!item); | |
164 | + | |
165 | + if (item->ci_parent) | |
166 | + parent = item->ci_parent->ci_dentry; | |
167 | + else if (configfs_mount && configfs_mount->mnt_sb) | |
168 | + parent = configfs_mount->mnt_sb->s_root; | |
169 | + else | |
170 | + return -EFAULT; | |
171 | + | |
172 | + error = create_dir(item,parent,dentry); | |
173 | + if (!error) | |
174 | + item->ci_dentry = dentry; | |
175 | + return error; | |
176 | +} | |
177 | + | |
178 | +int configfs_create_link(struct configfs_symlink *sl, | |
179 | + struct dentry *parent, | |
180 | + struct dentry *dentry) | |
181 | +{ | |
182 | + int err = 0; | |
183 | + umode_t mode = S_IFLNK | S_IRWXUGO; | |
184 | + | |
185 | + err = configfs_create(dentry, mode, init_symlink); | |
186 | + if (!err) { | |
187 | + err = configfs_make_dirent(parent->d_fsdata, dentry, sl, | |
188 | + mode, CONFIGFS_ITEM_LINK); | |
189 | + if (!err) | |
190 | + dentry->d_op = &configfs_dentry_ops; | |
191 | + } | |
192 | + return err; | |
193 | +} | |
194 | + | |
195 | +static void remove_dir(struct dentry * d) | |
196 | +{ | |
197 | + struct dentry * parent = dget(d->d_parent); | |
198 | + struct configfs_dirent * sd; | |
199 | + | |
200 | + sd = d->d_fsdata; | |
201 | + list_del_init(&sd->s_sibling); | |
202 | + configfs_put(sd); | |
203 | + if (d->d_inode) | |
204 | + simple_rmdir(parent->d_inode,d); | |
205 | + | |
206 | + pr_debug(" o %s removing done (%d)\n",d->d_name.name, | |
207 | + atomic_read(&d->d_count)); | |
208 | + | |
209 | + dput(parent); | |
210 | +} | |
211 | + | |
212 | +/** | |
213 | + * configfs_remove_dir - remove an config_item's directory. | |
214 | + * @item: config_item we're removing. | |
215 | + * | |
216 | + * The only thing special about this is that we remove any files in | |
217 | + * the directory before we remove the directory, and we've inlined | |
218 | + * what used to be configfs_rmdir() below, instead of calling separately. | |
219 | + */ | |
220 | + | |
221 | +static void configfs_remove_dir(struct config_item * item) | |
222 | +{ | |
223 | + struct dentry * dentry = dget(item->ci_dentry); | |
224 | + | |
225 | + if (!dentry) | |
226 | + return; | |
227 | + | |
228 | + remove_dir(dentry); | |
229 | + /** | |
230 | + * Drop reference from dget() on entrance. | |
231 | + */ | |
232 | + dput(dentry); | |
233 | +} | |
234 | + | |
235 | + | |
236 | +/* attaches attribute's configfs_dirent to the dentry corresponding to the | |
237 | + * attribute file | |
238 | + */ | |
239 | +static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * dentry) | |
240 | +{ | |
241 | + struct configfs_attribute * attr = sd->s_element; | |
242 | + int error; | |
243 | + | |
244 | + error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, init_file); | |
245 | + if (error) | |
246 | + return error; | |
247 | + | |
248 | + dentry->d_op = &configfs_dentry_ops; | |
249 | + dentry->d_fsdata = configfs_get(sd); | |
250 | + sd->s_dentry = dentry; | |
251 | + d_rehash(dentry); | |
252 | + | |
253 | + return 0; | |
254 | +} | |
255 | + | |
256 | +static struct dentry * configfs_lookup(struct inode *dir, | |
257 | + struct dentry *dentry, | |
258 | + struct nameidata *nd) | |
259 | +{ | |
260 | + struct configfs_dirent * parent_sd = dentry->d_parent->d_fsdata; | |
261 | + struct configfs_dirent * sd; | |
262 | + int found = 0; | |
263 | + int err = 0; | |
264 | + | |
265 | + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | |
266 | + if (sd->s_type & CONFIGFS_NOT_PINNED) { | |
267 | + const unsigned char * name = configfs_get_name(sd); | |
268 | + | |
269 | + if (strcmp(name, dentry->d_name.name)) | |
270 | + continue; | |
271 | + | |
272 | + found = 1; | |
273 | + err = configfs_attach_attr(sd, dentry); | |
274 | + break; | |
275 | + } | |
276 | + } | |
277 | + | |
278 | + if (!found) { | |
279 | + /* | |
280 | + * If it doesn't exist and it isn't a NOT_PINNED item, | |
281 | + * it must be negative. | |
282 | + */ | |
283 | + return simple_lookup(dir, dentry, nd); | |
284 | + } | |
285 | + | |
286 | + return ERR_PTR(err); | |
287 | +} | |
288 | + | |
289 | +/* | |
290 | + * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are | |
291 | + * attributes and are removed by rmdir(). We recurse, taking i_sem | |
292 | + * on all children that are candidates for default detach. If the | |
293 | + * result is clean, then configfs_detach_group() will handle dropping | |
294 | + * i_sem. If there is an error, the caller will clean up the i_sem | |
295 | + * holders via configfs_detach_rollback(). | |
296 | + */ | |
297 | +static int configfs_detach_prep(struct dentry *dentry) | |
298 | +{ | |
299 | + struct configfs_dirent *parent_sd = dentry->d_fsdata; | |
300 | + struct configfs_dirent *sd; | |
301 | + int ret; | |
302 | + | |
303 | + ret = -EBUSY; | |
304 | + if (!list_empty(&parent_sd->s_links)) | |
305 | + goto out; | |
306 | + | |
307 | + ret = 0; | |
308 | + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | |
309 | + if (sd->s_type & CONFIGFS_NOT_PINNED) | |
310 | + continue; | |
311 | + if (sd->s_type & CONFIGFS_USET_DEFAULT) { | |
312 | + down(&sd->s_dentry->d_inode->i_sem); | |
313 | + /* Mark that we've taken i_sem */ | |
314 | + sd->s_type |= CONFIGFS_USET_DROPPING; | |
315 | + | |
316 | + ret = configfs_detach_prep(sd->s_dentry); | |
317 | + if (!ret) | |
318 | + continue; | |
319 | + } else | |
320 | + ret = -ENOTEMPTY; | |
321 | + | |
322 | + break; | |
323 | + } | |
324 | + | |
325 | +out: | |
326 | + return ret; | |
327 | +} | |
328 | + | |
329 | +/* | |
330 | + * Walk the tree, dropping i_sem wherever CONFIGFS_USET_DROPPING is | |
331 | + * set. | |
332 | + */ | |
333 | +static void configfs_detach_rollback(struct dentry *dentry) | |
334 | +{ | |
335 | + struct configfs_dirent *parent_sd = dentry->d_fsdata; | |
336 | + struct configfs_dirent *sd; | |
337 | + | |
338 | + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | |
339 | + if (sd->s_type & CONFIGFS_USET_DEFAULT) { | |
340 | + configfs_detach_rollback(sd->s_dentry); | |
341 | + | |
342 | + if (sd->s_type & CONFIGFS_USET_DROPPING) { | |
343 | + sd->s_type &= ~CONFIGFS_USET_DROPPING; | |
344 | + up(&sd->s_dentry->d_inode->i_sem); | |
345 | + } | |
346 | + } | |
347 | + } | |
348 | +} | |
349 | + | |
350 | +static void detach_attrs(struct config_item * item) | |
351 | +{ | |
352 | + struct dentry * dentry = dget(item->ci_dentry); | |
353 | + struct configfs_dirent * parent_sd; | |
354 | + struct configfs_dirent * sd, * tmp; | |
355 | + | |
356 | + if (!dentry) | |
357 | + return; | |
358 | + | |
359 | + pr_debug("configfs %s: dropping attrs for dir\n", | |
360 | + dentry->d_name.name); | |
361 | + | |
362 | + parent_sd = dentry->d_fsdata; | |
363 | + list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { | |
364 | + if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED)) | |
365 | + continue; | |
366 | + list_del_init(&sd->s_sibling); | |
367 | + configfs_drop_dentry(sd, dentry); | |
368 | + configfs_put(sd); | |
369 | + } | |
370 | + | |
371 | + /** | |
372 | + * Drop reference from dget() on entrance. | |
373 | + */ | |
374 | + dput(dentry); | |
375 | +} | |
376 | + | |
377 | +static int populate_attrs(struct config_item *item) | |
378 | +{ | |
379 | + struct config_item_type *t = item->ci_type; | |
380 | + struct configfs_attribute *attr; | |
381 | + int error = 0; | |
382 | + int i; | |
383 | + | |
384 | + if (!t) | |
385 | + return -EINVAL; | |
386 | + if (t->ct_attrs) { | |
387 | + for (i = 0; (attr = t->ct_attrs[i]) != NULL; i++) { | |
388 | + if ((error = configfs_create_file(item, attr))) | |
389 | + break; | |
390 | + } | |
391 | + } | |
392 | + | |
393 | + if (error) | |
394 | + detach_attrs(item); | |
395 | + | |
396 | + return error; | |
397 | +} | |
398 | + | |
399 | +static int configfs_attach_group(struct config_item *parent_item, | |
400 | + struct config_item *item, | |
401 | + struct dentry *dentry); | |
402 | +static void configfs_detach_group(struct config_item *item); | |
403 | + | |
404 | +static void detach_groups(struct config_group *group) | |
405 | +{ | |
406 | + struct dentry * dentry = dget(group->cg_item.ci_dentry); | |
407 | + struct dentry *child; | |
408 | + struct configfs_dirent *parent_sd; | |
409 | + struct configfs_dirent *sd, *tmp; | |
410 | + | |
411 | + if (!dentry) | |
412 | + return; | |
413 | + | |
414 | + parent_sd = dentry->d_fsdata; | |
415 | + list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { | |
416 | + if (!sd->s_element || | |
417 | + !(sd->s_type & CONFIGFS_USET_DEFAULT)) | |
418 | + continue; | |
419 | + | |
420 | + child = sd->s_dentry; | |
421 | + | |
422 | + configfs_detach_group(sd->s_element); | |
423 | + child->d_inode->i_flags |= S_DEAD; | |
424 | + | |
425 | + /* | |
426 | + * From rmdir/unregister, a configfs_detach_prep() pass | |
427 | + * has taken our i_sem for us. Drop it. | |
428 | + * From mkdir/register cleanup, there is no sem held. | |
429 | + */ | |
430 | + if (sd->s_type & CONFIGFS_USET_DROPPING) | |
431 | + up(&child->d_inode->i_sem); | |
432 | + | |
433 | + d_delete(child); | |
434 | + dput(child); | |
435 | + } | |
436 | + | |
437 | + /** | |
438 | + * Drop reference from dget() on entrance. | |
439 | + */ | |
440 | + dput(dentry); | |
441 | +} | |
442 | + | |
443 | +/* | |
444 | + * This fakes mkdir(2) on a default_groups[] entry. It | |
445 | + * creates a dentry, attachs it, and then does fixup | |
446 | + * on the sd->s_type. | |
447 | + * | |
448 | + * We could, perhaps, tweak our parent's ->mkdir for a minute and | |
449 | + * try using vfs_mkdir. Just a thought. | |
450 | + */ | |
451 | +static int create_default_group(struct config_group *parent_group, | |
452 | + struct config_group *group) | |
453 | +{ | |
454 | + int ret; | |
455 | + struct qstr name; | |
456 | + struct configfs_dirent *sd; | |
457 | + /* We trust the caller holds a reference to parent */ | |
458 | + struct dentry *child, *parent = parent_group->cg_item.ci_dentry; | |
459 | + | |
460 | + if (!group->cg_item.ci_name) | |
461 | + group->cg_item.ci_name = group->cg_item.ci_namebuf; | |
462 | + name.name = group->cg_item.ci_name; | |
463 | + name.len = strlen(name.name); | |
464 | + name.hash = full_name_hash(name.name, name.len); | |
465 | + | |
466 | + ret = -ENOMEM; | |
467 | + child = d_alloc(parent, &name); | |
468 | + if (child) { | |
469 | + d_add(child, NULL); | |
470 | + | |
471 | + ret = configfs_attach_group(&parent_group->cg_item, | |
472 | + &group->cg_item, child); | |
473 | + if (!ret) { | |
474 | + sd = child->d_fsdata; | |
475 | + sd->s_type |= CONFIGFS_USET_DEFAULT; | |
476 | + } else { | |
477 | + d_delete(child); | |
478 | + dput(child); | |
479 | + } | |
480 | + } | |
481 | + | |
482 | + return ret; | |
483 | +} | |
484 | + | |
485 | +static int populate_groups(struct config_group *group) | |
486 | +{ | |
487 | + struct config_group *new_group; | |
488 | + struct dentry *dentry = group->cg_item.ci_dentry; | |
489 | + int ret = 0; | |
490 | + int i; | |
491 | + | |
492 | + if (group && group->default_groups) { | |
493 | + /* FYI, we're faking mkdir here | |
494 | + * I'm not sure we need this semaphore, as we're called | |
495 | + * from our parent's mkdir. That holds our parent's | |
496 | + * i_sem, so afaik lookup cannot continue through our | |
497 | + * parent to find us, let alone mess with our tree. | |
498 | + * That said, taking our i_sem is closer to mkdir | |
499 | + * emulation, and shouldn't hurt. */ | |
500 | + down(&dentry->d_inode->i_sem); | |
501 | + | |
502 | + for (i = 0; group->default_groups[i]; i++) { | |
503 | + new_group = group->default_groups[i]; | |
504 | + | |
505 | + ret = create_default_group(group, new_group); | |
506 | + if (ret) | |
507 | + break; | |
508 | + } | |
509 | + | |
510 | + up(&dentry->d_inode->i_sem); | |
511 | + } | |
512 | + | |
513 | + if (ret) | |
514 | + detach_groups(group); | |
515 | + | |
516 | + return ret; | |
517 | +} | |
518 | + | |
519 | +/* | |
520 | + * All of link_obj/unlink_obj/link_group/unlink_group require that | |
521 | + * subsys->su_sem is held. | |
522 | + */ | |
523 | + | |
524 | +static void unlink_obj(struct config_item *item) | |
525 | +{ | |
526 | + struct config_group *group; | |
527 | + | |
528 | + group = item->ci_group; | |
529 | + if (group) { | |
530 | + list_del_init(&item->ci_entry); | |
531 | + | |
532 | + item->ci_group = NULL; | |
533 | + item->ci_parent = NULL; | |
534 | + config_item_put(item); | |
535 | + | |
536 | + config_group_put(group); | |
537 | + } | |
538 | +} | |
539 | + | |
540 | +static void link_obj(struct config_item *parent_item, struct config_item *item) | |
541 | +{ | |
542 | + /* Parent seems redundant with group, but it makes certain | |
543 | + * traversals much nicer. */ | |
544 | + item->ci_parent = parent_item; | |
545 | + item->ci_group = config_group_get(to_config_group(parent_item)); | |
546 | + list_add_tail(&item->ci_entry, &item->ci_group->cg_children); | |
547 | + | |
548 | + config_item_get(item); | |
549 | +} | |
550 | + | |
551 | +static void unlink_group(struct config_group *group) | |
552 | +{ | |
553 | + int i; | |
554 | + struct config_group *new_group; | |
555 | + | |
556 | + if (group->default_groups) { | |
557 | + for (i = 0; group->default_groups[i]; i++) { | |
558 | + new_group = group->default_groups[i]; | |
559 | + unlink_group(new_group); | |
560 | + } | |
561 | + } | |
562 | + | |
563 | + group->cg_subsys = NULL; | |
564 | + unlink_obj(&group->cg_item); | |
565 | +} | |
566 | + | |
567 | +static void link_group(struct config_group *parent_group, struct config_group *group) | |
568 | +{ | |
569 | + int i; | |
570 | + struct config_group *new_group; | |
571 | + struct configfs_subsystem *subsys = NULL; /* gcc is a turd */ | |
572 | + | |
573 | + link_obj(&parent_group->cg_item, &group->cg_item); | |
574 | + | |
575 | + if (parent_group->cg_subsys) | |
576 | + subsys = parent_group->cg_subsys; | |
577 | + else if (configfs_is_root(&parent_group->cg_item)) | |
578 | + subsys = to_configfs_subsystem(group); | |
579 | + else | |
580 | + BUG(); | |
581 | + group->cg_subsys = subsys; | |
582 | + | |
583 | + if (group->default_groups) { | |
584 | + for (i = 0; group->default_groups[i]; i++) { | |
585 | + new_group = group->default_groups[i]; | |
586 | + link_group(group, new_group); | |
587 | + } | |
588 | + } | |
589 | +} | |
590 | + | |
591 | +/* | |
592 | + * The goal is that configfs_attach_item() (and | |
593 | + * configfs_attach_group()) can be called from either the VFS or this | |
594 | + * module. That is, they assume that the items have been created, | |
595 | + * the dentry allocated, and the dcache is all ready to go. | |
596 | + * | |
597 | + * If they fail, they must clean up after themselves as if they | |
598 | + * had never been called. The caller (VFS or local function) will | |
599 | + * handle cleaning up the dcache bits. | |
600 | + * | |
601 | + * configfs_detach_group() and configfs_detach_item() behave similarly on | |
602 | + * the way out. They assume that the proper semaphores are held, they | |
603 | + * clean up the configfs items, and they expect their callers will | |
604 | + * handle the dcache bits. | |
605 | + */ | |
606 | +static int configfs_attach_item(struct config_item *parent_item, | |
607 | + struct config_item *item, | |
608 | + struct dentry *dentry) | |
609 | +{ | |
610 | + int ret; | |
611 | + | |
612 | + ret = configfs_create_dir(item, dentry); | |
613 | + if (!ret) { | |
614 | + ret = populate_attrs(item); | |
615 | + if (ret) { | |
616 | + configfs_remove_dir(item); | |
617 | + d_delete(dentry); | |
618 | + } | |
619 | + } | |
620 | + | |
621 | + return ret; | |
622 | +} | |
623 | + | |
624 | +static void configfs_detach_item(struct config_item *item) | |
625 | +{ | |
626 | + detach_attrs(item); | |
627 | + configfs_remove_dir(item); | |
628 | +} | |
629 | + | |
630 | +static int configfs_attach_group(struct config_item *parent_item, | |
631 | + struct config_item *item, | |
632 | + struct dentry *dentry) | |
633 | +{ | |
634 | + int ret; | |
635 | + struct configfs_dirent *sd; | |
636 | + | |
637 | + ret = configfs_attach_item(parent_item, item, dentry); | |
638 | + if (!ret) { | |
639 | + sd = dentry->d_fsdata; | |
640 | + sd->s_type |= CONFIGFS_USET_DIR; | |
641 | + | |
642 | + ret = populate_groups(to_config_group(item)); | |
643 | + if (ret) { | |
644 | + configfs_detach_item(item); | |
645 | + d_delete(dentry); | |
646 | + } | |
647 | + } | |
648 | + | |
649 | + return ret; | |
650 | +} | |
651 | + | |
652 | +static void configfs_detach_group(struct config_item *item) | |
653 | +{ | |
654 | + detach_groups(to_config_group(item)); | |
655 | + configfs_detach_item(item); | |
656 | +} | |
657 | + | |
658 | +/* | |
659 | + * Drop the initial reference from make_item()/make_group() | |
660 | + * This function assumes that reference is held on item | |
661 | + * and that item holds a valid reference to the parent. Also, it | |
662 | + * assumes the caller has validated ci_type. | |
663 | + */ | |
664 | +static void client_drop_item(struct config_item *parent_item, | |
665 | + struct config_item *item) | |
666 | +{ | |
667 | + struct config_item_type *type; | |
668 | + | |
669 | + type = parent_item->ci_type; | |
670 | + BUG_ON(!type); | |
671 | + | |
672 | + if (type->ct_group_ops && type->ct_group_ops->drop_item) | |
673 | + type->ct_group_ops->drop_item(to_config_group(parent_item), | |
674 | + item); | |
675 | + else | |
676 | + config_item_put(item); | |
677 | +} | |
678 | + | |
679 | + | |
680 | +static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |
681 | +{ | |
682 | + int ret; | |
683 | + struct config_group *group; | |
684 | + struct config_item *item; | |
685 | + struct config_item *parent_item; | |
686 | + struct configfs_subsystem *subsys; | |
687 | + struct configfs_dirent *sd; | |
688 | + struct config_item_type *type; | |
689 | + struct module *owner; | |
690 | + char *name; | |
691 | + | |
692 | + if (dentry->d_parent == configfs_sb->s_root) | |
693 | + return -EPERM; | |
694 | + | |
695 | + sd = dentry->d_parent->d_fsdata; | |
696 | + if (!(sd->s_type & CONFIGFS_USET_DIR)) | |
697 | + return -EPERM; | |
698 | + | |
699 | + parent_item = configfs_get_config_item(dentry->d_parent); | |
700 | + type = parent_item->ci_type; | |
701 | + subsys = to_config_group(parent_item)->cg_subsys; | |
702 | + BUG_ON(!subsys); | |
703 | + | |
704 | + if (!type || !type->ct_group_ops || | |
705 | + (!type->ct_group_ops->make_group && | |
706 | + !type->ct_group_ops->make_item)) { | |
707 | + config_item_put(parent_item); | |
708 | + return -EPERM; /* What lack-of-mkdir returns */ | |
709 | + } | |
710 | + | |
711 | + name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL); | |
712 | + if (!name) { | |
713 | + config_item_put(parent_item); | |
714 | + return -ENOMEM; | |
715 | + } | |
716 | + snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name); | |
717 | + | |
718 | + down(&subsys->su_sem); | |
719 | + group = NULL; | |
720 | + item = NULL; | |
721 | + if (type->ct_group_ops->make_group) { | |
722 | + group = type->ct_group_ops->make_group(to_config_group(parent_item), name); | |
723 | + if (group) { | |
724 | + link_group(to_config_group(parent_item), group); | |
725 | + item = &group->cg_item; | |
726 | + } | |
727 | + } else { | |
728 | + item = type->ct_group_ops->make_item(to_config_group(parent_item), name); | |
729 | + if (item) | |
730 | + link_obj(parent_item, item); | |
731 | + } | |
732 | + up(&subsys->su_sem); | |
733 | + | |
734 | + kfree(name); | |
735 | + if (!item) { | |
736 | + config_item_put(parent_item); | |
737 | + return -ENOMEM; | |
738 | + } | |
739 | + | |
740 | + ret = -EINVAL; | |
741 | + type = item->ci_type; | |
742 | + if (type) { | |
743 | + owner = type->ct_owner; | |
744 | + if (try_module_get(owner)) { | |
745 | + if (group) { | |
746 | + ret = configfs_attach_group(parent_item, | |
747 | + item, | |
748 | + dentry); | |
749 | + } else { | |
750 | + ret = configfs_attach_item(parent_item, | |
751 | + item, | |
752 | + dentry); | |
753 | + } | |
754 | + | |
755 | + if (ret) { | |
756 | + down(&subsys->su_sem); | |
757 | + if (group) | |
758 | + unlink_group(group); | |
759 | + else | |
760 | + unlink_obj(item); | |
761 | + client_drop_item(parent_item, item); | |
762 | + up(&subsys->su_sem); | |
763 | + | |
764 | + config_item_put(parent_item); | |
765 | + module_put(owner); | |
766 | + } | |
767 | + } | |
768 | + } | |
769 | + | |
770 | + return ret; | |
771 | +} | |
772 | + | |
773 | +static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |
774 | +{ | |
775 | + struct config_item *parent_item; | |
776 | + struct config_item *item; | |
777 | + struct configfs_subsystem *subsys; | |
778 | + struct configfs_dirent *sd; | |
779 | + struct module *owner = NULL; | |
780 | + int ret; | |
781 | + | |
782 | + if (dentry->d_parent == configfs_sb->s_root) | |
783 | + return -EPERM; | |
784 | + | |
785 | + sd = dentry->d_fsdata; | |
786 | + if (sd->s_type & CONFIGFS_USET_DEFAULT) | |
787 | + return -EPERM; | |
788 | + | |
789 | + parent_item = configfs_get_config_item(dentry->d_parent); | |
790 | + subsys = to_config_group(parent_item)->cg_subsys; | |
791 | + BUG_ON(!subsys); | |
792 | + | |
793 | + if (!parent_item->ci_type) { | |
794 | + config_item_put(parent_item); | |
795 | + return -EINVAL; | |
796 | + } | |
797 | + | |
798 | + ret = configfs_detach_prep(dentry); | |
799 | + if (ret) { | |
800 | + configfs_detach_rollback(dentry); | |
801 | + config_item_put(parent_item); | |
802 | + return ret; | |
803 | + } | |
804 | + | |
805 | + item = configfs_get_config_item(dentry); | |
806 | + | |
807 | + /* Drop reference from above, item already holds one. */ | |
808 | + config_item_put(parent_item); | |
809 | + | |
810 | + if (item->ci_type) | |
811 | + owner = item->ci_type->ct_owner; | |
812 | + | |
813 | + if (sd->s_type & CONFIGFS_USET_DIR) { | |
814 | + configfs_detach_group(item); | |
815 | + | |
816 | + down(&subsys->su_sem); | |
817 | + unlink_group(to_config_group(item)); | |
818 | + } else { | |
819 | + configfs_detach_item(item); | |
820 | + | |
821 | + down(&subsys->su_sem); | |
822 | + unlink_obj(item); | |
823 | + } | |
824 | + | |
825 | + client_drop_item(parent_item, item); | |
826 | + up(&subsys->su_sem); | |
827 | + | |
828 | + /* Drop our reference from above */ | |
829 | + config_item_put(item); | |
830 | + | |
831 | + module_put(owner); | |
832 | + | |
833 | + return 0; | |
834 | +} | |
835 | + | |
836 | +struct inode_operations configfs_dir_inode_operations = { | |
837 | + .mkdir = configfs_mkdir, | |
838 | + .rmdir = configfs_rmdir, | |
839 | + .symlink = configfs_symlink, | |
840 | + .unlink = configfs_unlink, | |
841 | + .lookup = configfs_lookup, | |
842 | +}; | |
843 | + | |
844 | +#if 0 | |
845 | +int configfs_rename_dir(struct config_item * item, const char *new_name) | |
846 | +{ | |
847 | + int error = 0; | |
848 | + struct dentry * new_dentry, * parent; | |
849 | + | |
850 | + if (!strcmp(config_item_name(item), new_name)) | |
851 | + return -EINVAL; | |
852 | + | |
853 | + if (!item->parent) | |
854 | + return -EINVAL; | |
855 | + | |
856 | + down_write(&configfs_rename_sem); | |
857 | + parent = item->parent->dentry; | |
858 | + | |
859 | + down(&parent->d_inode->i_sem); | |
860 | + | |
861 | + new_dentry = lookup_one_len(new_name, parent, strlen(new_name)); | |
862 | + if (!IS_ERR(new_dentry)) { | |
863 | + if (!new_dentry->d_inode) { | |
864 | + error = config_item_set_name(item, "%s", new_name); | |
865 | + if (!error) { | |
866 | + d_add(new_dentry, NULL); | |
867 | + d_move(item->dentry, new_dentry); | |
868 | + } | |
869 | + else | |
870 | + d_delete(new_dentry); | |
871 | + } else | |
872 | + error = -EEXIST; | |
873 | + dput(new_dentry); | |
874 | + } | |
875 | + up(&parent->d_inode->i_sem); | |
876 | + up_write(&configfs_rename_sem); | |
877 | + | |
878 | + return error; | |
879 | +} | |
880 | +#endif | |
881 | + | |
882 | +static int configfs_dir_open(struct inode *inode, struct file *file) | |
883 | +{ | |
884 | + struct dentry * dentry = file->f_dentry; | |
885 | + struct configfs_dirent * parent_sd = dentry->d_fsdata; | |
886 | + | |
887 | + down(&dentry->d_inode->i_sem); | |
888 | + file->private_data = configfs_new_dirent(parent_sd, NULL); | |
889 | + up(&dentry->d_inode->i_sem); | |
890 | + | |
891 | + return file->private_data ? 0 : -ENOMEM; | |
892 | + | |
893 | +} | |
894 | + | |
895 | +static int configfs_dir_close(struct inode *inode, struct file *file) | |
896 | +{ | |
897 | + struct dentry * dentry = file->f_dentry; | |
898 | + struct configfs_dirent * cursor = file->private_data; | |
899 | + | |
900 | + down(&dentry->d_inode->i_sem); | |
901 | + list_del_init(&cursor->s_sibling); | |
902 | + up(&dentry->d_inode->i_sem); | |
903 | + | |
904 | + release_configfs_dirent(cursor); | |
905 | + | |
906 | + return 0; | |
907 | +} | |
908 | + | |
909 | +/* Relationship between s_mode and the DT_xxx types */ | |
910 | +static inline unsigned char dt_type(struct configfs_dirent *sd) | |
911 | +{ | |
912 | + return (sd->s_mode >> 12) & 15; | |
913 | +} | |
914 | + | |
915 | +static int configfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | |
916 | +{ | |
917 | + struct dentry *dentry = filp->f_dentry; | |
918 | + struct configfs_dirent * parent_sd = dentry->d_fsdata; | |
919 | + struct configfs_dirent *cursor = filp->private_data; | |
920 | + struct list_head *p, *q = &cursor->s_sibling; | |
921 | + ino_t ino; | |
922 | + int i = filp->f_pos; | |
923 | + | |
924 | + switch (i) { | |
925 | + case 0: | |
926 | + ino = dentry->d_inode->i_ino; | |
927 | + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) | |
928 | + break; | |
929 | + filp->f_pos++; | |
930 | + i++; | |
931 | + /* fallthrough */ | |
932 | + case 1: | |
933 | + ino = parent_ino(dentry); | |
934 | + if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) | |
935 | + break; | |
936 | + filp->f_pos++; | |
937 | + i++; | |
938 | + /* fallthrough */ | |
939 | + default: | |
940 | + if (filp->f_pos == 2) { | |
941 | + list_del(q); | |
942 | + list_add(q, &parent_sd->s_children); | |
943 | + } | |
944 | + for (p=q->next; p!= &parent_sd->s_children; p=p->next) { | |
945 | + struct configfs_dirent *next; | |
946 | + const char * name; | |
947 | + int len; | |
948 | + | |
949 | + next = list_entry(p, struct configfs_dirent, | |
950 | + s_sibling); | |
951 | + if (!next->s_element) | |
952 | + continue; | |
953 | + | |
954 | + name = configfs_get_name(next); | |
955 | + len = strlen(name); | |
956 | + if (next->s_dentry) | |
957 | + ino = next->s_dentry->d_inode->i_ino; | |
958 | + else | |
959 | + ino = iunique(configfs_sb, 2); | |
960 | + | |
961 | + if (filldir(dirent, name, len, filp->f_pos, ino, | |
962 | + dt_type(next)) < 0) | |
963 | + return 0; | |
964 | + | |
965 | + list_del(q); | |
966 | + list_add(q, p); | |
967 | + p = q; | |
968 | + filp->f_pos++; | |
969 | + } | |
970 | + } | |
971 | + return 0; | |
972 | +} | |
973 | + | |
974 | +static loff_t configfs_dir_lseek(struct file * file, loff_t offset, int origin) | |
975 | +{ | |
976 | + struct dentry * dentry = file->f_dentry; | |
977 | + | |
978 | + down(&dentry->d_inode->i_sem); | |
979 | + switch (origin) { | |
980 | + case 1: | |
981 | + offset += file->f_pos; | |
982 | + case 0: | |
983 | + if (offset >= 0) | |
984 | + break; | |
985 | + default: | |
986 | + up(&file->f_dentry->d_inode->i_sem); | |
987 | + return -EINVAL; | |
988 | + } | |
989 | + if (offset != file->f_pos) { | |
990 | + file->f_pos = offset; | |
991 | + if (file->f_pos >= 2) { | |
992 | + struct configfs_dirent *sd = dentry->d_fsdata; | |
993 | + struct configfs_dirent *cursor = file->private_data; | |
994 | + struct list_head *p; | |
995 | + loff_t n = file->f_pos - 2; | |
996 | + | |
997 | + list_del(&cursor->s_sibling); | |
998 | + p = sd->s_children.next; | |
999 | + while (n && p != &sd->s_children) { | |
1000 | + struct configfs_dirent *next; | |
1001 | + next = list_entry(p, struct configfs_dirent, | |
1002 | + s_sibling); | |
1003 | + if (next->s_element) | |
1004 | + n--; | |
1005 | + p = p->next; | |
1006 | + } | |
1007 | + list_add_tail(&cursor->s_sibling, p); | |
1008 | + } | |
1009 | + } | |
1010 | + up(&dentry->d_inode->i_sem); | |
1011 | + return offset; | |
1012 | +} | |
1013 | + | |
1014 | +struct file_operations configfs_dir_operations = { | |
1015 | + .open = configfs_dir_open, | |
1016 | + .release = configfs_dir_close, | |
1017 | + .llseek = configfs_dir_lseek, | |
1018 | + .read = generic_read_dir, | |
1019 | + .readdir = configfs_readdir, | |
1020 | +}; | |
1021 | + | |
1022 | +int configfs_register_subsystem(struct configfs_subsystem *subsys) | |
1023 | +{ | |
1024 | + int err; | |
1025 | + struct config_group *group = &subsys->su_group; | |
1026 | + struct qstr name; | |
1027 | + struct dentry *dentry; | |
1028 | + struct configfs_dirent *sd; | |
1029 | + | |
1030 | + err = configfs_pin_fs(); | |
1031 | + if (err) | |
1032 | + return err; | |
1033 | + | |
1034 | + if (!group->cg_item.ci_name) | |
1035 | + group->cg_item.ci_name = group->cg_item.ci_namebuf; | |
1036 | + | |
1037 | + sd = configfs_sb->s_root->d_fsdata; | |
1038 | + link_group(to_config_group(sd->s_element), group); | |
1039 | + | |
1040 | + down(&configfs_sb->s_root->d_inode->i_sem); | |
1041 | + | |
1042 | + name.name = group->cg_item.ci_name; | |
1043 | + name.len = strlen(name.name); | |
1044 | + name.hash = full_name_hash(name.name, name.len); | |
1045 | + | |
1046 | + err = -ENOMEM; | |
1047 | + dentry = d_alloc(configfs_sb->s_root, &name); | |
1048 | + if (!dentry) | |
1049 | + goto out_release; | |
1050 | + | |
1051 | + d_add(dentry, NULL); | |
1052 | + | |
1053 | + err = configfs_attach_group(sd->s_element, &group->cg_item, | |
1054 | + dentry); | |
1055 | + if (!err) | |
1056 | + dentry = NULL; | |
1057 | + else | |
1058 | + d_delete(dentry); | |
1059 | + | |
1060 | + up(&configfs_sb->s_root->d_inode->i_sem); | |
1061 | + | |
1062 | + if (dentry) { | |
1063 | + dput(dentry); | |
1064 | +out_release: | |
1065 | + unlink_group(group); | |
1066 | + configfs_release_fs(); | |
1067 | + } | |
1068 | + | |
1069 | + return err; | |
1070 | +} | |
1071 | + | |
1072 | +void configfs_unregister_subsystem(struct configfs_subsystem *subsys) | |
1073 | +{ | |
1074 | + struct config_group *group = &subsys->su_group; | |
1075 | + struct dentry *dentry = group->cg_item.ci_dentry; | |
1076 | + | |
1077 | + if (dentry->d_parent != configfs_sb->s_root) { | |
1078 | + printk(KERN_ERR "configfs: Tried to unregister non-subsystem!\n"); | |
1079 | + return; | |
1080 | + } | |
1081 | + | |
1082 | + down(&configfs_sb->s_root->d_inode->i_sem); | |
1083 | + down(&dentry->d_inode->i_sem); | |
1084 | + if (configfs_detach_prep(dentry)) { | |
1085 | + printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); | |
1086 | + } | |
1087 | + configfs_detach_group(&group->cg_item); | |
1088 | + dentry->d_inode->i_flags |= S_DEAD; | |
1089 | + up(&dentry->d_inode->i_sem); | |
1090 | + | |
1091 | + d_delete(dentry); | |
1092 | + | |
1093 | + up(&configfs_sb->s_root->d_inode->i_sem); | |
1094 | + | |
1095 | + dput(dentry); | |
1096 | + | |
1097 | + unlink_group(group); | |
1098 | + configfs_release_fs(); | |
1099 | +} | |
1100 | + | |
1101 | +EXPORT_SYMBOL(configfs_register_subsystem); | |
1102 | +EXPORT_SYMBOL(configfs_unregister_subsystem); |
fs/configfs/file.c
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * file.c - operations for regular (text) files. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on sysfs: | |
22 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
23 | + * | |
24 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
25 | + */ | |
26 | + | |
27 | +#include <linux/fs.h> | |
28 | +#include <linux/module.h> | |
29 | +#include <linux/dnotify.h> | |
30 | +#include <linux/slab.h> | |
31 | +#include <asm/uaccess.h> | |
32 | +#include <asm/semaphore.h> | |
33 | + | |
34 | +#include <linux/configfs.h> | |
35 | +#include "configfs_internal.h" | |
36 | + | |
37 | + | |
38 | +struct configfs_buffer { | |
39 | + size_t count; | |
40 | + loff_t pos; | |
41 | + char * page; | |
42 | + struct configfs_item_operations * ops; | |
43 | + struct semaphore sem; | |
44 | + int needs_read_fill; | |
45 | +}; | |
46 | + | |
47 | + | |
48 | +/** | |
49 | + * fill_read_buffer - allocate and fill buffer from item. | |
50 | + * @dentry: dentry pointer. | |
51 | + * @buffer: data buffer for file. | |
52 | + * | |
53 | + * Allocate @buffer->page, if it hasn't been already, then call the | |
54 | + * config_item's show() method to fill the buffer with this attribute's | |
55 | + * data. | |
56 | + * This is called only once, on the file's first read. | |
57 | + */ | |
58 | +static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buffer) | |
59 | +{ | |
60 | + struct configfs_attribute * attr = to_attr(dentry); | |
61 | + struct config_item * item = to_item(dentry->d_parent); | |
62 | + struct configfs_item_operations * ops = buffer->ops; | |
63 | + int ret = 0; | |
64 | + ssize_t count; | |
65 | + | |
66 | + if (!buffer->page) | |
67 | + buffer->page = (char *) get_zeroed_page(GFP_KERNEL); | |
68 | + if (!buffer->page) | |
69 | + return -ENOMEM; | |
70 | + | |
71 | + count = ops->show_attribute(item,attr,buffer->page); | |
72 | + buffer->needs_read_fill = 0; | |
73 | + BUG_ON(count > (ssize_t)PAGE_SIZE); | |
74 | + if (count >= 0) | |
75 | + buffer->count = count; | |
76 | + else | |
77 | + ret = count; | |
78 | + return ret; | |
79 | +} | |
80 | + | |
81 | + | |
82 | +/** | |
83 | + * flush_read_buffer - push buffer to userspace. | |
84 | + * @buffer: data buffer for file. | |
85 | + * @userbuf: user-passed buffer. | |
86 | + * @count: number of bytes requested. | |
87 | + * @ppos: file position. | |
88 | + * | |
89 | + * Copy the buffer we filled in fill_read_buffer() to userspace. | |
90 | + * This is done at the reader's leisure, copying and advancing | |
91 | + * the amount they specify each time. | |
92 | + * This may be called continuously until the buffer is empty. | |
93 | + */ | |
94 | +static int flush_read_buffer(struct configfs_buffer * buffer, char __user * buf, | |
95 | + size_t count, loff_t * ppos) | |
96 | +{ | |
97 | + int error; | |
98 | + | |
99 | + if (*ppos > buffer->count) | |
100 | + return 0; | |
101 | + | |
102 | + if (count > (buffer->count - *ppos)) | |
103 | + count = buffer->count - *ppos; | |
104 | + | |
105 | + error = copy_to_user(buf,buffer->page + *ppos,count); | |
106 | + if (!error) | |
107 | + *ppos += count; | |
108 | + return error ? -EFAULT : count; | |
109 | +} | |
110 | + | |
111 | +/** | |
112 | + * configfs_read_file - read an attribute. | |
113 | + * @file: file pointer. | |
114 | + * @buf: buffer to fill. | |
115 | + * @count: number of bytes to read. | |
116 | + * @ppos: starting offset in file. | |
117 | + * | |
118 | + * Userspace wants to read an attribute file. The attribute descriptor | |
119 | + * is in the file's ->d_fsdata. The target item is in the directory's | |
120 | + * ->d_fsdata. | |
121 | + * | |
122 | + * We call fill_read_buffer() to allocate and fill the buffer from the | |
123 | + * item's show() method exactly once (if the read is happening from | |
124 | + * the beginning of the file). That should fill the entire buffer with | |
125 | + * all the data the item has to offer for that attribute. | |
126 | + * We then call flush_read_buffer() to copy the buffer to userspace | |
127 | + * in the increments specified. | |
128 | + */ | |
129 | + | |
130 | +static ssize_t | |
131 | +configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |
132 | +{ | |
133 | + struct configfs_buffer * buffer = file->private_data; | |
134 | + ssize_t retval = 0; | |
135 | + | |
136 | + down(&buffer->sem); | |
137 | + if (buffer->needs_read_fill) { | |
138 | + if ((retval = fill_read_buffer(file->f_dentry,buffer))) | |
139 | + goto out; | |
140 | + } | |
141 | + pr_debug("%s: count = %d, ppos = %lld, buf = %s\n", | |
142 | + __FUNCTION__,count,*ppos,buffer->page); | |
143 | + retval = flush_read_buffer(buffer,buf,count,ppos); | |
144 | +out: | |
145 | + up(&buffer->sem); | |
146 | + return retval; | |
147 | +} | |
148 | + | |
149 | + | |
150 | +/** | |
151 | + * fill_write_buffer - copy buffer from userspace. | |
152 | + * @buffer: data buffer for file. | |
153 | + * @userbuf: data from user. | |
154 | + * @count: number of bytes in @userbuf. | |
155 | + * | |
156 | + * Allocate @buffer->page if it hasn't been already, then | |
157 | + * copy the user-supplied buffer into it. | |
158 | + */ | |
159 | + | |
160 | +static int | |
161 | +fill_write_buffer(struct configfs_buffer * buffer, const char __user * buf, size_t count) | |
162 | +{ | |
163 | + int error; | |
164 | + | |
165 | + if (!buffer->page) | |
166 | + buffer->page = (char *)get_zeroed_page(GFP_KERNEL); | |
167 | + if (!buffer->page) | |
168 | + return -ENOMEM; | |
169 | + | |
170 | + if (count > PAGE_SIZE) | |
171 | + count = PAGE_SIZE; | |
172 | + error = copy_from_user(buffer->page,buf,count); | |
173 | + buffer->needs_read_fill = 1; | |
174 | + return error ? -EFAULT : count; | |
175 | +} | |
176 | + | |
177 | + | |
178 | +/** | |
179 | + * flush_write_buffer - push buffer to config_item. | |
180 | + * @file: file pointer. | |
181 | + * @buffer: data buffer for file. | |
182 | + * | |
183 | + * Get the correct pointers for the config_item and the attribute we're | |
184 | + * dealing with, then call the store() method for the attribute, | |
185 | + * passing the buffer that we acquired in fill_write_buffer(). | |
186 | + */ | |
187 | + | |
188 | +static int | |
189 | +flush_write_buffer(struct dentry * dentry, struct configfs_buffer * buffer, size_t count) | |
190 | +{ | |
191 | + struct configfs_attribute * attr = to_attr(dentry); | |
192 | + struct config_item * item = to_item(dentry->d_parent); | |
193 | + struct configfs_item_operations * ops = buffer->ops; | |
194 | + | |
195 | + return ops->store_attribute(item,attr,buffer->page,count); | |
196 | +} | |
197 | + | |
198 | + | |
199 | +/** | |
200 | + * configfs_write_file - write an attribute. | |
201 | + * @file: file pointer | |
202 | + * @buf: data to write | |
203 | + * @count: number of bytes | |
204 | + * @ppos: starting offset | |
205 | + * | |
206 | + * Similar to configfs_read_file(), though working in the opposite direction. | |
207 | + * We allocate and fill the data from the user in fill_write_buffer(), | |
208 | + * then push it to the config_item in flush_write_buffer(). | |
209 | + * There is no easy way for us to know if userspace is only doing a partial | |
210 | + * write, so we don't support them. We expect the entire buffer to come | |
211 | + * on the first write. | |
212 | + * Hint: if you're writing a value, first read the file, modify only the | |
213 | + * the value you're changing, then write entire buffer back. | |
214 | + */ | |
215 | + | |
216 | +static ssize_t | |
217 | +configfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | |
218 | +{ | |
219 | + struct configfs_buffer * buffer = file->private_data; | |
220 | + | |
221 | + down(&buffer->sem); | |
222 | + count = fill_write_buffer(buffer,buf,count); | |
223 | + if (count > 0) | |
224 | + count = flush_write_buffer(file->f_dentry,buffer,count); | |
225 | + if (count > 0) | |
226 | + *ppos += count; | |
227 | + up(&buffer->sem); | |
228 | + return count; | |
229 | +} | |
230 | + | |
231 | +static int check_perm(struct inode * inode, struct file * file) | |
232 | +{ | |
233 | + struct config_item *item = configfs_get_config_item(file->f_dentry->d_parent); | |
234 | + struct configfs_attribute * attr = to_attr(file->f_dentry); | |
235 | + struct configfs_buffer * buffer; | |
236 | + struct configfs_item_operations * ops = NULL; | |
237 | + int error = 0; | |
238 | + | |
239 | + if (!item || !attr) | |
240 | + goto Einval; | |
241 | + | |
242 | + /* Grab the module reference for this attribute if we have one */ | |
243 | + if (!try_module_get(attr->ca_owner)) { | |
244 | + error = -ENODEV; | |
245 | + goto Done; | |
246 | + } | |
247 | + | |
248 | + if (item->ci_type) | |
249 | + ops = item->ci_type->ct_item_ops; | |
250 | + else | |
251 | + goto Eaccess; | |
252 | + | |
253 | + /* File needs write support. | |
254 | + * The inode's perms must say it's ok, | |
255 | + * and we must have a store method. | |
256 | + */ | |
257 | + if (file->f_mode & FMODE_WRITE) { | |
258 | + | |
259 | + if (!(inode->i_mode & S_IWUGO) || !ops->store_attribute) | |
260 | + goto Eaccess; | |
261 | + | |
262 | + } | |
263 | + | |
264 | + /* File needs read support. | |
265 | + * The inode's perms must say it's ok, and we there | |
266 | + * must be a show method for it. | |
267 | + */ | |
268 | + if (file->f_mode & FMODE_READ) { | |
269 | + if (!(inode->i_mode & S_IRUGO) || !ops->show_attribute) | |
270 | + goto Eaccess; | |
271 | + } | |
272 | + | |
273 | + /* No error? Great, allocate a buffer for the file, and store it | |
274 | + * it in file->private_data for easy access. | |
275 | + */ | |
276 | + buffer = kmalloc(sizeof(struct configfs_buffer),GFP_KERNEL); | |
277 | + if (buffer) { | |
278 | + memset(buffer,0,sizeof(struct configfs_buffer)); | |
279 | + init_MUTEX(&buffer->sem); | |
280 | + buffer->needs_read_fill = 1; | |
281 | + buffer->ops = ops; | |
282 | + file->private_data = buffer; | |
283 | + } else | |
284 | + error = -ENOMEM; | |
285 | + goto Done; | |
286 | + | |
287 | + Einval: | |
288 | + error = -EINVAL; | |
289 | + goto Done; | |
290 | + Eaccess: | |
291 | + error = -EACCES; | |
292 | + module_put(attr->ca_owner); | |
293 | + Done: | |
294 | + if (error && item) | |
295 | + config_item_put(item); | |
296 | + return error; | |
297 | +} | |
298 | + | |
299 | +static int configfs_open_file(struct inode * inode, struct file * filp) | |
300 | +{ | |
301 | + return check_perm(inode,filp); | |
302 | +} | |
303 | + | |
304 | +static int configfs_release(struct inode * inode, struct file * filp) | |
305 | +{ | |
306 | + struct config_item * item = to_item(filp->f_dentry->d_parent); | |
307 | + struct configfs_attribute * attr = to_attr(filp->f_dentry); | |
308 | + struct module * owner = attr->ca_owner; | |
309 | + struct configfs_buffer * buffer = filp->private_data; | |
310 | + | |
311 | + if (item) | |
312 | + config_item_put(item); | |
313 | + /* After this point, attr should not be accessed. */ | |
314 | + module_put(owner); | |
315 | + | |
316 | + if (buffer) { | |
317 | + if (buffer->page) | |
318 | + free_page((unsigned long)buffer->page); | |
319 | + kfree(buffer); | |
320 | + } | |
321 | + return 0; | |
322 | +} | |
323 | + | |
324 | +struct file_operations configfs_file_operations = { | |
325 | + .read = configfs_read_file, | |
326 | + .write = configfs_write_file, | |
327 | + .llseek = generic_file_llseek, | |
328 | + .open = configfs_open_file, | |
329 | + .release = configfs_release, | |
330 | +}; | |
331 | + | |
332 | + | |
333 | +int configfs_add_file(struct dentry * dir, const struct configfs_attribute * attr, int type) | |
334 | +{ | |
335 | + struct configfs_dirent * parent_sd = dir->d_fsdata; | |
336 | + umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG; | |
337 | + int error = 0; | |
338 | + | |
339 | + down(&dir->d_inode->i_sem); | |
340 | + error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode, type); | |
341 | + up(&dir->d_inode->i_sem); | |
342 | + | |
343 | + return error; | |
344 | +} | |
345 | + | |
346 | + | |
347 | +/** | |
348 | + * configfs_create_file - create an attribute file for an item. | |
349 | + * @item: item we're creating for. | |
350 | + * @attr: atrribute descriptor. | |
351 | + */ | |
352 | + | |
353 | +int configfs_create_file(struct config_item * item, const struct configfs_attribute * attr) | |
354 | +{ | |
355 | + BUG_ON(!item || !item->ci_dentry || !attr); | |
356 | + | |
357 | + return configfs_add_file(item->ci_dentry, attr, | |
358 | + CONFIGFS_ITEM_ATTR); | |
359 | +} |
fs/configfs/inode.c
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * inode.c - basic inode and dentry operations. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on sysfs: | |
22 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
23 | + * | |
24 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
25 | + * | |
26 | + * Please see Documentation/filesystems/configfs.txt for more information. | |
27 | + */ | |
28 | + | |
29 | +#undef DEBUG | |
30 | + | |
31 | +#include <linux/pagemap.h> | |
32 | +#include <linux/namei.h> | |
33 | +#include <linux/backing-dev.h> | |
34 | + | |
35 | +#include <linux/configfs.h> | |
36 | +#include "configfs_internal.h" | |
37 | + | |
38 | +extern struct super_block * configfs_sb; | |
39 | + | |
40 | +static struct address_space_operations configfs_aops = { | |
41 | + .readpage = simple_readpage, | |
42 | + .prepare_write = simple_prepare_write, | |
43 | + .commit_write = simple_commit_write | |
44 | +}; | |
45 | + | |
46 | +static struct backing_dev_info configfs_backing_dev_info = { | |
47 | + .ra_pages = 0, /* No readahead */ | |
48 | + .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | |
49 | +}; | |
50 | + | |
51 | +struct inode * configfs_new_inode(mode_t mode) | |
52 | +{ | |
53 | + struct inode * inode = new_inode(configfs_sb); | |
54 | + if (inode) { | |
55 | + inode->i_mode = mode; | |
56 | + inode->i_uid = 0; | |
57 | + inode->i_gid = 0; | |
58 | + inode->i_blksize = PAGE_CACHE_SIZE; | |
59 | + inode->i_blocks = 0; | |
60 | + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | |
61 | + inode->i_mapping->a_ops = &configfs_aops; | |
62 | + inode->i_mapping->backing_dev_info = &configfs_backing_dev_info; | |
63 | + } | |
64 | + return inode; | |
65 | +} | |
66 | + | |
67 | +int configfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) | |
68 | +{ | |
69 | + int error = 0; | |
70 | + struct inode * inode = NULL; | |
71 | + if (dentry) { | |
72 | + if (!dentry->d_inode) { | |
73 | + if ((inode = configfs_new_inode(mode))) { | |
74 | + if (dentry->d_parent && dentry->d_parent->d_inode) { | |
75 | + struct inode *p_inode = dentry->d_parent->d_inode; | |
76 | + p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; | |
77 | + } | |
78 | + goto Proceed; | |
79 | + } | |
80 | + else | |
81 | + error = -ENOMEM; | |
82 | + } else | |
83 | + error = -EEXIST; | |
84 | + } else | |
85 | + error = -ENOENT; | |
86 | + goto Done; | |
87 | + | |
88 | + Proceed: | |
89 | + if (init) | |
90 | + error = init(inode); | |
91 | + if (!error) { | |
92 | + d_instantiate(dentry, inode); | |
93 | + if (S_ISDIR(mode) || S_ISLNK(mode)) | |
94 | + dget(dentry); /* pin link and directory dentries in core */ | |
95 | + } else | |
96 | + iput(inode); | |
97 | + Done: | |
98 | + return error; | |
99 | +} | |
100 | + | |
101 | +/* | |
102 | + * Get the name for corresponding element represented by the given configfs_dirent | |
103 | + */ | |
104 | +const unsigned char * configfs_get_name(struct configfs_dirent *sd) | |
105 | +{ | |
106 | + struct attribute * attr; | |
107 | + | |
108 | + if (!sd || !sd->s_element) | |
109 | + BUG(); | |
110 | + | |
111 | + /* These always have a dentry, so use that */ | |
112 | + if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK)) | |
113 | + return sd->s_dentry->d_name.name; | |
114 | + | |
115 | + if (sd->s_type & CONFIGFS_ITEM_ATTR) { | |
116 | + attr = sd->s_element; | |
117 | + return attr->name; | |
118 | + } | |
119 | + return NULL; | |
120 | +} | |
121 | + | |
122 | + | |
123 | +/* | |
124 | + * Unhashes the dentry corresponding to given configfs_dirent | |
125 | + * Called with parent inode's i_sem held. | |
126 | + */ | |
127 | +void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent) | |
128 | +{ | |
129 | + struct dentry * dentry = sd->s_dentry; | |
130 | + | |
131 | + if (dentry) { | |
132 | + spin_lock(&dcache_lock); | |
133 | + if (!(d_unhashed(dentry) && dentry->d_inode)) { | |
134 | + dget_locked(dentry); | |
135 | + __d_drop(dentry); | |
136 | + spin_unlock(&dcache_lock); | |
137 | + simple_unlink(parent->d_inode, dentry); | |
138 | + } else | |
139 | + spin_unlock(&dcache_lock); | |
140 | + } | |
141 | +} | |
142 | + | |
143 | +void configfs_hash_and_remove(struct dentry * dir, const char * name) | |
144 | +{ | |
145 | + struct configfs_dirent * sd; | |
146 | + struct configfs_dirent * parent_sd = dir->d_fsdata; | |
147 | + | |
148 | + down(&dir->d_inode->i_sem); | |
149 | + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | |
150 | + if (!sd->s_element) | |
151 | + continue; | |
152 | + if (!strcmp(configfs_get_name(sd), name)) { | |
153 | + list_del_init(&sd->s_sibling); | |
154 | + configfs_drop_dentry(sd, dir); | |
155 | + configfs_put(sd); | |
156 | + break; | |
157 | + } | |
158 | + } | |
159 | + up(&dir->d_inode->i_sem); | |
160 | +} |
fs/configfs/item.c
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * item.c - library routines for handling generic config items | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on kobject: | |
22 | + * kobject is Copyright (c) 2002-2003 Patrick Mochel | |
23 | + * | |
24 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
25 | + * | |
26 | + * Please see the file Documentation/filesystems/configfs.txt for | |
27 | + * critical information about using the config_item interface. | |
28 | + */ | |
29 | + | |
30 | +#include <linux/string.h> | |
31 | +#include <linux/module.h> | |
32 | +#include <linux/stat.h> | |
33 | +#include <linux/slab.h> | |
34 | + | |
35 | +#include <linux/configfs.h> | |
36 | + | |
37 | + | |
38 | +static inline struct config_item * to_item(struct list_head * entry) | |
39 | +{ | |
40 | + return container_of(entry,struct config_item,ci_entry); | |
41 | +} | |
42 | + | |
43 | +/* Evil kernel */ | |
44 | +static void config_item_release(struct kref *kref); | |
45 | + | |
46 | +/** | |
47 | + * config_item_init - initialize item. | |
48 | + * @item: item in question. | |
49 | + */ | |
50 | +void config_item_init(struct config_item * item) | |
51 | +{ | |
52 | + kref_init(&item->ci_kref); | |
53 | + INIT_LIST_HEAD(&item->ci_entry); | |
54 | +} | |
55 | + | |
56 | +/** | |
57 | + * config_item_set_name - Set the name of an item | |
58 | + * @item: item. | |
59 | + * @name: name. | |
60 | + * | |
61 | + * If strlen(name) >= CONFIGFS_ITEM_NAME_LEN, then use a | |
62 | + * dynamically allocated string that @item->ci_name points to. | |
63 | + * Otherwise, use the static @item->ci_namebuf array. | |
64 | + */ | |
65 | + | |
66 | +int config_item_set_name(struct config_item * item, const char * fmt, ...) | |
67 | +{ | |
68 | + int error = 0; | |
69 | + int limit = CONFIGFS_ITEM_NAME_LEN; | |
70 | + int need; | |
71 | + va_list args; | |
72 | + char * name; | |
73 | + | |
74 | + /* | |
75 | + * First, try the static array | |
76 | + */ | |
77 | + va_start(args,fmt); | |
78 | + need = vsnprintf(item->ci_namebuf,limit,fmt,args); | |
79 | + va_end(args); | |
80 | + if (need < limit) | |
81 | + name = item->ci_namebuf; | |
82 | + else { | |
83 | + /* | |
84 | + * Need more space? Allocate it and try again | |
85 | + */ | |
86 | + limit = need + 1; | |
87 | + name = kmalloc(limit,GFP_KERNEL); | |
88 | + if (!name) { | |
89 | + error = -ENOMEM; | |
90 | + goto Done; | |
91 | + } | |
92 | + va_start(args,fmt); | |
93 | + need = vsnprintf(name,limit,fmt,args); | |
94 | + va_end(args); | |
95 | + | |
96 | + /* Still? Give up. */ | |
97 | + if (need >= limit) { | |
98 | + kfree(name); | |
99 | + error = -EFAULT; | |
100 | + goto Done; | |
101 | + } | |
102 | + } | |
103 | + | |
104 | + /* Free the old name, if necessary. */ | |
105 | + if (item->ci_name && item->ci_name != item->ci_namebuf) | |
106 | + kfree(item->ci_name); | |
107 | + | |
108 | + /* Now, set the new name */ | |
109 | + item->ci_name = name; | |
110 | + Done: | |
111 | + return error; | |
112 | +} | |
113 | + | |
114 | +EXPORT_SYMBOL(config_item_set_name); | |
115 | + | |
116 | +void config_item_init_type_name(struct config_item *item, | |
117 | + const char *name, | |
118 | + struct config_item_type *type) | |
119 | +{ | |
120 | + config_item_set_name(item, name); | |
121 | + item->ci_type = type; | |
122 | + config_item_init(item); | |
123 | +} | |
124 | +EXPORT_SYMBOL(config_item_init_type_name); | |
125 | + | |
126 | +void config_group_init_type_name(struct config_group *group, const char *name, | |
127 | + struct config_item_type *type) | |
128 | +{ | |
129 | + config_item_set_name(&group->cg_item, name); | |
130 | + group->cg_item.ci_type = type; | |
131 | + config_group_init(group); | |
132 | +} | |
133 | +EXPORT_SYMBOL(config_group_init_type_name); | |
134 | + | |
135 | +struct config_item * config_item_get(struct config_item * item) | |
136 | +{ | |
137 | + if (item) | |
138 | + kref_get(&item->ci_kref); | |
139 | + return item; | |
140 | +} | |
141 | + | |
142 | +/** | |
143 | + * config_item_cleanup - free config_item resources. | |
144 | + * @item: item. | |
145 | + */ | |
146 | + | |
147 | +void config_item_cleanup(struct config_item * item) | |
148 | +{ | |
149 | + struct config_item_type * t = item->ci_type; | |
150 | + struct config_group * s = item->ci_group; | |
151 | + struct config_item * parent = item->ci_parent; | |
152 | + | |
153 | + pr_debug("config_item %s: cleaning up\n",config_item_name(item)); | |
154 | + if (item->ci_name != item->ci_namebuf) | |
155 | + kfree(item->ci_name); | |
156 | + item->ci_name = NULL; | |
157 | + if (t && t->ct_item_ops && t->ct_item_ops->release) | |
158 | + t->ct_item_ops->release(item); | |
159 | + if (s) | |
160 | + config_group_put(s); | |
161 | + if (parent) | |
162 | + config_item_put(parent); | |
163 | +} | |
164 | + | |
165 | +static void config_item_release(struct kref *kref) | |
166 | +{ | |
167 | + config_item_cleanup(container_of(kref, struct config_item, ci_kref)); | |
168 | +} | |
169 | + | |
170 | +/** | |
171 | + * config_item_put - decrement refcount for item. | |
172 | + * @item: item. | |
173 | + * | |
174 | + * Decrement the refcount, and if 0, call config_item_cleanup(). | |
175 | + */ | |
176 | +void config_item_put(struct config_item * item) | |
177 | +{ | |
178 | + if (item) | |
179 | + kref_put(&item->ci_kref, config_item_release); | |
180 | +} | |
181 | + | |
182 | + | |
183 | +/** | |
184 | + * config_group_init - initialize a group for use | |
185 | + * @k: group | |
186 | + */ | |
187 | + | |
188 | +void config_group_init(struct config_group *group) | |
189 | +{ | |
190 | + config_item_init(&group->cg_item); | |
191 | + INIT_LIST_HEAD(&group->cg_children); | |
192 | +} | |
193 | + | |
194 | + | |
195 | +/** | |
196 | + * config_group_find_obj - search for item in group. | |
197 | + * @group: group we're looking in. | |
198 | + * @name: item's name. | |
199 | + * | |
200 | + * Lock group via @group->cg_subsys, and iterate over @group->cg_list, | |
201 | + * looking for a matching config_item. If matching item is found | |
202 | + * take a reference and return the item. | |
203 | + */ | |
204 | + | |
205 | +struct config_item * config_group_find_obj(struct config_group * group, const char * name) | |
206 | +{ | |
207 | + struct list_head * entry; | |
208 | + struct config_item * ret = NULL; | |
209 | + | |
210 | + /* XXX LOCKING! */ | |
211 | + list_for_each(entry,&group->cg_children) { | |
212 | + struct config_item * item = to_item(entry); | |
213 | + if (config_item_name(item) && | |
214 | + !strcmp(config_item_name(item), name)) { | |
215 | + ret = config_item_get(item); | |
216 | + break; | |
217 | + } | |
218 | + } | |
219 | + return ret; | |
220 | +} | |
221 | + | |
222 | + | |
223 | +EXPORT_SYMBOL(config_item_init); | |
224 | +EXPORT_SYMBOL(config_group_init); | |
225 | +EXPORT_SYMBOL(config_item_get); | |
226 | +EXPORT_SYMBOL(config_item_put); |
fs/configfs/mount.c
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * mount.c - operations for initializing and mounting configfs. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on sysfs: | |
22 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
23 | + * | |
24 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
25 | + */ | |
26 | + | |
27 | +#include <linux/fs.h> | |
28 | +#include <linux/module.h> | |
29 | +#include <linux/mount.h> | |
30 | +#include <linux/pagemap.h> | |
31 | +#include <linux/init.h> | |
32 | + | |
33 | +#include <linux/configfs.h> | |
34 | +#include "configfs_internal.h" | |
35 | + | |
36 | +/* Random magic number */ | |
37 | +#define CONFIGFS_MAGIC 0x62656570 | |
38 | + | |
39 | +struct vfsmount * configfs_mount = NULL; | |
40 | +struct super_block * configfs_sb = NULL; | |
41 | +static int configfs_mnt_count = 0; | |
42 | + | |
43 | +static struct super_operations configfs_ops = { | |
44 | + .statfs = simple_statfs, | |
45 | + .drop_inode = generic_delete_inode, | |
46 | +}; | |
47 | + | |
48 | +static struct config_group configfs_root_group = { | |
49 | + .cg_item = { | |
50 | + .ci_namebuf = "root", | |
51 | + .ci_name = configfs_root_group.cg_item.ci_namebuf, | |
52 | + }, | |
53 | +}; | |
54 | + | |
55 | +int configfs_is_root(struct config_item *item) | |
56 | +{ | |
57 | + return item == &configfs_root_group.cg_item; | |
58 | +} | |
59 | + | |
60 | +static struct configfs_dirent configfs_root = { | |
61 | + .s_sibling = LIST_HEAD_INIT(configfs_root.s_sibling), | |
62 | + .s_children = LIST_HEAD_INIT(configfs_root.s_children), | |
63 | + .s_element = &configfs_root_group.cg_item, | |
64 | + .s_type = CONFIGFS_ROOT, | |
65 | +}; | |
66 | + | |
67 | +static int configfs_fill_super(struct super_block *sb, void *data, int silent) | |
68 | +{ | |
69 | + struct inode *inode; | |
70 | + struct dentry *root; | |
71 | + | |
72 | + sb->s_blocksize = PAGE_CACHE_SIZE; | |
73 | + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | |
74 | + sb->s_magic = CONFIGFS_MAGIC; | |
75 | + sb->s_op = &configfs_ops; | |
76 | + configfs_sb = sb; | |
77 | + | |
78 | + inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); | |
79 | + if (inode) { | |
80 | + inode->i_op = &configfs_dir_inode_operations; | |
81 | + inode->i_fop = &configfs_dir_operations; | |
82 | + /* directory inodes start off with i_nlink == 2 (for "." entry) */ | |
83 | + inode->i_nlink++; | |
84 | + } else { | |
85 | + pr_debug("configfs: could not get root inode\n"); | |
86 | + return -ENOMEM; | |
87 | + } | |
88 | + | |
89 | + root = d_alloc_root(inode); | |
90 | + if (!root) { | |
91 | + pr_debug("%s: could not get root dentry!\n",__FUNCTION__); | |
92 | + iput(inode); | |
93 | + return -ENOMEM; | |
94 | + } | |
95 | + config_group_init(&configfs_root_group); | |
96 | + configfs_root_group.cg_item.ci_dentry = root; | |
97 | + root->d_fsdata = &configfs_root; | |
98 | + sb->s_root = root; | |
99 | + return 0; | |
100 | +} | |
101 | + | |
102 | +static struct super_block *configfs_get_sb(struct file_system_type *fs_type, | |
103 | + int flags, const char *dev_name, void *data) | |
104 | +{ | |
105 | + return get_sb_single(fs_type, flags, data, configfs_fill_super); | |
106 | +} | |
107 | + | |
108 | +static struct file_system_type configfs_fs_type = { | |
109 | + .owner = THIS_MODULE, | |
110 | + .name = "configfs", | |
111 | + .get_sb = configfs_get_sb, | |
112 | + .kill_sb = kill_litter_super, | |
113 | +}; | |
114 | + | |
115 | +int configfs_pin_fs(void) | |
116 | +{ | |
117 | + return simple_pin_fs("configfs", &configfs_mount, | |
118 | + &configfs_mnt_count); | |
119 | +} | |
120 | + | |
121 | +void configfs_release_fs(void) | |
122 | +{ | |
123 | + simple_release_fs(&configfs_mount, &configfs_mnt_count); | |
124 | +} | |
125 | + | |
126 | + | |
127 | +static decl_subsys(config, NULL, NULL); | |
128 | + | |
129 | +static int __init configfs_init(void) | |
130 | +{ | |
131 | + int err; | |
132 | + | |
133 | + kset_set_kset_s(&config_subsys, kernel_subsys); | |
134 | + err = subsystem_register(&config_subsys); | |
135 | + if (err) | |
136 | + return err; | |
137 | + | |
138 | + err = register_filesystem(&configfs_fs_type); | |
139 | + if (err) { | |
140 | + printk(KERN_ERR "configfs: Unable to register filesystem!\n"); | |
141 | + subsystem_unregister(&config_subsys); | |
142 | + } | |
143 | + | |
144 | + return err; | |
145 | +} | |
146 | + | |
147 | +static void __exit configfs_exit(void) | |
148 | +{ | |
149 | + unregister_filesystem(&configfs_fs_type); | |
150 | + subsystem_unregister(&config_subsys); | |
151 | +} | |
152 | + | |
153 | +MODULE_AUTHOR("Oracle"); | |
154 | +MODULE_LICENSE("GPL"); | |
155 | +MODULE_VERSION("0.0.1"); | |
156 | +MODULE_DESCRIPTION("Simple RAM filesystem for user driven kernel subsystem configuration."); | |
157 | + | |
158 | +module_init(configfs_init); | |
159 | +module_exit(configfs_exit); |
fs/configfs/symlink.c
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * symlink.c - operations for configfs symlinks. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on sysfs: | |
22 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
23 | + * | |
24 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
25 | + */ | |
26 | + | |
27 | +#include <linux/fs.h> | |
28 | +#include <linux/module.h> | |
29 | +#include <linux/namei.h> | |
30 | + | |
31 | +#include <linux/configfs.h> | |
32 | +#include "configfs_internal.h" | |
33 | + | |
34 | +static int item_depth(struct config_item * item) | |
35 | +{ | |
36 | + struct config_item * p = item; | |
37 | + int depth = 0; | |
38 | + do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p)); | |
39 | + return depth; | |
40 | +} | |
41 | + | |
42 | +static int item_path_length(struct config_item * item) | |
43 | +{ | |
44 | + struct config_item * p = item; | |
45 | + int length = 1; | |
46 | + do { | |
47 | + length += strlen(config_item_name(p)) + 1; | |
48 | + p = p->ci_parent; | |
49 | + } while (p && !configfs_is_root(p)); | |
50 | + return length; | |
51 | +} | |
52 | + | |
53 | +static void fill_item_path(struct config_item * item, char * buffer, int length) | |
54 | +{ | |
55 | + struct config_item * p; | |
56 | + | |
57 | + --length; | |
58 | + for (p = item; p && !configfs_is_root(p); p = p->ci_parent) { | |
59 | + int cur = strlen(config_item_name(p)); | |
60 | + | |
61 | + /* back up enough to print this bus id with '/' */ | |
62 | + length -= cur; | |
63 | + strncpy(buffer + length,config_item_name(p),cur); | |
64 | + *(buffer + --length) = '/'; | |
65 | + } | |
66 | +} | |
67 | + | |
68 | +static int create_link(struct config_item *parent_item, | |
69 | + struct config_item *item, | |
70 | + struct dentry *dentry) | |
71 | +{ | |
72 | + struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; | |
73 | + struct configfs_symlink *sl; | |
74 | + int ret; | |
75 | + | |
76 | + ret = -ENOMEM; | |
77 | + sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); | |
78 | + if (sl) { | |
79 | + sl->sl_target = config_item_get(item); | |
80 | + /* FIXME: needs a lock, I'd bet */ | |
81 | + list_add(&sl->sl_list, &target_sd->s_links); | |
82 | + ret = configfs_create_link(sl, parent_item->ci_dentry, | |
83 | + dentry); | |
84 | + if (ret) { | |
85 | + list_del_init(&sl->sl_list); | |
86 | + config_item_put(item); | |
87 | + kfree(sl); | |
88 | + } | |
89 | + } | |
90 | + | |
91 | + return ret; | |
92 | +} | |
93 | + | |
94 | + | |
95 | +static int get_target(const char *symname, struct nameidata *nd, | |
96 | + struct config_item **target) | |
97 | +{ | |
98 | + int ret; | |
99 | + | |
100 | + ret = path_lookup(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, nd); | |
101 | + if (!ret) { | |
102 | + if (nd->dentry->d_sb == configfs_sb) { | |
103 | + *target = configfs_get_config_item(nd->dentry); | |
104 | + if (!*target) { | |
105 | + ret = -ENOENT; | |
106 | + path_release(nd); | |
107 | + } | |
108 | + } else | |
109 | + ret = -EPERM; | |
110 | + } | |
111 | + | |
112 | + return ret; | |
113 | +} | |
114 | + | |
115 | + | |
116 | +int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | |
117 | +{ | |
118 | + int ret; | |
119 | + struct nameidata nd; | |
120 | + struct config_item *parent_item; | |
121 | + struct config_item *target_item; | |
122 | + struct config_item_type *type; | |
123 | + | |
124 | + ret = -EPERM; /* What lack-of-symlink returns */ | |
125 | + if (dentry->d_parent == configfs_sb->s_root) | |
126 | + goto out; | |
127 | + | |
128 | + parent_item = configfs_get_config_item(dentry->d_parent); | |
129 | + type = parent_item->ci_type; | |
130 | + | |
131 | + if (!type || !type->ct_item_ops || | |
132 | + !type->ct_item_ops->allow_link) | |
133 | + goto out_put; | |
134 | + | |
135 | + ret = get_target(symname, &nd, &target_item); | |
136 | + if (ret) | |
137 | + goto out_put; | |
138 | + | |
139 | + ret = type->ct_item_ops->allow_link(parent_item, target_item); | |
140 | + if (!ret) | |
141 | + ret = create_link(parent_item, target_item, dentry); | |
142 | + | |
143 | + config_item_put(target_item); | |
144 | + path_release(&nd); | |
145 | + | |
146 | +out_put: | |
147 | + config_item_put(parent_item); | |
148 | + | |
149 | +out: | |
150 | + return ret; | |
151 | +} | |
152 | + | |
153 | +int configfs_unlink(struct inode *dir, struct dentry *dentry) | |
154 | +{ | |
155 | + struct configfs_dirent *sd = dentry->d_fsdata; | |
156 | + struct configfs_symlink *sl; | |
157 | + struct config_item *parent_item; | |
158 | + struct config_item_type *type; | |
159 | + int ret; | |
160 | + | |
161 | + ret = -EPERM; /* What lack-of-symlink returns */ | |
162 | + if (!(sd->s_type & CONFIGFS_ITEM_LINK)) | |
163 | + goto out; | |
164 | + | |
165 | + if (dentry->d_parent == configfs_sb->s_root) | |
166 | + BUG(); | |
167 | + | |
168 | + sl = sd->s_element; | |
169 | + | |
170 | + parent_item = configfs_get_config_item(dentry->d_parent); | |
171 | + type = parent_item->ci_type; | |
172 | + | |
173 | + list_del_init(&sd->s_sibling); | |
174 | + configfs_drop_dentry(sd, dentry->d_parent); | |
175 | + dput(dentry); | |
176 | + configfs_put(sd); | |
177 | + | |
178 | + /* | |
179 | + * drop_link() must be called before | |
180 | + * list_del_init(&sl->sl_list), so that the order of | |
181 | + * drop_link(this, target) and drop_item(target) is preserved. | |
182 | + */ | |
183 | + if (type && type->ct_item_ops && | |
184 | + type->ct_item_ops->drop_link) | |
185 | + type->ct_item_ops->drop_link(parent_item, | |
186 | + sl->sl_target); | |
187 | + | |
188 | + /* FIXME: Needs lock */ | |
189 | + list_del_init(&sl->sl_list); | |
190 | + | |
191 | + /* Put reference from create_link() */ | |
192 | + config_item_put(sl->sl_target); | |
193 | + kfree(sl); | |
194 | + | |
195 | + config_item_put(parent_item); | |
196 | + | |
197 | + ret = 0; | |
198 | + | |
199 | +out: | |
200 | + return ret; | |
201 | +} | |
202 | + | |
203 | +static int configfs_get_target_path(struct config_item * item, struct config_item * target, | |
204 | + char *path) | |
205 | +{ | |
206 | + char * s; | |
207 | + int depth, size; | |
208 | + | |
209 | + depth = item_depth(item); | |
210 | + size = item_path_length(target) + depth * 3 - 1; | |
211 | + if (size > PATH_MAX) | |
212 | + return -ENAMETOOLONG; | |
213 | + | |
214 | + pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size); | |
215 | + | |
216 | + for (s = path; depth--; s += 3) | |
217 | + strcpy(s,"../"); | |
218 | + | |
219 | + fill_item_path(target, path, size); | |
220 | + pr_debug("%s: path = '%s'\n", __FUNCTION__, path); | |
221 | + | |
222 | + return 0; | |
223 | +} | |
224 | + | |
225 | +static int configfs_getlink(struct dentry *dentry, char * path) | |
226 | +{ | |
227 | + struct config_item *item, *target_item; | |
228 | + int error = 0; | |
229 | + | |
230 | + item = configfs_get_config_item(dentry->d_parent); | |
231 | + if (!item) | |
232 | + return -EINVAL; | |
233 | + | |
234 | + target_item = configfs_get_config_item(dentry); | |
235 | + if (!target_item) { | |
236 | + config_item_put(item); | |
237 | + return -EINVAL; | |
238 | + } | |
239 | + | |
240 | + down_read(&configfs_rename_sem); | |
241 | + error = configfs_get_target_path(item, target_item, path); | |
242 | + up_read(&configfs_rename_sem); | |
243 | + | |
244 | + config_item_put(item); | |
245 | + config_item_put(target_item); | |
246 | + return error; | |
247 | + | |
248 | +} | |
249 | + | |
250 | +static void *configfs_follow_link(struct dentry *dentry, struct nameidata *nd) | |
251 | +{ | |
252 | + int error = -ENOMEM; | |
253 | + unsigned long page = get_zeroed_page(GFP_KERNEL); | |
254 | + | |
255 | + if (page) { | |
256 | + error = configfs_getlink(dentry, (char *)page); | |
257 | + if (!error) { | |
258 | + nd_set_link(nd, (char *)page); | |
259 | + return (void *)page; | |
260 | + } | |
261 | + } | |
262 | + | |
263 | + nd_set_link(nd, ERR_PTR(error)); | |
264 | + return NULL; | |
265 | +} | |
266 | + | |
267 | +static void configfs_put_link(struct dentry *dentry, struct nameidata *nd, | |
268 | + void *cookie) | |
269 | +{ | |
270 | + if (cookie) { | |
271 | + unsigned long page = (unsigned long)cookie; | |
272 | + free_page(page); | |
273 | + } | |
274 | +} | |
275 | + | |
276 | +struct inode_operations configfs_symlink_inode_operations = { | |
277 | + .follow_link = configfs_follow_link, | |
278 | + .readlink = generic_readlink, | |
279 | + .put_link = configfs_put_link, | |
280 | +}; |
include/linux/configfs.h
1 | +/* -*- mode: c; c-basic-offset: 8; -*- | |
2 | + * vim: noexpandtab sw=8 ts=8 sts=0: | |
3 | + * | |
4 | + * configfs.h - definitions for the device driver filesystem | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public | |
17 | + * License along with this program; if not, write to the | |
18 | + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | + * Boston, MA 021110-1307, USA. | |
20 | + * | |
21 | + * Based on sysfs: | |
22 | + * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | |
23 | + * | |
24 | + * Based on kobject.h: | |
25 | + * Copyright (c) 2002-2003 Patrick Mochel | |
26 | + * Copyright (c) 2002-2003 Open Source Development Labs | |
27 | + * | |
28 | + * configfs Copyright (C) 2005 Oracle. All rights reserved. | |
29 | + * | |
30 | + * Please read Documentation/filesystems/configfs.txt before using the | |
31 | + * configfs interface, ESPECIALLY the parts about reference counts and | |
32 | + * item destructors. | |
33 | + */ | |
34 | + | |
35 | +#ifndef _CONFIGFS_H_ | |
36 | +#define _CONFIGFS_H_ | |
37 | + | |
38 | +#ifdef __KERNEL__ | |
39 | + | |
40 | +#include <linux/types.h> | |
41 | +#include <linux/list.h> | |
42 | +#include <linux/kref.h> | |
43 | + | |
44 | +#include <asm/atomic.h> | |
45 | +#include <asm/semaphore.h> | |
46 | + | |
47 | +#define CONFIGFS_ITEM_NAME_LEN 20 | |
48 | + | |
49 | +struct module; | |
50 | + | |
51 | +struct configfs_item_operations; | |
52 | +struct configfs_group_operations; | |
53 | +struct configfs_attribute; | |
54 | +struct configfs_subsystem; | |
55 | + | |
56 | +struct config_item { | |
57 | + char *ci_name; | |
58 | + char ci_namebuf[CONFIGFS_ITEM_NAME_LEN]; | |
59 | + struct kref ci_kref; | |
60 | + struct list_head ci_entry; | |
61 | + struct config_item *ci_parent; | |
62 | + struct config_group *ci_group; | |
63 | + struct config_item_type *ci_type; | |
64 | + struct dentry *ci_dentry; | |
65 | +}; | |
66 | + | |
67 | +extern int config_item_set_name(struct config_item *, const char *, ...); | |
68 | + | |
69 | +static inline char *config_item_name(struct config_item * item) | |
70 | +{ | |
71 | + return item->ci_name; | |
72 | +} | |
73 | + | |
74 | +extern void config_item_init(struct config_item *); | |
75 | +extern void config_item_init_type_name(struct config_item *item, | |
76 | + const char *name, | |
77 | + struct config_item_type *type); | |
78 | +extern void config_item_cleanup(struct config_item *); | |
79 | + | |
80 | +extern struct config_item * config_item_get(struct config_item *); | |
81 | +extern void config_item_put(struct config_item *); | |
82 | + | |
83 | +struct config_item_type { | |
84 | + struct module *ct_owner; | |
85 | + struct configfs_item_operations *ct_item_ops; | |
86 | + struct configfs_group_operations *ct_group_ops; | |
87 | + struct configfs_attribute **ct_attrs; | |
88 | +}; | |
89 | + | |
90 | + | |
91 | +/** | |
92 | + * group - a group of config_items of a specific type, belonging | |
93 | + * to a specific subsystem. | |
94 | + */ | |
95 | + | |
96 | +struct config_group { | |
97 | + struct config_item cg_item; | |
98 | + struct list_head cg_children; | |
99 | + struct configfs_subsystem *cg_subsys; | |
100 | + struct config_group **default_groups; | |
101 | +}; | |
102 | + | |
103 | + | |
104 | +extern void config_group_init(struct config_group *group); | |
105 | +extern void config_group_init_type_name(struct config_group *group, | |
106 | + const char *name, | |
107 | + struct config_item_type *type); | |
108 | + | |
109 | + | |
110 | +static inline struct config_group *to_config_group(struct config_item *item) | |
111 | +{ | |
112 | + return item ? container_of(item,struct config_group,cg_item) : NULL; | |
113 | +} | |
114 | + | |
115 | +static inline struct config_group *config_group_get(struct config_group *group) | |
116 | +{ | |
117 | + return group ? to_config_group(config_item_get(&group->cg_item)) : NULL; | |
118 | +} | |
119 | + | |
120 | +static inline void config_group_put(struct config_group *group) | |
121 | +{ | |
122 | + config_item_put(&group->cg_item); | |
123 | +} | |
124 | + | |
125 | +extern struct config_item *config_group_find_obj(struct config_group *, const char *); | |
126 | + | |
127 | + | |
128 | +struct configfs_attribute { | |
129 | + char *ca_name; | |
130 | + struct module *ca_owner; | |
131 | + mode_t ca_mode; | |
132 | +}; | |
133 | + | |
134 | + | |
135 | +/* | |
136 | + * If allow_link() exists, the item can symlink(2) out to other | |
137 | + * items. If the item is a group, it may support mkdir(2). | |
138 | + * Groups supply one of make_group() and make_item(). If the | |
139 | + * group supports make_group(), one can create group children. If it | |
140 | + * supports make_item(), one can create config_item children. If it has | |
141 | + * default_groups on group->default_groups, it has automatically created | |
142 | + * group children. default_groups may coexist alongsize make_group() or | |
143 | + * make_item(), but if the group wishes to have only default_groups | |
144 | + * children (disallowing mkdir(2)), it need not provide either function. | |
145 | + * If the group has commit(), it supports pending and commited (active) | |
146 | + * items. | |
147 | + */ | |
148 | +struct configfs_item_operations { | |
149 | + void (*release)(struct config_item *); | |
150 | + ssize_t (*show_attribute)(struct config_item *, struct configfs_attribute *,char *); | |
151 | + ssize_t (*store_attribute)(struct config_item *,struct configfs_attribute *,const char *, size_t); | |
152 | + int (*allow_link)(struct config_item *src, struct config_item *target); | |
153 | + int (*drop_link)(struct config_item *src, struct config_item *target); | |
154 | +}; | |
155 | + | |
156 | +struct configfs_group_operations { | |
157 | + struct config_item *(*make_item)(struct config_group *group, const char *name); | |
158 | + struct config_group *(*make_group)(struct config_group *group, const char *name); | |
159 | + int (*commit_item)(struct config_item *item); | |
160 | + void (*drop_item)(struct config_group *group, struct config_item *item); | |
161 | +}; | |
162 | + | |
163 | + | |
164 | + | |
165 | +/** | |
166 | + * Use these macros to make defining attributes easier. See include/linux/device.h | |
167 | + * for examples.. | |
168 | + */ | |
169 | + | |
170 | +#if 0 | |
171 | +#define __ATTR(_name,_mode,_show,_store) { \ | |
172 | + .attr = {.ca_name = __stringify(_name), .ca_mode = _mode, .ca_owner = THIS_MODULE }, \ | |
173 | + .show = _show, \ | |
174 | + .store = _store, \ | |
175 | +} | |
176 | + | |
177 | +#define __ATTR_RO(_name) { \ | |
178 | + .attr = { .ca_name = __stringify(_name), .ca_mode = 0444, .ca_owner = THIS_MODULE }, \ | |
179 | + .show = _name##_show, \ | |
180 | +} | |
181 | + | |
182 | +#define __ATTR_NULL { .attr = { .name = NULL } } | |
183 | + | |
184 | +#define attr_name(_attr) (_attr).attr.name | |
185 | +#endif | |
186 | + | |
187 | + | |
188 | +struct configfs_subsystem { | |
189 | + struct config_group su_group; | |
190 | + struct semaphore su_sem; | |
191 | +}; | |
192 | + | |
193 | +static inline struct configfs_subsystem *to_configfs_subsystem(struct config_group *group) | |
194 | +{ | |
195 | + return group ? | |
196 | + container_of(group, struct configfs_subsystem, su_group) : | |
197 | + NULL; | |
198 | +} | |
199 | + | |
200 | +int configfs_register_subsystem(struct configfs_subsystem *subsys); | |
201 | +void configfs_unregister_subsystem(struct configfs_subsystem *subsys); | |
202 | + | |
203 | +#endif /* __KERNEL__ */ | |
204 | + | |
205 | +#endif /* _CONFIGFS_H_ */ |