mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	[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>
This commit is contained in:
		
							parent
							
								
									88026842b0
								
							
						
					
					
						commit
						7063fbf226
					
				
					 15 changed files with 3575 additions and 0 deletions
				
			
		|  | @ -12,6 +12,8 @@ cifs.txt | |||
| 	- description of the CIFS filesystem | ||||
| coda.txt | ||||
| 	- description of the CODA filesystem. | ||||
| configfs/ | ||||
| 	- directory containing configfs documentation and example code. | ||||
| cramfs.txt | ||||
| 	- info on the cram filesystem for small storage (ROMs etc) | ||||
| devfs/ | ||||
|  |  | |||
							
								
								
									
										434
									
								
								Documentation/filesystems/configfs/configfs.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								Documentation/filesystems/configfs/configfs.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,434 @@ | |||
| 
 | ||||
| configfs - Userspace-driven kernel object configuation. | ||||
| 
 | ||||
| Joel Becker <joel.becker@oracle.com> | ||||
| 
 | ||||
| Updated: 31 March 2005 | ||||
| 
 | ||||
| Copyright (c) 2005 Oracle Corporation, | ||||
| 	Joel Becker <joel.becker@oracle.com> | ||||
| 
 | ||||
| 
 | ||||
| [What is configfs?] | ||||
| 
 | ||||
| configfs is a ram-based filesystem that provides the converse of | ||||
| sysfs's functionality.  Where sysfs is a filesystem-based view of | ||||
| kernel objects, configfs is a filesystem-based manager of kernel | ||||
| objects, or config_items. | ||||
| 
 | ||||
| With sysfs, an object is created in kernel (for example, when a device | ||||
| is discovered) and it is registered with sysfs.  Its attributes then | ||||
| appear in sysfs, allowing userspace to read the attributes via | ||||
| readdir(3)/read(2).  It may allow some attributes to be modified via | ||||
| write(2).  The important point is that the object is created and | ||||
| destroyed in kernel, the kernel controls the lifecycle of the sysfs | ||||
| representation, and sysfs is merely a window on all this. | ||||
| 
 | ||||
| A configfs config_item is created via an explicit userspace operation: | ||||
| mkdir(2).  It is destroyed via rmdir(2).  The attributes appear at | ||||
| mkdir(2) time, and can be read or modified via read(2) and write(2). | ||||
| As with sysfs, readdir(3) queries the list of items and/or attributes. | ||||
| symlink(2) can be used to group items together.  Unlike sysfs, the | ||||
| lifetime of the representation is completely driven by userspace.  The | ||||
| kernel modules backing the items must respond to this. | ||||
| 
 | ||||
| Both sysfs and configfs can and should exist together on the same | ||||
| system.  One is not a replacement for the other. | ||||
| 
 | ||||
| [Using configfs] | ||||
| 
 | ||||
| configfs can be compiled as a module or into the kernel.  You can access | ||||
| it by doing | ||||
| 
 | ||||
| 	mount -t configfs none /config | ||||
| 
 | ||||
| The configfs tree will be empty unless client modules are also loaded. | ||||
| These are modules that register their item types with configfs as | ||||
| subsystems.  Once a client subsystem is loaded, it will appear as a | ||||
| subdirectory (or more than one) under /config.  Like sysfs, the | ||||
| configfs tree is always there, whether mounted on /config or not. | ||||
| 
 | ||||
| An item is created via mkdir(2).  The item's attributes will also | ||||
| appear at this time.  readdir(3) can determine what the attributes are, | ||||
| read(2) can query their default values, and write(2) can store new | ||||
| values.  Like sysfs, attributes should be ASCII text files, preferably | ||||
| with only one value per file.  The same efficiency caveats from sysfs | ||||
| apply.  Don't mix more than one attribute in one attribute file. | ||||
| 
 | ||||
| Like sysfs, configfs expects write(2) to store the entire buffer at | ||||
| once.  When writing to configfs attributes, userspace processes should | ||||
| first read the entire file, modify the portions they wish to change, and | ||||
| then write the entire buffer back.  Attribute files have a maximum size | ||||
| of one page (PAGE_SIZE, 4096 on i386). | ||||
| 
 | ||||
| When an item needs to be destroyed, remove it with rmdir(2).  An | ||||
| item cannot be destroyed if any other item has a link to it (via | ||||
| symlink(2)).  Links can be removed via unlink(2). | ||||
| 
 | ||||
| [Configuring FakeNBD: an Example] | ||||
| 
 | ||||
| Imagine there's a Network Block Device (NBD) driver that allows you to | ||||
| access remote block devices.  Call it FakeNBD.  FakeNBD uses configfs | ||||
| for its configuration.  Obviously, there will be a nice program that | ||||
| sysadmins use to configure FakeNBD, but somehow that program has to tell | ||||
| the driver about it.  Here's where configfs comes in. | ||||
| 
 | ||||
| When the FakeNBD driver is loaded, it registers itself with configfs. | ||||
| readdir(3) sees this just fine: | ||||
| 
 | ||||
| 	# ls /config | ||||
| 	fakenbd | ||||
| 
 | ||||
| A fakenbd connection can be created with mkdir(2).  The name is | ||||
| arbitrary, but likely the tool will make some use of the name.  Perhaps | ||||
| it is a uuid or a disk name: | ||||
| 
 | ||||
| 	# mkdir /config/fakenbd/disk1 | ||||
| 	# ls /config/fakenbd/disk1 | ||||
| 	target device rw | ||||
| 
 | ||||
| The target attribute contains the IP address of the server FakeNBD will | ||||
| connect to.  The device attribute is the device on the server. | ||||
| Predictably, the rw attribute determines whether the connection is | ||||
| read-only or read-write. | ||||
| 
 | ||||
| 	# echo 10.0.0.1 > /config/fakenbd/disk1/target | ||||
| 	# echo /dev/sda1 > /config/fakenbd/disk1/device | ||||
| 	# echo 1 > /config/fakenbd/disk1/rw | ||||
| 
 | ||||
| That's it.  That's all there is.  Now the device is configured, via the | ||||
| shell no less. | ||||
| 
 | ||||
| [Coding With configfs] | ||||
| 
 | ||||
| Every object in configfs is a config_item.  A config_item reflects an | ||||
| object in the subsystem.  It has attributes that match values on that | ||||
| object.  configfs handles the filesystem representation of that object | ||||
| and its attributes, allowing the subsystem to ignore all but the | ||||
| basic show/store interaction. | ||||
| 
 | ||||
| Items are created and destroyed inside a config_group.  A group is a | ||||
| collection of items that share the same attributes and operations. | ||||
| Items are created by mkdir(2) and removed by rmdir(2), but configfs | ||||
| handles that.  The group has a set of operations to perform these tasks | ||||
| 
 | ||||
| A subsystem is the top level of a client module.  During initialization, | ||||
| the client module registers the subsystem with configfs, the subsystem | ||||
| appears as a directory at the top of the configfs filesystem.  A | ||||
| subsystem is also a config_group, and can do everything a config_group | ||||
| can. | ||||
| 
 | ||||
| [struct config_item] | ||||
| 
 | ||||
| 	struct config_item { | ||||
| 		char                    *ci_name; | ||||
| 		char                    ci_namebuf[UOBJ_NAME_LEN]; | ||||
| 		struct kref             ci_kref; | ||||
| 		struct list_head        ci_entry; | ||||
| 		struct config_item      *ci_parent; | ||||
| 		struct config_group     *ci_group; | ||||
| 		struct config_item_type *ci_type; | ||||
| 		struct dentry           *ci_dentry; | ||||
| 	}; | ||||
| 
 | ||||
| 	void config_item_init(struct config_item *); | ||||
| 	void config_item_init_type_name(struct config_item *, | ||||
| 					const char *name, | ||||
| 					struct config_item_type *type); | ||||
| 	struct config_item *config_item_get(struct config_item *); | ||||
| 	void config_item_put(struct config_item *); | ||||
| 
 | ||||
| Generally, struct config_item is embedded in a container structure, a | ||||
| structure that actually represents what the subsystem is doing.  The | ||||
| config_item portion of that structure is how the object interacts with | ||||
| configfs. | ||||
| 
 | ||||
| Whether statically defined in a source file or created by a parent | ||||
| config_group, a config_item must have one of the _init() functions | ||||
| called on it.  This initializes the reference count and sets up the | ||||
| appropriate fields. | ||||
| 
 | ||||
| All users of a config_item should have a reference on it via | ||||
| config_item_get(), and drop the reference when they are done via | ||||
| config_item_put(). | ||||
| 
 | ||||
| By itself, a config_item cannot do much more than appear in configfs. | ||||
| Usually a subsystem wants the item to display and/or store attributes, | ||||
| among other things.  For that, it needs a type. | ||||
| 
 | ||||
| [struct config_item_type] | ||||
| 
 | ||||
| 	struct configfs_item_operations { | ||||
| 		void (*release)(struct config_item *); | ||||
| 		ssize_t (*show_attribute)(struct config_item *, | ||||
| 					  struct configfs_attribute *, | ||||
| 					  char *); | ||||
| 		ssize_t (*store_attribute)(struct config_item *, | ||||
| 					   struct configfs_attribute *, | ||||
| 					   const char *, size_t); | ||||
| 		int (*allow_link)(struct config_item *src, | ||||
| 				  struct config_item *target); | ||||
| 		int (*drop_link)(struct config_item *src, | ||||
| 				 struct config_item *target); | ||||
| 	}; | ||||
| 
 | ||||
| 	struct config_item_type { | ||||
| 		struct module                           *ct_owner; | ||||
| 		struct configfs_item_operations         *ct_item_ops; | ||||
| 		struct configfs_group_operations        *ct_group_ops; | ||||
| 		struct configfs_attribute               **ct_attrs; | ||||
| 	}; | ||||
| 
 | ||||
| The most basic function of a config_item_type is to define what | ||||
| operations can be performed on a config_item.  All items that have been | ||||
| allocated dynamically will need to provide the ct_item_ops->release() | ||||
| method.  This method is called when the config_item's reference count | ||||
| reaches zero.  Items that wish to display an attribute need to provide | ||||
| the ct_item_ops->show_attribute() method.  Similarly, storing a new | ||||
| attribute value uses the store_attribute() method. | ||||
| 
 | ||||
| [struct configfs_attribute] | ||||
| 
 | ||||
| 	struct configfs_attribute { | ||||
| 		char                    *ca_name; | ||||
| 		struct module           *ca_owner; | ||||
| 		mode_t                  ca_mode; | ||||
| 	}; | ||||
| 
 | ||||
| When a config_item wants an attribute to appear as a file in the item's | ||||
| configfs directory, it must define a configfs_attribute describing it. | ||||
| It then adds the attribute to the NULL-terminated array | ||||
| config_item_type->ct_attrs.  When the item appears in configfs, the | ||||
| attribute file will appear with the configfs_attribute->ca_name | ||||
| filename.  configfs_attribute->ca_mode specifies the file permissions. | ||||
| 
 | ||||
| If an attribute is readable and the config_item provides a | ||||
| ct_item_ops->show_attribute() method, that method will be called | ||||
| whenever userspace asks for a read(2) on the attribute.  The converse | ||||
| will happen for write(2). | ||||
| 
 | ||||
| [struct config_group] | ||||
| 
 | ||||
| A config_item cannot live in a vaccum.  The only way one can be created | ||||
| is via mkdir(2) on a config_group.  This will trigger creation of a | ||||
| child item. | ||||
| 
 | ||||
| 	struct config_group { | ||||
| 		struct config_item		cg_item; | ||||
| 		struct list_head		cg_children; | ||||
| 		struct configfs_subsystem 	*cg_subsys; | ||||
| 		struct config_group		**default_groups; | ||||
| 	}; | ||||
| 
 | ||||
| 	void config_group_init(struct config_group *group); | ||||
| 	void config_group_init_type_name(struct config_group *group, | ||||
| 					 const char *name, | ||||
| 					 struct config_item_type *type); | ||||
| 
 | ||||
| 
 | ||||
| The config_group structure contains a config_item.  Properly configuring | ||||
| that item means that a group can behave as an item in its own right. | ||||
| However, it can do more: it can create child items or groups.  This is | ||||
| accomplished via the group operations specified on the group's | ||||
| config_item_type. | ||||
| 
 | ||||
| 	struct configfs_group_operations { | ||||
| 		struct config_item *(*make_item)(struct config_group *group, | ||||
| 						 const char *name); | ||||
| 		struct config_group *(*make_group)(struct config_group *group, | ||||
| 						   const char *name); | ||||
| 		int (*commit_item)(struct config_item *item); | ||||
| 		void (*drop_item)(struct config_group *group, | ||||
| 				  struct config_item *item); | ||||
| 	}; | ||||
| 
 | ||||
| A group creates child items by providing the | ||||
| 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 | ||||
| config_item (or more likely, its container structure), initializes it, | ||||
| and returns it to configfs.  Configfs will then populate the filesystem | ||||
| tree to reflect the new item. | ||||
| 
 | ||||
| If the subsystem wants the child to be a group itself, the subsystem | ||||
| provides ct_group_ops->make_group().  Everything else behaves the same, | ||||
| using the group _init() functions on the group. | ||||
| 
 | ||||
| Finally, when userspace calls rmdir(2) on the item or group, | ||||
| ct_group_ops->drop_item() is called.  As a config_group is also a | ||||
| config_item, it is not necessary for a seperate drop_group() method. | ||||
| The subsystem must config_item_put() the reference that was initialized | ||||
| upon item allocation.  If a subsystem has no work to do, it may omit | ||||
| the ct_group_ops->drop_item() method, and configfs will call | ||||
| config_item_put() on the item on behalf of the subsystem. | ||||
| 
 | ||||
| IMPORTANT: drop_item() is void, and as such cannot fail.  When rmdir(2) | ||||
| is called, configfs WILL remove the item from the filesystem tree | ||||
| (assuming that it has no children to keep it busy).  The subsystem is | ||||
| responsible for responding to this.  If the subsystem has references to | ||||
| the item in other threads, the memory is safe.  It may take some time | ||||
| for the item to actually disappear from the subsystem's usage.  But it | ||||
| is gone from configfs. | ||||
| 
 | ||||
| A config_group cannot be removed while it still has child items.  This | ||||
| is implemented in the configfs rmdir(2) code.  ->drop_item() will not be | ||||
| called, as the item has not been dropped.  rmdir(2) will fail, as the | ||||
| directory is not empty. | ||||
| 
 | ||||
| [struct configfs_subsystem] | ||||
| 
 | ||||
| A subsystem must register itself, ususally at module_init time.  This | ||||
| tells configfs to make the subsystem appear in the file tree. | ||||
| 
 | ||||
| 	struct configfs_subsystem { | ||||
| 		struct config_group	su_group; | ||||
| 		struct semaphore	su_sem; | ||||
| 	}; | ||||
| 
 | ||||
| 	int configfs_register_subsystem(struct configfs_subsystem *subsys); | ||||
| 	void configfs_unregister_subsystem(struct configfs_subsystem *subsys); | ||||
| 
 | ||||
| 	A subsystem consists of a toplevel config_group and a semaphore. | ||||
| The group is where child config_items are created.  For a subsystem, | ||||
| this group is usually defined statically.  Before calling | ||||
| configfs_register_subsystem(), the subsystem must have initialized the | ||||
| group via the usual group _init() functions, and it must also have | ||||
| initialized the semaphore. | ||||
| 	When the register call returns, the subsystem is live, and it | ||||
| will be visible via configfs.  At that point, mkdir(2) can be called and | ||||
| the subsystem must be ready for it. | ||||
| 
 | ||||
| [An Example] | ||||
| 
 | ||||
| The best example of these basic concepts is the simple_children | ||||
| subsystem/group and the simple_child item in configfs_example.c  It | ||||
| shows a trivial object displaying and storing an attribute, and a simple | ||||
| group creating and destroying these children. | ||||
| 
 | ||||
| [Hierarchy Navigation and the Subsystem Semaphore] | ||||
| 
 | ||||
| There is an extra bonus that configfs provides.  The config_groups and | ||||
| config_items are arranged in a hierarchy due to the fact that they | ||||
| appear in a filesystem.  A subsystem is NEVER to touch the filesystem | ||||
| parts, but the subsystem might be interested in this hierarchy.  For | ||||
| this reason, the hierarchy is mirrored via the config_group->cg_children | ||||
| and config_item->ci_parent structure members. | ||||
| 
 | ||||
| A subsystem can navigate the cg_children list and the ci_parent pointer | ||||
| to see the tree created by the subsystem.  This can race with configfs' | ||||
| management of the hierarchy, so configfs uses the subsystem semaphore to | ||||
| protect modifications.  Whenever a subsystem wants to navigate the | ||||
| hierarchy, it must do so under the protection of the subsystem | ||||
| semaphore. | ||||
| 
 | ||||
| A subsystem will be prevented from acquiring the semaphore while a newly | ||||
| allocated item has not been linked into this hierarchy.   Similarly, it | ||||
| will not be able to acquire the semaphore while a dropping item has not | ||||
| yet been unlinked.  This means that an item's ci_parent pointer will | ||||
| never be NULL while the item is in configfs, and that an item will only | ||||
| be in its parent's cg_children list for the same duration.  This allows | ||||
| a subsystem to trust ci_parent and cg_children while they hold the | ||||
| semaphore. | ||||
| 
 | ||||
| [Item Aggregation Via symlink(2)] | ||||
| 
 | ||||
| configfs provides a simple group via the group->item parent/child | ||||
| relationship.  Often, however, a larger environment requires aggregation | ||||
| outside of the parent/child connection.  This is implemented via | ||||
| symlink(2). | ||||
| 
 | ||||
| A config_item may provide the ct_item_ops->allow_link() and | ||||
| ct_item_ops->drop_link() methods.  If the ->allow_link() method exists, | ||||
| symlink(2) may be called with the config_item as the source of the link. | ||||
| These links are only allowed between configfs config_items.  Any | ||||
| symlink(2) attempt outside the configfs filesystem will be denied. | ||||
| 
 | ||||
| When symlink(2) is called, the source config_item's ->allow_link() | ||||
| method is called with itself and a target item.  If the source item | ||||
| allows linking to target item, it returns 0.  A source item may wish to | ||||
| reject a link if it only wants links to a certain type of object (say, | ||||
| in its own subsystem). | ||||
| 
 | ||||
| When unlink(2) is called on the symbolic link, the source item is | ||||
| notified via the ->drop_link() method.  Like the ->drop_item() method, | ||||
| this is a void function and cannot return failure.  The subsystem is | ||||
| responsible for responding to the change. | ||||
| 
 | ||||
| A config_item cannot be removed while it links to any other item, nor | ||||
| can it be removed while an item links to it.  Dangling symlinks are not | ||||
| allowed in configfs. | ||||
| 
 | ||||
| [Automatically Created Subgroups] | ||||
| 
 | ||||
| A new config_group may want to have two types of child config_items. | ||||
| While this could be codified by magic names in ->make_item(), it is much | ||||
| more explicit to have a method whereby userspace sees this divergence. | ||||
| 
 | ||||
| Rather than have a group where some items behave differently than | ||||
| others, configfs provides a method whereby one or many subgroups are | ||||
| automatically created inside the parent at its creation.  Thus, | ||||
| mkdir("parent) results in "parent", "parent/subgroup1", up through | ||||
| "parent/subgroupN".  Items of type 1 can now be created in | ||||
| "parent/subgroup1", and items of type N can be created in | ||||
| "parent/subgroupN". | ||||
| 
 | ||||
| These automatic subgroups, or default groups, do not preclude other | ||||
| children of the parent group.  If ct_group_ops->make_group() exists, | ||||
| other child groups can be created on the parent group directly. | ||||
| 
 | ||||
| A configfs subsystem specifies default groups by filling in the | ||||
| NULL-terminated array default_groups on the config_group structure. | ||||
| Each group in that array is populated in the configfs tree at the same | ||||
| time as the parent group.  Similarly, they are removed at the same time | ||||
| as the parent.  No extra notification is provided.  When a ->drop_item() | ||||
| method call notifies the subsystem the parent group is going away, it | ||||
| also means every default group child associated with that parent group. | ||||
| 
 | ||||
| As a consequence of this, default_groups cannot be removed directly via | ||||
| rmdir(2).  They also are not considered when rmdir(2) on the parent | ||||
| group is checking for children. | ||||
| 
 | ||||
| [Committable Items] | ||||
| 
 | ||||
| NOTE: Committable items are currently unimplemented. | ||||
| 
 | ||||
| Some config_items cannot have a valid initial state.  That is, no | ||||
| default values can be specified for the item's attributes such that the | ||||
| item can do its work.  Userspace must configure one or more attributes, | ||||
| after which the subsystem can start whatever entity this item | ||||
| represents. | ||||
| 
 | ||||
| Consider the FakeNBD device from above.  Without a target address *and* | ||||
| a target device, the subsystem has no idea what block device to import. | ||||
| The simple example assumes that the subsystem merely waits until all the | ||||
| appropriate attributes are configured, and then connects.  This will, | ||||
| indeed, work, but now every attribute store must check if the attributes | ||||
| are initialized.  Every attribute store must fire off the connection if | ||||
| that condition is met. | ||||
| 
 | ||||
| Far better would be an explicit action notifying the subsystem that the | ||||
| config_item is ready to go.  More importantly, an explicit action allows | ||||
| the subsystem to provide feedback as to whether the attibutes are | ||||
| initialized in a way that makes sense.  configfs provides this as | ||||
| committable items. | ||||
| 
 | ||||
| configfs still uses only normal filesystem operations.  An item is | ||||
| committed via rename(2).  The item is moved from a directory where it | ||||
| can be modified to a directory where it cannot. | ||||
| 
 | ||||
| Any group that provides the ct_group_ops->commit_item() method has | ||||
| committable items.  When this group appears in configfs, mkdir(2) will | ||||
| not work directly in the group.  Instead, the group will have two | ||||
| subdirectories: "live" and "pending".  The "live" directory does not | ||||
| support mkdir(2) or rmdir(2) either.  It only allows rename(2).  The | ||||
| "pending" directory does allow mkdir(2) and rmdir(2).  An item is | ||||
| created in the "pending" directory.  Its attributes can be modified at | ||||
| will.  Userspace commits the item by renaming it into the "live" | ||||
| directory.  At this point, the subsystem recieves the ->commit_item() | ||||
| callback.  If all required attributes are filled to satisfaction, the | ||||
| method returns zero and the item is moved to the "live" directory. | ||||
| 
 | ||||
| As rmdir(2) does not work in the "live" directory, an item must be | ||||
| shutdown, or "uncommitted".  Again, this is done via rename(2), this | ||||
| time from the "live" directory back to the "pending" one.  The subsystem | ||||
| is notified by the ct_group_ops->uncommit_object() method. | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										474
									
								
								Documentation/filesystems/configfs/configfs_example.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								Documentation/filesystems/configfs/configfs_example.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,474 @@ | |||
| /*
 | ||||
|  * vim: noexpandtab ts=8 sts=0 sw=8: | ||||
|  * | ||||
|  * configfs_example.c - This file is a demonstration module containing | ||||
|  *      a number of configfs subsystems. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on sysfs: | ||||
|  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #include <linux/configfs.h> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * 01-childless | ||||
|  * | ||||
|  * This first example is a childless subsystem.  It cannot create | ||||
|  * any config_items.  It just has attributes. | ||||
|  * | ||||
|  * Note that we are enclosing the configfs_subsystem inside a container. | ||||
|  * This is not necessary if a subsystem has no attributes directly | ||||
|  * on the subsystem.  See the next example, 02-simple-children, for | ||||
|  * such a subsystem. | ||||
|  */ | ||||
| 
 | ||||
| struct childless { | ||||
| 	struct configfs_subsystem subsys; | ||||
| 	int showme; | ||||
| 	int storeme; | ||||
| }; | ||||
| 
 | ||||
| struct childless_attribute { | ||||
| 	struct configfs_attribute attr; | ||||
| 	ssize_t (*show)(struct childless *, char *); | ||||
| 	ssize_t (*store)(struct childless *, const char *, size_t); | ||||
| }; | ||||
| 
 | ||||
| static inline struct childless *to_childless(struct config_item *item) | ||||
| { | ||||
| 	return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct childless, subsys) : NULL; | ||||
| } | ||||
| 
 | ||||
| static ssize_t childless_showme_read(struct childless *childless, | ||||
| 				     char *page) | ||||
| { | ||||
| 	ssize_t pos; | ||||
| 
 | ||||
| 	pos = sprintf(page, "%d\n", childless->showme); | ||||
| 	childless->showme++; | ||||
| 
 | ||||
| 	return pos; | ||||
| } | ||||
| 
 | ||||
| static ssize_t childless_storeme_read(struct childless *childless, | ||||
| 				      char *page) | ||||
| { | ||||
| 	return sprintf(page, "%d\n", childless->storeme); | ||||
| } | ||||
| 
 | ||||
| static ssize_t childless_storeme_write(struct childless *childless, | ||||
| 				       const char *page, | ||||
| 				       size_t count) | ||||
| { | ||||
| 	unsigned long tmp; | ||||
| 	char *p = (char *) page; | ||||
| 
 | ||||
| 	tmp = simple_strtoul(p, &p, 10); | ||||
| 	if (!p || (*p && (*p != '\n'))) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (tmp > INT_MAX) | ||||
| 		return -ERANGE; | ||||
| 
 | ||||
| 	childless->storeme = tmp; | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t childless_description_read(struct childless *childless, | ||||
| 					  char *page) | ||||
| { | ||||
| 	return sprintf(page, | ||||
| "[01-childless]\n" | ||||
| "\n" | ||||
| "The childless subsystem is the simplest possible subsystem in\n" | ||||
| "configfs.  It does not support the creation of child config_items.\n" | ||||
| "It only has a few attributes.  In fact, it isn't much different\n" | ||||
| "than a directory in /proc.\n"); | ||||
| } | ||||
| 
 | ||||
| static struct childless_attribute childless_attr_showme = { | ||||
| 	.attr	= { .ca_owner = THIS_MODULE, .ca_name = "showme", .ca_mode = S_IRUGO }, | ||||
| 	.show	= childless_showme_read, | ||||
| }; | ||||
| static struct childless_attribute childless_attr_storeme = { | ||||
| 	.attr	= { .ca_owner = THIS_MODULE, .ca_name = "storeme", .ca_mode = S_IRUGO | S_IWUSR }, | ||||
| 	.show	= childless_storeme_read, | ||||
| 	.store	= childless_storeme_write, | ||||
| }; | ||||
| static struct childless_attribute childless_attr_description = { | ||||
| 	.attr = { .ca_owner = THIS_MODULE, .ca_name = "description", .ca_mode = S_IRUGO }, | ||||
| 	.show = childless_description_read, | ||||
| }; | ||||
| 
 | ||||
| static struct configfs_attribute *childless_attrs[] = { | ||||
| 	&childless_attr_showme.attr, | ||||
| 	&childless_attr_storeme.attr, | ||||
| 	&childless_attr_description.attr, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| static ssize_t childless_attr_show(struct config_item *item, | ||||
| 				   struct configfs_attribute *attr, | ||||
| 				   char *page) | ||||
| { | ||||
| 	struct childless *childless = to_childless(item); | ||||
| 	struct childless_attribute *childless_attr = | ||||
| 		container_of(attr, struct childless_attribute, attr); | ||||
| 	ssize_t ret = 0; | ||||
| 
 | ||||
| 	if (childless_attr->show) | ||||
| 		ret = childless_attr->show(childless, page); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t childless_attr_store(struct config_item *item, | ||||
| 				    struct configfs_attribute *attr, | ||||
| 				    const char *page, size_t count) | ||||
| { | ||||
| 	struct childless *childless = to_childless(item); | ||||
| 	struct childless_attribute *childless_attr = | ||||
| 		container_of(attr, struct childless_attribute, attr); | ||||
| 	ssize_t ret = -EINVAL; | ||||
| 
 | ||||
| 	if (childless_attr->store) | ||||
| 		ret = childless_attr->store(childless, page, count); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct configfs_item_operations childless_item_ops = { | ||||
| 	.show_attribute		= childless_attr_show, | ||||
| 	.store_attribute	= childless_attr_store, | ||||
| }; | ||||
| 
 | ||||
| static struct config_item_type childless_type = { | ||||
| 	.ct_item_ops	= &childless_item_ops, | ||||
| 	.ct_attrs	= childless_attrs, | ||||
| 	.ct_owner	= THIS_MODULE, | ||||
| }; | ||||
| 
 | ||||
| static struct childless childless_subsys = { | ||||
| 	.subsys = { | ||||
| 		.su_group = { | ||||
| 			.cg_item = { | ||||
| 				.ci_namebuf = "01-childless", | ||||
| 				.ci_type = &childless_type, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* ----------------------------------------------------------------- */ | ||||
| 
 | ||||
| /*
 | ||||
|  * 02-simple-children | ||||
|  * | ||||
|  * This example merely has a simple one-attribute child.  Note that | ||||
|  * there is no extra attribute structure, as the child's attribute is | ||||
|  * known from the get-go.  Also, there is no container for the | ||||
|  * subsystem, as it has no attributes of its own. | ||||
|  */ | ||||
| 
 | ||||
| struct simple_child { | ||||
| 	struct config_item item; | ||||
| 	int storeme; | ||||
| }; | ||||
| 
 | ||||
| static inline struct simple_child *to_simple_child(struct config_item *item) | ||||
| { | ||||
| 	return item ? container_of(item, struct simple_child, item) : NULL; | ||||
| } | ||||
| 
 | ||||
| static struct configfs_attribute simple_child_attr_storeme = { | ||||
| 	.ca_owner = THIS_MODULE, | ||||
| 	.ca_name = "storeme", | ||||
| 	.ca_mode = S_IRUGO | S_IWUSR, | ||||
| }; | ||||
| 
 | ||||
| static struct configfs_attribute *simple_child_attrs[] = { | ||||
| 	&simple_child_attr_storeme, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| static ssize_t simple_child_attr_show(struct config_item *item, | ||||
| 				      struct configfs_attribute *attr, | ||||
| 				      char *page) | ||||
| { | ||||
| 	ssize_t count; | ||||
| 	struct simple_child *simple_child = to_simple_child(item); | ||||
| 
 | ||||
| 	count = sprintf(page, "%d\n", simple_child->storeme); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t simple_child_attr_store(struct config_item *item, | ||||
| 				       struct configfs_attribute *attr, | ||||
| 				       const char *page, size_t count) | ||||
| { | ||||
| 	struct simple_child *simple_child = to_simple_child(item); | ||||
| 	unsigned long tmp; | ||||
| 	char *p = (char *) page; | ||||
| 
 | ||||
| 	tmp = simple_strtoul(p, &p, 10); | ||||
| 	if (!p || (*p && (*p != '\n'))) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (tmp > INT_MAX) | ||||
| 		return -ERANGE; | ||||
| 
 | ||||
| 	simple_child->storeme = tmp; | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static void simple_child_release(struct config_item *item) | ||||
| { | ||||
| 	kfree(to_simple_child(item)); | ||||
| } | ||||
| 
 | ||||
| static struct configfs_item_operations simple_child_item_ops = { | ||||
| 	.release		= simple_child_release, | ||||
| 	.show_attribute		= simple_child_attr_show, | ||||
| 	.store_attribute	= simple_child_attr_store, | ||||
| }; | ||||
| 
 | ||||
| static struct config_item_type simple_child_type = { | ||||
| 	.ct_item_ops	= &simple_child_item_ops, | ||||
| 	.ct_attrs	= simple_child_attrs, | ||||
| 	.ct_owner	= THIS_MODULE, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static struct config_item *simple_children_make_item(struct config_group *group, const char *name) | ||||
| { | ||||
| 	struct simple_child *simple_child; | ||||
| 
 | ||||
| 	simple_child = kmalloc(sizeof(struct simple_child), GFP_KERNEL); | ||||
| 	if (!simple_child) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	memset(simple_child, 0, sizeof(struct simple_child)); | ||||
| 
 | ||||
| 	config_item_init_type_name(&simple_child->item, name, | ||||
| 				   &simple_child_type); | ||||
| 
 | ||||
| 	simple_child->storeme = 0; | ||||
| 
 | ||||
| 	return &simple_child->item; | ||||
| } | ||||
| 
 | ||||
| static struct configfs_attribute simple_children_attr_description = { | ||||
| 	.ca_owner = THIS_MODULE, | ||||
| 	.ca_name = "description", | ||||
| 	.ca_mode = S_IRUGO, | ||||
| }; | ||||
| 
 | ||||
| static struct configfs_attribute *simple_children_attrs[] = { | ||||
| 	&simple_children_attr_description, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| static ssize_t simple_children_attr_show(struct config_item *item, | ||||
| 			   		 struct configfs_attribute *attr, | ||||
| 			   		 char *page) | ||||
| { | ||||
| 	return sprintf(page, | ||||
| "[02-simple-children]\n" | ||||
| "\n" | ||||
| "This subsystem allows the creation of child config_items.  These\n" | ||||
| "items have only one attribute that is readable and writeable.\n"); | ||||
| } | ||||
| 
 | ||||
| static struct configfs_item_operations simple_children_item_ops = { | ||||
| 	.show_attribute	= simple_children_attr_show, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Note that, since no extra work is required on ->drop_item(), | ||||
|  * no ->drop_item() is provided. | ||||
|  */ | ||||
| static struct configfs_group_operations simple_children_group_ops = { | ||||
| 	.make_item	= simple_children_make_item, | ||||
| }; | ||||
| 
 | ||||
| static struct config_item_type simple_children_type = { | ||||
| 	.ct_item_ops	= &simple_children_item_ops, | ||||
| 	.ct_group_ops	= &simple_children_group_ops, | ||||
| 	.ct_attrs	= simple_children_attrs, | ||||
| }; | ||||
| 
 | ||||
| static struct configfs_subsystem simple_children_subsys = { | ||||
| 	.su_group = { | ||||
| 		.cg_item = { | ||||
| 			.ci_namebuf = "02-simple-children", | ||||
| 			.ci_type = &simple_children_type, | ||||
| 		}, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* ----------------------------------------------------------------- */ | ||||
| 
 | ||||
| /*
 | ||||
|  * 03-group-children | ||||
|  * | ||||
|  * This example reuses the simple_children group from above.  However, | ||||
|  * the simple_children group is not the subsystem itself, it is a | ||||
|  * child of the subsystem.  Creation of a group in the subsystem creates | ||||
|  * a new simple_children group.  That group can then have simple_child | ||||
|  * children of its own. | ||||
|  */ | ||||
| 
 | ||||
| struct simple_children { | ||||
| 	struct config_group group; | ||||
| }; | ||||
| 
 | ||||
| static struct config_group *group_children_make_group(struct config_group *group, const char *name) | ||||
| { | ||||
| 	struct simple_children *simple_children; | ||||
| 
 | ||||
| 	simple_children = kmalloc(sizeof(struct simple_children), | ||||
| 				  GFP_KERNEL); | ||||
| 	if (!simple_children) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	memset(simple_children, 0, sizeof(struct simple_children)); | ||||
| 
 | ||||
| 	config_group_init_type_name(&simple_children->group, name, | ||||
| 				    &simple_children_type); | ||||
| 
 | ||||
| 	return &simple_children->group; | ||||
| } | ||||
| 
 | ||||
| static struct configfs_attribute group_children_attr_description = { | ||||
| 	.ca_owner = THIS_MODULE, | ||||
| 	.ca_name = "description", | ||||
| 	.ca_mode = S_IRUGO, | ||||
| }; | ||||
| 
 | ||||
| static struct configfs_attribute *group_children_attrs[] = { | ||||
| 	&group_children_attr_description, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| static ssize_t group_children_attr_show(struct config_item *item, | ||||
| 			   		struct configfs_attribute *attr, | ||||
| 			   		char *page) | ||||
| { | ||||
| 	return sprintf(page, | ||||
| "[03-group-children]\n" | ||||
| "\n" | ||||
| "This subsystem allows the creation of child config_groups.  These\n" | ||||
| "groups are like the subsystem simple-children.\n"); | ||||
| } | ||||
| 
 | ||||
| static struct configfs_item_operations group_children_item_ops = { | ||||
| 	.show_attribute	= group_children_attr_show, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Note that, since no extra work is required on ->drop_item(), | ||||
|  * no ->drop_item() is provided. | ||||
|  */ | ||||
| static struct configfs_group_operations group_children_group_ops = { | ||||
| 	.make_group	= group_children_make_group, | ||||
| }; | ||||
| 
 | ||||
| static struct config_item_type group_children_type = { | ||||
| 	.ct_item_ops	= &group_children_item_ops, | ||||
| 	.ct_group_ops	= &group_children_group_ops, | ||||
| 	.ct_attrs	= group_children_attrs, | ||||
| }; | ||||
| 
 | ||||
| static struct configfs_subsystem group_children_subsys = { | ||||
| 	.su_group = { | ||||
| 		.cg_item = { | ||||
| 			.ci_namebuf = "03-group-children", | ||||
| 			.ci_type = &group_children_type, | ||||
| 		}, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| /* ----------------------------------------------------------------- */ | ||||
| 
 | ||||
| /*
 | ||||
|  * We're now done with our subsystem definitions. | ||||
|  * For convenience in this module, here's a list of them all.  It | ||||
|  * allows the init function to easily register them.  Most modules | ||||
|  * will only have one subsystem, and will only call register_subsystem | ||||
|  * on it directly. | ||||
|  */ | ||||
| static struct configfs_subsystem *example_subsys[] = { | ||||
| 	&childless_subsys.subsys, | ||||
| 	&simple_children_subsys, | ||||
| 	&group_children_subsys, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| static int __init configfs_example_init(void) | ||||
| { | ||||
| 	int ret; | ||||
| 	int i; | ||||
| 	struct configfs_subsystem *subsys; | ||||
| 
 | ||||
| 	for (i = 0; example_subsys[i]; i++) { | ||||
| 		subsys = example_subsys[i]; | ||||
| 
 | ||||
| 		config_group_init(&subsys->su_group); | ||||
| 		init_MUTEX(&subsys->su_sem); | ||||
| 		ret = configfs_register_subsystem(subsys); | ||||
| 		if (ret) { | ||||
| 			printk(KERN_ERR "Error %d while registering subsystem %s\n", | ||||
| 			       ret, | ||||
| 			       subsys->su_group.cg_item.ci_namebuf); | ||||
| 			goto out_unregister; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_unregister: | ||||
| 	for (; i >= 0; i--) { | ||||
| 		configfs_unregister_subsystem(example_subsys[i]); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void __exit configfs_example_exit(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; example_subsys[i]; i++) { | ||||
| 		configfs_unregister_subsystem(example_subsys[i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module_init(configfs_example_init); | ||||
| module_exit(configfs_example_exit); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  | @ -554,6 +554,11 @@ W:	http://us1.samba.org/samba/Linux_CIFS_client.html | |||
| T:	git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git | ||||
| S:	Supported	 | ||||
| 
 | ||||
| CONFIGFS | ||||
| P:	Joel Becker | ||||
| M:	Joel Becker <joel.becker@oracle.com> | ||||
| S:	Supported | ||||
| 
 | ||||
| CIRRUS LOGIC GENERIC FBDEV DRIVER | ||||
| P:	Jeff Garzik | ||||
| M:	jgarzik@pobox.com | ||||
|  |  | |||
							
								
								
									
										14
									
								
								fs/Kconfig
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								fs/Kconfig
									
									
									
									
									
								
							|  | @ -841,6 +841,20 @@ config RELAYFS_FS | |||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config CONFIGFS_FS | ||||
| 	tristate "Userspace-driven configuration filesystem (EXPERIMENTAL)" | ||||
| 	depends on EXPERIMENTAL | ||||
| 	help | ||||
| 	  configfs is a ram-based filesystem that provides the converse | ||||
| 	  of sysfs's functionality. Where sysfs is a filesystem-based | ||||
| 	  view of kernel objects, configfs is a filesystem-based manager | ||||
| 	  of kernel objects, or config_items. | ||||
| 
 | ||||
| 	  Both sysfs and configfs can and should exist together on the | ||||
| 	  same system. One is not a replacement for the other. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| endmenu | ||||
| 
 | ||||
| menu "Miscellaneous filesystems" | ||||
|  |  | |||
|  | @ -101,3 +101,4 @@ obj-$(CONFIG_BEFS_FS)		+= befs/ | |||
| obj-$(CONFIG_HOSTFS)		+= hostfs/ | ||||
| obj-$(CONFIG_HPPFS)		+= hppfs/ | ||||
| obj-$(CONFIG_DEBUG_FS)		+= debugfs/ | ||||
| obj-$(CONFIG_CONFIGFS_FS)	+= configfs/ | ||||
|  |  | |||
							
								
								
									
										7
									
								
								fs/configfs/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								fs/configfs/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| #
 | ||||
| # Makefile for the configfs virtual filesystem
 | ||||
| #
 | ||||
| 
 | ||||
| obj-$(CONFIG_CONFIGFS_FS)	+= configfs.o | ||||
| 
 | ||||
| configfs-objs	:= inode.o file.o dir.o symlink.o mount.o item.o | ||||
							
								
								
									
										142
									
								
								fs/configfs/configfs_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								fs/configfs/configfs_internal.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,142 @@ | |||
| /* -*- mode: c; c-basic-offset:8; -*-
 | ||||
|  * vim: noexpandtab sw=8 ts=8 sts=0: | ||||
|  * | ||||
|  * configfs_internal.h - Internal stuff for configfs | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on sysfs: | ||||
|  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/slab.h> | ||||
| #include <linux/list.h> | ||||
| 
 | ||||
| struct configfs_dirent { | ||||
| 	atomic_t		s_count; | ||||
| 	struct list_head	s_sibling; | ||||
| 	struct list_head	s_children; | ||||
| 	struct list_head	s_links; | ||||
| 	void 			* s_element; | ||||
| 	int			s_type; | ||||
| 	umode_t			s_mode; | ||||
| 	struct dentry		* s_dentry; | ||||
| }; | ||||
| 
 | ||||
| #define CONFIGFS_ROOT		0x0001 | ||||
| #define CONFIGFS_DIR		0x0002 | ||||
| #define CONFIGFS_ITEM_ATTR 	0x0004 | ||||
| #define CONFIGFS_ITEM_LINK 	0x0020 | ||||
| #define CONFIGFS_USET_DIR	0x0040 | ||||
| #define CONFIGFS_USET_DEFAULT	0x0080 | ||||
| #define CONFIGFS_USET_DROPPING	0x0100 | ||||
| #define CONFIGFS_NOT_PINNED	(CONFIGFS_ITEM_ATTR) | ||||
| 
 | ||||
| extern struct vfsmount * configfs_mount; | ||||
| 
 | ||||
| extern int configfs_is_root(struct config_item *item); | ||||
| 
 | ||||
| extern struct inode * configfs_new_inode(mode_t mode); | ||||
| extern int configfs_create(struct dentry *, int mode, int (*init)(struct inode *)); | ||||
| 
 | ||||
| extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); | ||||
| extern int configfs_make_dirent(struct configfs_dirent *, | ||||
| 				struct dentry *, void *, umode_t, int); | ||||
| 
 | ||||
| extern int configfs_add_file(struct dentry *, const struct configfs_attribute *, int); | ||||
| extern void configfs_hash_and_remove(struct dentry * dir, const char * name); | ||||
| 
 | ||||
| extern const unsigned char * configfs_get_name(struct configfs_dirent *sd); | ||||
| extern void configfs_drop_dentry(struct configfs_dirent *sd, struct dentry *parent); | ||||
| 
 | ||||
| extern int configfs_pin_fs(void); | ||||
| extern void configfs_release_fs(void); | ||||
| 
 | ||||
| extern struct rw_semaphore configfs_rename_sem; | ||||
| extern struct super_block * configfs_sb; | ||||
| extern struct file_operations configfs_dir_operations; | ||||
| extern struct file_operations configfs_file_operations; | ||||
| extern struct file_operations bin_fops; | ||||
| extern struct inode_operations configfs_dir_inode_operations; | ||||
| extern struct inode_operations configfs_symlink_inode_operations; | ||||
| 
 | ||||
| extern int configfs_symlink(struct inode *dir, struct dentry *dentry, | ||||
| 			    const char *symname); | ||||
| extern int configfs_unlink(struct inode *dir, struct dentry *dentry); | ||||
| 
 | ||||
| struct configfs_symlink { | ||||
| 	struct list_head sl_list; | ||||
| 	struct config_item *sl_target; | ||||
| }; | ||||
| 
 | ||||
| extern int configfs_create_link(struct configfs_symlink *sl, | ||||
| 				struct dentry *parent, | ||||
| 				struct dentry *dentry); | ||||
| 
 | ||||
| static inline struct config_item * to_item(struct dentry * dentry) | ||||
| { | ||||
| 	struct configfs_dirent * sd = dentry->d_fsdata; | ||||
| 	return ((struct config_item *) sd->s_element); | ||||
| } | ||||
| 
 | ||||
| static inline struct configfs_attribute * to_attr(struct dentry * dentry) | ||||
| { | ||||
| 	struct configfs_dirent * sd = dentry->d_fsdata; | ||||
| 	return ((struct configfs_attribute *) sd->s_element); | ||||
| } | ||||
| 
 | ||||
| static inline struct config_item *configfs_get_config_item(struct dentry *dentry) | ||||
| { | ||||
| 	struct config_item * item = NULL; | ||||
| 
 | ||||
| 	spin_lock(&dcache_lock); | ||||
| 	if (!d_unhashed(dentry)) { | ||||
| 		struct configfs_dirent * sd = dentry->d_fsdata; | ||||
| 		if (sd->s_type & CONFIGFS_ITEM_LINK) { | ||||
| 			struct configfs_symlink * sl = sd->s_element; | ||||
| 			item = config_item_get(sl->sl_target); | ||||
| 		} else | ||||
| 			item = config_item_get(sd->s_element); | ||||
| 	} | ||||
| 	spin_unlock(&dcache_lock); | ||||
| 
 | ||||
| 	return item; | ||||
| } | ||||
| 
 | ||||
| static inline void release_configfs_dirent(struct configfs_dirent * sd) | ||||
| { | ||||
| 	if (!(sd->s_type & CONFIGFS_ROOT)) | ||||
| 		kfree(sd); | ||||
| } | ||||
| 
 | ||||
| static inline struct configfs_dirent * configfs_get(struct configfs_dirent * sd) | ||||
| { | ||||
| 	if (sd) { | ||||
| 		WARN_ON(!atomic_read(&sd->s_count)); | ||||
| 		atomic_inc(&sd->s_count); | ||||
| 	} | ||||
| 	return sd; | ||||
| } | ||||
| 
 | ||||
| static inline void configfs_put(struct configfs_dirent * sd) | ||||
| { | ||||
| 	WARN_ON(!atomic_read(&sd->s_count)); | ||||
| 	if (atomic_dec_and_test(&sd->s_count)) | ||||
| 		release_configfs_dirent(sd); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										1102
									
								
								fs/configfs/dir.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1102
									
								
								fs/configfs/dir.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										360
									
								
								fs/configfs/file.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										360
									
								
								fs/configfs/file.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,360 @@ | |||
| /* -*- mode: c; c-basic-offset: 8; -*-
 | ||||
|  * vim: noexpandtab sw=8 ts=8 sts=0: | ||||
|  * | ||||
|  * file.c - operations for regular (text) files. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on sysfs: | ||||
|  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/fs.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/dnotify.h> | ||||
| #include <linux/slab.h> | ||||
| #include <asm/uaccess.h> | ||||
| #include <asm/semaphore.h> | ||||
| 
 | ||||
| #include <linux/configfs.h> | ||||
| #include "configfs_internal.h" | ||||
| 
 | ||||
| 
 | ||||
| struct configfs_buffer { | ||||
| 	size_t			count; | ||||
| 	loff_t			pos; | ||||
| 	char			* page; | ||||
| 	struct configfs_item_operations	* ops; | ||||
| 	struct semaphore	sem; | ||||
| 	int			needs_read_fill; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	fill_read_buffer - allocate and fill buffer from item. | ||||
|  *	@dentry:	dentry pointer. | ||||
|  *	@buffer:	data buffer for file. | ||||
|  * | ||||
|  *	Allocate @buffer->page, if it hasn't been already, then call the | ||||
|  *	config_item's show() method to fill the buffer with this attribute's | ||||
|  *	data. | ||||
|  *	This is called only once, on the file's first read. | ||||
|  */ | ||||
| static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buffer) | ||||
| { | ||||
| 	struct configfs_attribute * attr = to_attr(dentry); | ||||
| 	struct config_item * item = to_item(dentry->d_parent); | ||||
| 	struct configfs_item_operations * ops = buffer->ops; | ||||
| 	int ret = 0; | ||||
| 	ssize_t count; | ||||
| 
 | ||||
| 	if (!buffer->page) | ||||
| 		buffer->page = (char *) get_zeroed_page(GFP_KERNEL); | ||||
| 	if (!buffer->page) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	count = ops->show_attribute(item,attr,buffer->page); | ||||
| 	buffer->needs_read_fill = 0; | ||||
| 	BUG_ON(count > (ssize_t)PAGE_SIZE); | ||||
| 	if (count >= 0) | ||||
| 		buffer->count = count; | ||||
| 	else | ||||
| 		ret = count; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	flush_read_buffer - push buffer to userspace. | ||||
|  *	@buffer:	data buffer for file. | ||||
|  *	@userbuf:	user-passed buffer. | ||||
|  *	@count:		number of bytes requested. | ||||
|  *	@ppos:		file position. | ||||
|  * | ||||
|  *	Copy the buffer we filled in fill_read_buffer() to userspace. | ||||
|  *	This is done at the reader's leisure, copying and advancing | ||||
|  *	the amount they specify each time. | ||||
|  *	This may be called continuously until the buffer is empty. | ||||
|  */ | ||||
| static int flush_read_buffer(struct configfs_buffer * buffer, char __user * buf, | ||||
| 			     size_t count, loff_t * ppos) | ||||
| { | ||||
| 	int error; | ||||
| 
 | ||||
| 	if (*ppos > buffer->count) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (count > (buffer->count - *ppos)) | ||||
| 		count = buffer->count - *ppos; | ||||
| 
 | ||||
| 	error = copy_to_user(buf,buffer->page + *ppos,count); | ||||
| 	if (!error) | ||||
| 		*ppos += count; | ||||
| 	return error ? -EFAULT : count; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	configfs_read_file - read an attribute. | ||||
|  *	@file:	file pointer. | ||||
|  *	@buf:	buffer to fill. | ||||
|  *	@count:	number of bytes to read. | ||||
|  *	@ppos:	starting offset in file. | ||||
|  * | ||||
|  *	Userspace wants to read an attribute file. The attribute descriptor | ||||
|  *	is in the file's ->d_fsdata. The target item is in the directory's | ||||
|  *	->d_fsdata. | ||||
|  * | ||||
|  *	We call fill_read_buffer() to allocate and fill the buffer from the | ||||
|  *	item's show() method exactly once (if the read is happening from | ||||
|  *	the beginning of the file). That should fill the entire buffer with | ||||
|  *	all the data the item has to offer for that attribute. | ||||
|  *	We then call flush_read_buffer() to copy the buffer to userspace | ||||
|  *	in the increments specified. | ||||
|  */ | ||||
| 
 | ||||
| static ssize_t | ||||
| configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||||
| { | ||||
| 	struct configfs_buffer * buffer = file->private_data; | ||||
| 	ssize_t retval = 0; | ||||
| 
 | ||||
| 	down(&buffer->sem); | ||||
| 	if (buffer->needs_read_fill) { | ||||
| 		if ((retval = fill_read_buffer(file->f_dentry,buffer))) | ||||
| 			goto out; | ||||
| 	} | ||||
| 	pr_debug("%s: count = %d, ppos = %lld, buf = %s\n", | ||||
| 		 __FUNCTION__,count,*ppos,buffer->page); | ||||
| 	retval = flush_read_buffer(buffer,buf,count,ppos); | ||||
| out: | ||||
| 	up(&buffer->sem); | ||||
| 	return retval; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	fill_write_buffer - copy buffer from userspace. | ||||
|  *	@buffer:	data buffer for file. | ||||
|  *	@userbuf:	data from user. | ||||
|  *	@count:		number of bytes in @userbuf. | ||||
|  * | ||||
|  *	Allocate @buffer->page if it hasn't been already, then | ||||
|  *	copy the user-supplied buffer into it. | ||||
|  */ | ||||
| 
 | ||||
| static int | ||||
| fill_write_buffer(struct configfs_buffer * buffer, const char __user * buf, size_t count) | ||||
| { | ||||
| 	int error; | ||||
| 
 | ||||
| 	if (!buffer->page) | ||||
| 		buffer->page = (char *)get_zeroed_page(GFP_KERNEL); | ||||
| 	if (!buffer->page) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	if (count > PAGE_SIZE) | ||||
| 		count = PAGE_SIZE; | ||||
| 	error = copy_from_user(buffer->page,buf,count); | ||||
| 	buffer->needs_read_fill = 1; | ||||
| 	return error ? -EFAULT : count; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	flush_write_buffer - push buffer to config_item. | ||||
|  *	@file:		file pointer. | ||||
|  *	@buffer:	data buffer for file. | ||||
|  * | ||||
|  *	Get the correct pointers for the config_item and the attribute we're | ||||
|  *	dealing with, then call the store() method for the attribute, | ||||
|  *	passing the buffer that we acquired in fill_write_buffer(). | ||||
|  */ | ||||
| 
 | ||||
| static int | ||||
| flush_write_buffer(struct dentry * dentry, struct configfs_buffer * buffer, size_t count) | ||||
| { | ||||
| 	struct configfs_attribute * attr = to_attr(dentry); | ||||
| 	struct config_item * item = to_item(dentry->d_parent); | ||||
| 	struct configfs_item_operations * ops = buffer->ops; | ||||
| 
 | ||||
| 	return ops->store_attribute(item,attr,buffer->page,count); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	configfs_write_file - write an attribute. | ||||
|  *	@file:	file pointer | ||||
|  *	@buf:	data to write | ||||
|  *	@count:	number of bytes | ||||
|  *	@ppos:	starting offset | ||||
|  * | ||||
|  *	Similar to configfs_read_file(), though working in the opposite direction. | ||||
|  *	We allocate and fill the data from the user in fill_write_buffer(), | ||||
|  *	then push it to the config_item in flush_write_buffer(). | ||||
|  *	There is no easy way for us to know if userspace is only doing a partial | ||||
|  *	write, so we don't support them. We expect the entire buffer to come | ||||
|  *	on the first write. | ||||
|  *	Hint: if you're writing a value, first read the file, modify only the | ||||
|  *	the value you're changing, then write entire buffer back. | ||||
|  */ | ||||
| 
 | ||||
| static ssize_t | ||||
| configfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||||
| { | ||||
| 	struct configfs_buffer * buffer = file->private_data; | ||||
| 
 | ||||
| 	down(&buffer->sem); | ||||
| 	count = fill_write_buffer(buffer,buf,count); | ||||
| 	if (count > 0) | ||||
| 		count = flush_write_buffer(file->f_dentry,buffer,count); | ||||
| 	if (count > 0) | ||||
| 		*ppos += count; | ||||
| 	up(&buffer->sem); | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static int check_perm(struct inode * inode, struct file * file) | ||||
| { | ||||
| 	struct config_item *item = configfs_get_config_item(file->f_dentry->d_parent); | ||||
| 	struct configfs_attribute * attr = to_attr(file->f_dentry); | ||||
| 	struct configfs_buffer * buffer; | ||||
| 	struct configfs_item_operations * ops = NULL; | ||||
| 	int error = 0; | ||||
| 
 | ||||
| 	if (!item || !attr) | ||||
| 		goto Einval; | ||||
| 
 | ||||
| 	/* Grab the module reference for this attribute if we have one */ | ||||
| 	if (!try_module_get(attr->ca_owner)) { | ||||
| 		error = -ENODEV; | ||||
| 		goto Done; | ||||
| 	} | ||||
| 
 | ||||
| 	if (item->ci_type) | ||||
| 		ops = item->ci_type->ct_item_ops; | ||||
| 	else | ||||
| 		goto Eaccess; | ||||
| 
 | ||||
| 	/* File needs write support.
 | ||||
| 	 * The inode's perms must say it's ok, | ||||
| 	 * and we must have a store method. | ||||
| 	 */ | ||||
| 	if (file->f_mode & FMODE_WRITE) { | ||||
| 
 | ||||
| 		if (!(inode->i_mode & S_IWUGO) || !ops->store_attribute) | ||||
| 			goto Eaccess; | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	/* File needs read support.
 | ||||
| 	 * The inode's perms must say it's ok, and we there | ||||
| 	 * must be a show method for it. | ||||
| 	 */ | ||||
| 	if (file->f_mode & FMODE_READ) { | ||||
| 		if (!(inode->i_mode & S_IRUGO) || !ops->show_attribute) | ||||
| 			goto Eaccess; | ||||
| 	} | ||||
| 
 | ||||
| 	/* No error? Great, allocate a buffer for the file, and store it
 | ||||
| 	 * it in file->private_data for easy access. | ||||
| 	 */ | ||||
| 	buffer = kmalloc(sizeof(struct configfs_buffer),GFP_KERNEL); | ||||
| 	if (buffer) { | ||||
| 		memset(buffer,0,sizeof(struct configfs_buffer)); | ||||
| 		init_MUTEX(&buffer->sem); | ||||
| 		buffer->needs_read_fill = 1; | ||||
| 		buffer->ops = ops; | ||||
| 		file->private_data = buffer; | ||||
| 	} else | ||||
| 		error = -ENOMEM; | ||||
| 	goto Done; | ||||
| 
 | ||||
|  Einval: | ||||
| 	error = -EINVAL; | ||||
| 	goto Done; | ||||
|  Eaccess: | ||||
| 	error = -EACCES; | ||||
| 	module_put(attr->ca_owner); | ||||
|  Done: | ||||
| 	if (error && item) | ||||
| 		config_item_put(item); | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| static int configfs_open_file(struct inode * inode, struct file * filp) | ||||
| { | ||||
| 	return check_perm(inode,filp); | ||||
| } | ||||
| 
 | ||||
| static int configfs_release(struct inode * inode, struct file * filp) | ||||
| { | ||||
| 	struct config_item * item = to_item(filp->f_dentry->d_parent); | ||||
| 	struct configfs_attribute * attr = to_attr(filp->f_dentry); | ||||
| 	struct module * owner = attr->ca_owner; | ||||
| 	struct configfs_buffer * buffer = filp->private_data; | ||||
| 
 | ||||
| 	if (item) | ||||
| 		config_item_put(item); | ||||
| 	/* After this point, attr should not be accessed. */ | ||||
| 	module_put(owner); | ||||
| 
 | ||||
| 	if (buffer) { | ||||
| 		if (buffer->page) | ||||
| 			free_page((unsigned long)buffer->page); | ||||
| 		kfree(buffer); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct file_operations configfs_file_operations = { | ||||
| 	.read		= configfs_read_file, | ||||
| 	.write		= configfs_write_file, | ||||
| 	.llseek		= generic_file_llseek, | ||||
| 	.open		= configfs_open_file, | ||||
| 	.release	= configfs_release, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| int configfs_add_file(struct dentry * dir, const struct configfs_attribute * attr, int type) | ||||
| { | ||||
| 	struct configfs_dirent * parent_sd = dir->d_fsdata; | ||||
| 	umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG; | ||||
| 	int error = 0; | ||||
| 
 | ||||
| 	down(&dir->d_inode->i_sem); | ||||
| 	error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode, type); | ||||
| 	up(&dir->d_inode->i_sem); | ||||
| 
 | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	configfs_create_file - create an attribute file for an item. | ||||
|  *	@item:	item we're creating for. | ||||
|  *	@attr:	atrribute descriptor. | ||||
|  */ | ||||
| 
 | ||||
| int configfs_create_file(struct config_item * item, const struct configfs_attribute * attr) | ||||
| { | ||||
| 	BUG_ON(!item || !item->ci_dentry || !attr); | ||||
| 
 | ||||
| 	return configfs_add_file(item->ci_dentry, attr, | ||||
| 				 CONFIGFS_ITEM_ATTR); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										162
									
								
								fs/configfs/inode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								fs/configfs/inode.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| /* -*- mode: c; c-basic-offset: 8; -*-
 | ||||
|  * vim: noexpandtab sw=8 ts=8 sts=0: | ||||
|  * | ||||
|  * inode.c - basic inode and dentry operations. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on sysfs: | ||||
|  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  * | ||||
|  * Please see Documentation/filesystems/configfs.txt for more information. | ||||
|  */ | ||||
| 
 | ||||
| #undef DEBUG | ||||
| 
 | ||||
| #include <linux/pagemap.h> | ||||
| #include <linux/namei.h> | ||||
| #include <linux/backing-dev.h> | ||||
| 
 | ||||
| #include <linux/configfs.h> | ||||
| #include "configfs_internal.h" | ||||
| 
 | ||||
| extern struct super_block * configfs_sb; | ||||
| 
 | ||||
| static struct address_space_operations configfs_aops = { | ||||
| 	.readpage	= simple_readpage, | ||||
| 	.prepare_write	= simple_prepare_write, | ||||
| 	.commit_write	= simple_commit_write | ||||
| }; | ||||
| 
 | ||||
| static struct backing_dev_info configfs_backing_dev_info = { | ||||
| 	.ra_pages	= 0,	/* No readahead */ | ||||
| 	.capabilities	= BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | ||||
| }; | ||||
| 
 | ||||
| struct inode * configfs_new_inode(mode_t mode) | ||||
| { | ||||
| 	struct inode * inode = new_inode(configfs_sb); | ||||
| 	if (inode) { | ||||
| 		inode->i_mode = mode; | ||||
| 		inode->i_uid = 0; | ||||
| 		inode->i_gid = 0; | ||||
| 		inode->i_blksize = PAGE_CACHE_SIZE; | ||||
| 		inode->i_blocks = 0; | ||||
| 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||||
| 		inode->i_mapping->a_ops = &configfs_aops; | ||||
| 		inode->i_mapping->backing_dev_info = &configfs_backing_dev_info; | ||||
| 	} | ||||
| 	return inode; | ||||
| } | ||||
| 
 | ||||
| int configfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) | ||||
| { | ||||
| 	int error = 0; | ||||
| 	struct inode * inode = NULL; | ||||
| 	if (dentry) { | ||||
| 		if (!dentry->d_inode) { | ||||
| 			if ((inode = configfs_new_inode(mode))) { | ||||
| 				if (dentry->d_parent && dentry->d_parent->d_inode) { | ||||
| 					struct inode *p_inode = dentry->d_parent->d_inode; | ||||
| 					p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; | ||||
| 				} | ||||
| 				goto Proceed; | ||||
| 			} | ||||
| 			else | ||||
| 				error = -ENOMEM; | ||||
| 		} else | ||||
| 			error = -EEXIST; | ||||
| 	} else | ||||
| 		error = -ENOENT; | ||||
| 	goto Done; | ||||
| 
 | ||||
|  Proceed: | ||||
| 	if (init) | ||||
| 		error = init(inode); | ||||
| 	if (!error) { | ||||
| 		d_instantiate(dentry, inode); | ||||
| 		if (S_ISDIR(mode) || S_ISLNK(mode)) | ||||
| 			dget(dentry);  /* pin link and directory dentries in core */ | ||||
| 	} else | ||||
| 		iput(inode); | ||||
|  Done: | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the name for corresponding element represented by the given configfs_dirent | ||||
|  */ | ||||
| const unsigned char * configfs_get_name(struct configfs_dirent *sd) | ||||
| { | ||||
| 	struct attribute * attr; | ||||
| 
 | ||||
| 	if (!sd || !sd->s_element) | ||||
| 		BUG(); | ||||
| 
 | ||||
| 	/* These always have a dentry, so use that */ | ||||
| 	if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK)) | ||||
| 		return sd->s_dentry->d_name.name; | ||||
| 
 | ||||
| 	if (sd->s_type & CONFIGFS_ITEM_ATTR) { | ||||
| 		attr = sd->s_element; | ||||
| 		return attr->name; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Unhashes the dentry corresponding to given configfs_dirent | ||||
|  * Called with parent inode's i_sem held. | ||||
|  */ | ||||
| void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent) | ||||
| { | ||||
| 	struct dentry * dentry = sd->s_dentry; | ||||
| 
 | ||||
| 	if (dentry) { | ||||
| 		spin_lock(&dcache_lock); | ||||
| 		if (!(d_unhashed(dentry) && dentry->d_inode)) { | ||||
| 			dget_locked(dentry); | ||||
| 			__d_drop(dentry); | ||||
| 			spin_unlock(&dcache_lock); | ||||
| 			simple_unlink(parent->d_inode, dentry); | ||||
| 		} else | ||||
| 			spin_unlock(&dcache_lock); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void configfs_hash_and_remove(struct dentry * dir, const char * name) | ||||
| { | ||||
| 	struct configfs_dirent * sd; | ||||
| 	struct configfs_dirent * parent_sd = dir->d_fsdata; | ||||
| 
 | ||||
| 	down(&dir->d_inode->i_sem); | ||||
| 	list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | ||||
| 		if (!sd->s_element) | ||||
| 			continue; | ||||
| 		if (!strcmp(configfs_get_name(sd), name)) { | ||||
| 			list_del_init(&sd->s_sibling); | ||||
| 			configfs_drop_dentry(sd, dir); | ||||
| 			configfs_put(sd); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	up(&dir->d_inode->i_sem); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										227
									
								
								fs/configfs/item.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								fs/configfs/item.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,227 @@ | |||
| /* -*- mode: c; c-basic-offset: 8; -*-
 | ||||
|  * vim: noexpandtab sw=8 ts=8 sts=0: | ||||
|  * | ||||
|  * item.c - library routines for handling generic config items | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on kobject: | ||||
|  * 	kobject is Copyright (c) 2002-2003 Patrick Mochel | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  * | ||||
|  * Please see the file Documentation/filesystems/configfs.txt for | ||||
|  * critical information about using the config_item interface. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/string.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/stat.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #include <linux/configfs.h> | ||||
| 
 | ||||
| 
 | ||||
| static inline struct config_item * to_item(struct list_head * entry) | ||||
| { | ||||
| 	return container_of(entry,struct config_item,ci_entry); | ||||
| } | ||||
| 
 | ||||
| /* Evil kernel */ | ||||
| static void config_item_release(struct kref *kref); | ||||
| 
 | ||||
| /**
 | ||||
|  *	config_item_init - initialize item. | ||||
|  *	@item:	item in question. | ||||
|  */ | ||||
| void config_item_init(struct config_item * item) | ||||
| { | ||||
| 	kref_init(&item->ci_kref); | ||||
| 	INIT_LIST_HEAD(&item->ci_entry); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	config_item_set_name - Set the name of an item | ||||
|  *	@item:	item. | ||||
|  *	@name:	name. | ||||
|  * | ||||
|  *	If strlen(name) >= CONFIGFS_ITEM_NAME_LEN, then use a | ||||
|  *	dynamically allocated string that @item->ci_name points to. | ||||
|  *	Otherwise, use the static @item->ci_namebuf array. | ||||
|  */ | ||||
| 
 | ||||
| int config_item_set_name(struct config_item * item, const char * fmt, ...) | ||||
| { | ||||
| 	int error = 0; | ||||
| 	int limit = CONFIGFS_ITEM_NAME_LEN; | ||||
| 	int need; | ||||
| 	va_list args; | ||||
| 	char * name; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * First, try the static array | ||||
| 	 */ | ||||
| 	va_start(args,fmt); | ||||
| 	need = vsnprintf(item->ci_namebuf,limit,fmt,args); | ||||
| 	va_end(args); | ||||
| 	if (need < limit) | ||||
| 		name = item->ci_namebuf; | ||||
| 	else { | ||||
| 		/*
 | ||||
| 		 * Need more space? Allocate it and try again | ||||
| 		 */ | ||||
| 		limit = need + 1; | ||||
| 		name = kmalloc(limit,GFP_KERNEL); | ||||
| 		if (!name) { | ||||
| 			error = -ENOMEM; | ||||
| 			goto Done; | ||||
| 		} | ||||
| 		va_start(args,fmt); | ||||
| 		need = vsnprintf(name,limit,fmt,args); | ||||
| 		va_end(args); | ||||
| 
 | ||||
| 		/* Still? Give up. */ | ||||
| 		if (need >= limit) { | ||||
| 			kfree(name); | ||||
| 			error = -EFAULT; | ||||
| 			goto Done; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Free the old name, if necessary. */ | ||||
| 	if (item->ci_name && item->ci_name != item->ci_namebuf) | ||||
| 		kfree(item->ci_name); | ||||
| 
 | ||||
| 	/* Now, set the new name */ | ||||
| 	item->ci_name = name; | ||||
|  Done: | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| EXPORT_SYMBOL(config_item_set_name); | ||||
| 
 | ||||
| void config_item_init_type_name(struct config_item *item, | ||||
| 				const char *name, | ||||
| 				struct config_item_type *type) | ||||
| { | ||||
| 	config_item_set_name(item, name); | ||||
| 	item->ci_type = type; | ||||
| 	config_item_init(item); | ||||
| } | ||||
| EXPORT_SYMBOL(config_item_init_type_name); | ||||
| 
 | ||||
| void config_group_init_type_name(struct config_group *group, const char *name, | ||||
| 			 struct config_item_type *type) | ||||
| { | ||||
| 	config_item_set_name(&group->cg_item, name); | ||||
| 	group->cg_item.ci_type = type; | ||||
| 	config_group_init(group); | ||||
| } | ||||
| EXPORT_SYMBOL(config_group_init_type_name); | ||||
| 
 | ||||
| struct config_item * config_item_get(struct config_item * item) | ||||
| { | ||||
| 	if (item) | ||||
| 		kref_get(&item->ci_kref); | ||||
| 	return item; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	config_item_cleanup - free config_item resources. | ||||
|  *	@item:	item. | ||||
|  */ | ||||
| 
 | ||||
| void config_item_cleanup(struct config_item * item) | ||||
| { | ||||
| 	struct config_item_type * t = item->ci_type; | ||||
| 	struct config_group * s = item->ci_group; | ||||
| 	struct config_item * parent = item->ci_parent; | ||||
| 
 | ||||
| 	pr_debug("config_item %s: cleaning up\n",config_item_name(item)); | ||||
| 	if (item->ci_name != item->ci_namebuf) | ||||
| 		kfree(item->ci_name); | ||||
| 	item->ci_name = NULL; | ||||
| 	if (t && t->ct_item_ops && t->ct_item_ops->release) | ||||
| 		t->ct_item_ops->release(item); | ||||
| 	if (s) | ||||
| 		config_group_put(s); | ||||
| 	if (parent) | ||||
| 		config_item_put(parent); | ||||
| } | ||||
| 
 | ||||
| static void config_item_release(struct kref *kref) | ||||
| { | ||||
| 	config_item_cleanup(container_of(kref, struct config_item, ci_kref)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	config_item_put - decrement refcount for item. | ||||
|  *	@item:	item. | ||||
|  * | ||||
|  *	Decrement the refcount, and if 0, call config_item_cleanup(). | ||||
|  */ | ||||
| void config_item_put(struct config_item * item) | ||||
| { | ||||
| 	if (item) | ||||
| 		kref_put(&item->ci_kref, config_item_release); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	config_group_init - initialize a group for use | ||||
|  *	@k:	group | ||||
|  */ | ||||
| 
 | ||||
| void config_group_init(struct config_group *group) | ||||
| { | ||||
| 	config_item_init(&group->cg_item); | ||||
| 	INIT_LIST_HEAD(&group->cg_children); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	config_group_find_obj - search for item in group. | ||||
|  *	@group:	group we're looking in. | ||||
|  *	@name:	item's name. | ||||
|  * | ||||
|  *	Lock group via @group->cg_subsys, and iterate over @group->cg_list, | ||||
|  *	looking for a matching config_item. If matching item is found | ||||
|  *	take a reference and return the item. | ||||
|  */ | ||||
| 
 | ||||
| struct config_item * config_group_find_obj(struct config_group * group, const char * name) | ||||
| { | ||||
| 	struct list_head * entry; | ||||
| 	struct config_item * ret = NULL; | ||||
| 
 | ||||
|         /* XXX LOCKING! */ | ||||
| 	list_for_each(entry,&group->cg_children) { | ||||
| 		struct config_item * item = to_item(entry); | ||||
| 		if (config_item_name(item) && | ||||
|                     !strcmp(config_item_name(item), name)) { | ||||
| 			ret = config_item_get(item); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| EXPORT_SYMBOL(config_item_init); | ||||
| EXPORT_SYMBOL(config_group_init); | ||||
| EXPORT_SYMBOL(config_item_get); | ||||
| EXPORT_SYMBOL(config_item_put); | ||||
| 
 | ||||
							
								
								
									
										159
									
								
								fs/configfs/mount.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								fs/configfs/mount.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,159 @@ | |||
| /* -*- mode: c; c-basic-offset: 8; -*-
 | ||||
|  * vim: noexpandtab sw=8 ts=8 sts=0: | ||||
|  * | ||||
|  * mount.c - operations for initializing and mounting configfs. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on sysfs: | ||||
|  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/fs.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/mount.h> | ||||
| #include <linux/pagemap.h> | ||||
| #include <linux/init.h> | ||||
| 
 | ||||
| #include <linux/configfs.h> | ||||
| #include "configfs_internal.h" | ||||
| 
 | ||||
| /* Random magic number */ | ||||
| #define CONFIGFS_MAGIC 0x62656570 | ||||
| 
 | ||||
| struct vfsmount * configfs_mount = NULL; | ||||
| struct super_block * configfs_sb = NULL; | ||||
| static int configfs_mnt_count = 0; | ||||
| 
 | ||||
| static struct super_operations configfs_ops = { | ||||
| 	.statfs		= simple_statfs, | ||||
| 	.drop_inode	= generic_delete_inode, | ||||
| }; | ||||
| 
 | ||||
| static struct config_group configfs_root_group = { | ||||
| 	.cg_item = { | ||||
| 		.ci_namebuf	= "root", | ||||
| 		.ci_name	= configfs_root_group.cg_item.ci_namebuf, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int configfs_is_root(struct config_item *item) | ||||
| { | ||||
| 	return item == &configfs_root_group.cg_item; | ||||
| } | ||||
| 
 | ||||
| static struct configfs_dirent configfs_root = { | ||||
| 	.s_sibling	= LIST_HEAD_INIT(configfs_root.s_sibling), | ||||
| 	.s_children	= LIST_HEAD_INIT(configfs_root.s_children), | ||||
| 	.s_element	= &configfs_root_group.cg_item, | ||||
| 	.s_type		= CONFIGFS_ROOT, | ||||
| }; | ||||
| 
 | ||||
| static int configfs_fill_super(struct super_block *sb, void *data, int silent) | ||||
| { | ||||
| 	struct inode *inode; | ||||
| 	struct dentry *root; | ||||
| 
 | ||||
| 	sb->s_blocksize = PAGE_CACHE_SIZE; | ||||
| 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | ||||
| 	sb->s_magic = CONFIGFS_MAGIC; | ||||
| 	sb->s_op = &configfs_ops; | ||||
| 	configfs_sb = sb; | ||||
| 
 | ||||
| 	inode = configfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); | ||||
| 	if (inode) { | ||||
| 		inode->i_op = &configfs_dir_inode_operations; | ||||
| 		inode->i_fop = &configfs_dir_operations; | ||||
| 		/* directory inodes start off with i_nlink == 2 (for "." entry) */ | ||||
| 		inode->i_nlink++; | ||||
| 	} else { | ||||
| 		pr_debug("configfs: could not get root inode\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	root = d_alloc_root(inode); | ||||
| 	if (!root) { | ||||
| 		pr_debug("%s: could not get root dentry!\n",__FUNCTION__); | ||||
| 		iput(inode); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	config_group_init(&configfs_root_group); | ||||
| 	configfs_root_group.cg_item.ci_dentry = root; | ||||
| 	root->d_fsdata = &configfs_root; | ||||
| 	sb->s_root = root; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct super_block *configfs_get_sb(struct file_system_type *fs_type, | ||||
| 	int flags, const char *dev_name, void *data) | ||||
| { | ||||
| 	return get_sb_single(fs_type, flags, data, configfs_fill_super); | ||||
| } | ||||
| 
 | ||||
| static struct file_system_type configfs_fs_type = { | ||||
| 	.owner		= THIS_MODULE, | ||||
| 	.name		= "configfs", | ||||
| 	.get_sb		= configfs_get_sb, | ||||
| 	.kill_sb	= kill_litter_super, | ||||
| }; | ||||
| 
 | ||||
| int configfs_pin_fs(void) | ||||
| { | ||||
| 	return simple_pin_fs("configfs", &configfs_mount, | ||||
| 			     &configfs_mnt_count); | ||||
| } | ||||
| 
 | ||||
| void configfs_release_fs(void) | ||||
| { | ||||
| 	simple_release_fs(&configfs_mount, &configfs_mnt_count); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static decl_subsys(config, NULL, NULL); | ||||
| 
 | ||||
| static int __init configfs_init(void) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	kset_set_kset_s(&config_subsys, kernel_subsys); | ||||
| 	err = subsystem_register(&config_subsys); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	err = register_filesystem(&configfs_fs_type); | ||||
| 	if (err) { | ||||
| 		printk(KERN_ERR "configfs: Unable to register filesystem!\n"); | ||||
| 		subsystem_unregister(&config_subsys); | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void __exit configfs_exit(void) | ||||
| { | ||||
| 	unregister_filesystem(&configfs_fs_type); | ||||
| 	subsystem_unregister(&config_subsys); | ||||
| } | ||||
| 
 | ||||
| MODULE_AUTHOR("Oracle"); | ||||
| MODULE_LICENSE("GPL"); | ||||
| MODULE_VERSION("0.0.1"); | ||||
| MODULE_DESCRIPTION("Simple RAM filesystem for user driven kernel subsystem configuration."); | ||||
| 
 | ||||
| module_init(configfs_init); | ||||
| module_exit(configfs_exit); | ||||
							
								
								
									
										281
									
								
								fs/configfs/symlink.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								fs/configfs/symlink.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,281 @@ | |||
| /* -*- mode: c; c-basic-offset: 8; -*-
 | ||||
|  * vim: noexpandtab sw=8 ts=8 sts=0: | ||||
|  * | ||||
|  * symlink.c - operations for configfs symlinks. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on sysfs: | ||||
|  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/fs.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/namei.h> | ||||
| 
 | ||||
| #include <linux/configfs.h> | ||||
| #include "configfs_internal.h" | ||||
| 
 | ||||
| static int item_depth(struct config_item * item) | ||||
| { | ||||
| 	struct config_item * p = item; | ||||
| 	int depth = 0; | ||||
| 	do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p)); | ||||
| 	return depth; | ||||
| } | ||||
| 
 | ||||
| static int item_path_length(struct config_item * item) | ||||
| { | ||||
| 	struct config_item * p = item; | ||||
| 	int length = 1; | ||||
| 	do { | ||||
| 		length += strlen(config_item_name(p)) + 1; | ||||
| 		p = p->ci_parent; | ||||
| 	} while (p && !configfs_is_root(p)); | ||||
| 	return length; | ||||
| } | ||||
| 
 | ||||
| static void fill_item_path(struct config_item * item, char * buffer, int length) | ||||
| { | ||||
| 	struct config_item * p; | ||||
| 
 | ||||
| 	--length; | ||||
| 	for (p = item; p && !configfs_is_root(p); p = p->ci_parent) { | ||||
| 		int cur = strlen(config_item_name(p)); | ||||
| 
 | ||||
| 		/* back up enough to print this bus id with '/' */ | ||||
| 		length -= cur; | ||||
| 		strncpy(buffer + length,config_item_name(p),cur); | ||||
| 		*(buffer + --length) = '/'; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int create_link(struct config_item *parent_item, | ||||
|  		       struct config_item *item, | ||||
| 		       struct dentry *dentry) | ||||
| { | ||||
| 	struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; | ||||
| 	struct configfs_symlink *sl; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = -ENOMEM; | ||||
| 	sl = kmalloc(sizeof(struct configfs_symlink), GFP_KERNEL); | ||||
| 	if (sl) { | ||||
| 		sl->sl_target = config_item_get(item); | ||||
| 		/* FIXME: needs a lock, I'd bet */ | ||||
| 		list_add(&sl->sl_list, &target_sd->s_links); | ||||
| 		ret = configfs_create_link(sl, parent_item->ci_dentry, | ||||
| 					   dentry); | ||||
| 		if (ret) { | ||||
| 			list_del_init(&sl->sl_list); | ||||
| 			config_item_put(item); | ||||
| 			kfree(sl); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int get_target(const char *symname, struct nameidata *nd, | ||||
| 		      struct config_item **target) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = path_lookup(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, nd); | ||||
| 	if (!ret) { | ||||
| 		if (nd->dentry->d_sb == configfs_sb) { | ||||
| 			*target = configfs_get_config_item(nd->dentry); | ||||
| 			if (!*target) { | ||||
| 				ret = -ENOENT; | ||||
| 				path_release(nd); | ||||
| 			} | ||||
| 		} else | ||||
| 			ret = -EPERM; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct nameidata nd; | ||||
| 	struct config_item *parent_item; | ||||
| 	struct config_item *target_item; | ||||
| 	struct config_item_type *type; | ||||
| 
 | ||||
| 	ret = -EPERM;  /* What lack-of-symlink returns */ | ||||
| 	if (dentry->d_parent == configfs_sb->s_root) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	parent_item = configfs_get_config_item(dentry->d_parent); | ||||
| 	type = parent_item->ci_type; | ||||
| 
 | ||||
| 	if (!type || !type->ct_item_ops || | ||||
| 	    !type->ct_item_ops->allow_link) | ||||
| 		goto out_put; | ||||
| 
 | ||||
| 	ret = get_target(symname, &nd, &target_item); | ||||
| 	if (ret) | ||||
| 		goto out_put; | ||||
| 
 | ||||
| 	ret = type->ct_item_ops->allow_link(parent_item, target_item); | ||||
| 	if (!ret) | ||||
| 		ret = create_link(parent_item, target_item, dentry); | ||||
| 
 | ||||
| 	config_item_put(target_item); | ||||
| 	path_release(&nd); | ||||
| 
 | ||||
| out_put: | ||||
| 	config_item_put(parent_item); | ||||
| 
 | ||||
| out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int configfs_unlink(struct inode *dir, struct dentry *dentry) | ||||
| { | ||||
| 	struct configfs_dirent *sd = dentry->d_fsdata; | ||||
| 	struct configfs_symlink *sl; | ||||
| 	struct config_item *parent_item; | ||||
| 	struct config_item_type *type; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = -EPERM;  /* What lack-of-symlink returns */ | ||||
| 	if (!(sd->s_type & CONFIGFS_ITEM_LINK)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (dentry->d_parent == configfs_sb->s_root) | ||||
| 		BUG(); | ||||
| 
 | ||||
| 	sl = sd->s_element; | ||||
| 
 | ||||
| 	parent_item = configfs_get_config_item(dentry->d_parent); | ||||
| 	type = parent_item->ci_type; | ||||
| 
 | ||||
| 	list_del_init(&sd->s_sibling); | ||||
| 	configfs_drop_dentry(sd, dentry->d_parent); | ||||
| 	dput(dentry); | ||||
| 	configfs_put(sd); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * drop_link() must be called before | ||||
| 	 * list_del_init(&sl->sl_list), so that the order of | ||||
| 	 * drop_link(this, target) and drop_item(target) is preserved. | ||||
| 	 */ | ||||
| 	if (type && type->ct_item_ops && | ||||
| 	    type->ct_item_ops->drop_link) | ||||
| 		type->ct_item_ops->drop_link(parent_item, | ||||
| 					       sl->sl_target); | ||||
| 
 | ||||
| 	/* FIXME: Needs lock */ | ||||
| 	list_del_init(&sl->sl_list); | ||||
| 
 | ||||
| 	/* Put reference from create_link() */ | ||||
| 	config_item_put(sl->sl_target); | ||||
| 	kfree(sl); | ||||
| 
 | ||||
| 	config_item_put(parent_item); | ||||
| 
 | ||||
| 	ret = 0; | ||||
| 
 | ||||
| out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int configfs_get_target_path(struct config_item * item, struct config_item * target, | ||||
| 				   char *path) | ||||
| { | ||||
| 	char * s; | ||||
| 	int depth, size; | ||||
| 
 | ||||
| 	depth = item_depth(item); | ||||
| 	size = item_path_length(target) + depth * 3 - 1; | ||||
| 	if (size > PATH_MAX) | ||||
| 		return -ENAMETOOLONG; | ||||
| 
 | ||||
| 	pr_debug("%s: depth = %d, size = %d\n", __FUNCTION__, depth, size); | ||||
| 
 | ||||
| 	for (s = path; depth--; s += 3) | ||||
| 		strcpy(s,"../"); | ||||
| 
 | ||||
| 	fill_item_path(target, path, size); | ||||
| 	pr_debug("%s: path = '%s'\n", __FUNCTION__, path); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int configfs_getlink(struct dentry *dentry, char * path) | ||||
| { | ||||
| 	struct config_item *item, *target_item; | ||||
| 	int error = 0; | ||||
| 
 | ||||
| 	item = configfs_get_config_item(dentry->d_parent); | ||||
| 	if (!item) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	target_item = configfs_get_config_item(dentry); | ||||
| 	if (!target_item) { | ||||
| 		config_item_put(item); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	down_read(&configfs_rename_sem); | ||||
| 	error = configfs_get_target_path(item, target_item, path); | ||||
| 	up_read(&configfs_rename_sem); | ||||
| 
 | ||||
| 	config_item_put(item); | ||||
| 	config_item_put(target_item); | ||||
| 	return error; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static void *configfs_follow_link(struct dentry *dentry, struct nameidata *nd) | ||||
| { | ||||
| 	int error = -ENOMEM; | ||||
| 	unsigned long page = get_zeroed_page(GFP_KERNEL); | ||||
| 
 | ||||
| 	if (page) { | ||||
| 		error = configfs_getlink(dentry, (char *)page); | ||||
| 		if (!error) { | ||||
| 			nd_set_link(nd, (char *)page); | ||||
| 			return (void *)page; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	nd_set_link(nd, ERR_PTR(error)); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void configfs_put_link(struct dentry *dentry, struct nameidata *nd, | ||||
| 			      void *cookie) | ||||
| { | ||||
| 	if (cookie) { | ||||
| 		unsigned long page = (unsigned long)cookie; | ||||
| 		free_page(page); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct inode_operations configfs_symlink_inode_operations = { | ||||
| 	.follow_link = configfs_follow_link, | ||||
| 	.readlink = generic_readlink, | ||||
| 	.put_link = configfs_put_link, | ||||
| }; | ||||
| 
 | ||||
							
								
								
									
										205
									
								
								include/linux/configfs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								include/linux/configfs.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,205 @@ | |||
| /* -*- mode: c; c-basic-offset: 8; -*-
 | ||||
|  * vim: noexpandtab sw=8 ts=8 sts=0: | ||||
|  * | ||||
|  * configfs.h - definitions for the device driver filesystem | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public | ||||
|  * License along with this program; if not, write to the | ||||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||||
|  * Boston, MA 021110-1307, USA. | ||||
|  * | ||||
|  * Based on sysfs: | ||||
|  * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel | ||||
|  * | ||||
|  * Based on kobject.h: | ||||
|  *      Copyright (c) 2002-2003	Patrick Mochel | ||||
|  *      Copyright (c) 2002-2003	Open Source Development Labs | ||||
|  * | ||||
|  * configfs Copyright (C) 2005 Oracle.  All rights reserved. | ||||
|  * | ||||
|  * Please read Documentation/filesystems/configfs.txt before using the | ||||
|  * configfs interface, ESPECIALLY the parts about reference counts and | ||||
|  * item destructors. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _CONFIGFS_H_ | ||||
| #define _CONFIGFS_H_ | ||||
| 
 | ||||
| #ifdef __KERNEL__ | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/kref.h> | ||||
| 
 | ||||
| #include <asm/atomic.h> | ||||
| #include <asm/semaphore.h> | ||||
| 
 | ||||
| #define CONFIGFS_ITEM_NAME_LEN	20 | ||||
| 
 | ||||
| struct module; | ||||
| 
 | ||||
| struct configfs_item_operations; | ||||
| struct configfs_group_operations; | ||||
| struct configfs_attribute; | ||||
| struct configfs_subsystem; | ||||
| 
 | ||||
| struct config_item { | ||||
| 	char			*ci_name; | ||||
| 	char			ci_namebuf[CONFIGFS_ITEM_NAME_LEN]; | ||||
| 	struct kref		ci_kref; | ||||
| 	struct list_head	ci_entry; | ||||
| 	struct config_item	*ci_parent; | ||||
| 	struct config_group	*ci_group; | ||||
| 	struct config_item_type	*ci_type; | ||||
| 	struct dentry		*ci_dentry; | ||||
| }; | ||||
| 
 | ||||
| extern int config_item_set_name(struct config_item *, const char *, ...); | ||||
| 
 | ||||
| static inline char *config_item_name(struct config_item * item) | ||||
| { | ||||
| 	return item->ci_name; | ||||
| } | ||||
| 
 | ||||
| extern void config_item_init(struct config_item *); | ||||
| extern void config_item_init_type_name(struct config_item *item, | ||||
| 				       const char *name, | ||||
| 				       struct config_item_type *type); | ||||
| extern void config_item_cleanup(struct config_item *); | ||||
| 
 | ||||
| extern struct config_item * config_item_get(struct config_item *); | ||||
| extern void config_item_put(struct config_item *); | ||||
| 
 | ||||
| struct config_item_type { | ||||
| 	struct module				*ct_owner; | ||||
| 	struct configfs_item_operations		*ct_item_ops; | ||||
| 	struct configfs_group_operations	*ct_group_ops; | ||||
| 	struct configfs_attribute		**ct_attrs; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  *	group - a group of config_items of a specific type, belonging | ||||
|  *	to a specific subsystem. | ||||
|  */ | ||||
| 
 | ||||
| struct config_group { | ||||
| 	struct config_item		cg_item; | ||||
| 	struct list_head		cg_children; | ||||
| 	struct configfs_subsystem 	*cg_subsys; | ||||
| 	struct config_group		**default_groups; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| extern void config_group_init(struct config_group *group); | ||||
| extern void config_group_init_type_name(struct config_group *group, | ||||
| 					const char *name, | ||||
| 					struct config_item_type *type); | ||||
| 
 | ||||
| 
 | ||||
| static inline struct config_group *to_config_group(struct config_item *item) | ||||
| { | ||||
| 	return item ? container_of(item,struct config_group,cg_item) : NULL; | ||||
| } | ||||
| 
 | ||||
| static inline struct config_group *config_group_get(struct config_group *group) | ||||
| { | ||||
| 	return group ? to_config_group(config_item_get(&group->cg_item)) : NULL; | ||||
| } | ||||
| 
 | ||||
| static inline void config_group_put(struct config_group *group) | ||||
| { | ||||
| 	config_item_put(&group->cg_item); | ||||
| } | ||||
| 
 | ||||
| extern struct config_item *config_group_find_obj(struct config_group *, const char *); | ||||
| 
 | ||||
| 
 | ||||
| struct configfs_attribute { | ||||
| 	char			*ca_name; | ||||
| 	struct module 		*ca_owner; | ||||
| 	mode_t			ca_mode; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * If allow_link() exists, the item can symlink(2) out to other | ||||
|  * items.  If the item is a group, it may support mkdir(2). | ||||
|  * Groups supply one of make_group() and make_item().  If the | ||||
|  * group supports make_group(), one can create group children.  If it | ||||
|  * supports make_item(), one can create config_item children.  If it has | ||||
|  * default_groups on group->default_groups, it has automatically created | ||||
|  * group children.  default_groups may coexist alongsize make_group() or | ||||
|  * make_item(), but if the group wishes to have only default_groups | ||||
|  * children (disallowing mkdir(2)), it need not provide either function. | ||||
|  * If the group has commit(), it supports pending and commited (active) | ||||
|  * items. | ||||
|  */ | ||||
| struct configfs_item_operations { | ||||
| 	void (*release)(struct config_item *); | ||||
| 	ssize_t	(*show_attribute)(struct config_item *, struct configfs_attribute *,char *); | ||||
| 	ssize_t	(*store_attribute)(struct config_item *,struct configfs_attribute *,const char *, size_t); | ||||
| 	int (*allow_link)(struct config_item *src, struct config_item *target); | ||||
| 	int (*drop_link)(struct config_item *src, struct config_item *target); | ||||
| }; | ||||
| 
 | ||||
| struct configfs_group_operations { | ||||
| 	struct config_item *(*make_item)(struct config_group *group, const char *name); | ||||
| 	struct config_group *(*make_group)(struct config_group *group, const char *name); | ||||
| 	int (*commit_item)(struct config_item *item); | ||||
| 	void (*drop_item)(struct config_group *group, struct config_item *item); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Use these macros to make defining attributes easier. See include/linux/device.h | ||||
|  * for examples.. | ||||
|  */ | ||||
| 
 | ||||
| #if 0 | ||||
| #define __ATTR(_name,_mode,_show,_store) { \ | ||||
| 	.attr = {.ca_name = __stringify(_name), .ca_mode = _mode, .ca_owner = THIS_MODULE },	\ | ||||
| 	.show	= _show,					\ | ||||
| 	.store	= _store,					\ | ||||
| } | ||||
| 
 | ||||
| #define __ATTR_RO(_name) { \ | ||||
| 	.attr	= { .ca_name = __stringify(_name), .ca_mode = 0444, .ca_owner = THIS_MODULE },	\ | ||||
| 	.show	= _name##_show,	\ | ||||
| } | ||||
| 
 | ||||
| #define __ATTR_NULL { .attr = { .name = NULL } } | ||||
| 
 | ||||
| #define attr_name(_attr) (_attr).attr.name | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| struct configfs_subsystem { | ||||
| 	struct config_group	su_group; | ||||
| 	struct semaphore	su_sem; | ||||
| }; | ||||
| 
 | ||||
| static inline struct configfs_subsystem *to_configfs_subsystem(struct config_group *group) | ||||
| { | ||||
| 	return group ? | ||||
| 		container_of(group, struct configfs_subsystem, su_group) : | ||||
| 		NULL; | ||||
| } | ||||
| 
 | ||||
| int configfs_register_subsystem(struct configfs_subsystem *subsys); | ||||
| void configfs_unregister_subsystem(struct configfs_subsystem *subsys); | ||||
| 
 | ||||
| #endif  /* __KERNEL__ */ | ||||
| 
 | ||||
| #endif /* _CONFIGFS_H_ */ | ||||
		Loading…
	
		Reference in a new issue
	
	 Joel Becker
						Joel Becker