2005-04-16 15:20:36 -07:00
/* devfs (Device FileSystem) driver.
Copyright ( C ) 1998 - 2002 Richard Gooch
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation ; either
version 2 of the License , or ( at your option ) any later version .
This library 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
Library General Public License for more details .
You should have received a copy of the GNU Library General Public
License along with this library ; if not , write to the Free
Software Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
Richard Gooch may be reached by email at rgooch @ atnf . csiro . au
The postal address is :
Richard Gooch , c / o ATNF , P . O . Box 76 , Epping , N . S . W . , 2121 , Australia .
ChangeLog
19980110 Richard Gooch < rgooch @ atnf . csiro . au >
Original version .
v0 .1
19980111 Richard Gooch < rgooch @ atnf . csiro . au >
Created per - fs inode table rather than using inode - > u . generic_ip
v0 .2
19980111 Richard Gooch < rgooch @ atnf . csiro . au >
Created . epoch inode which has a ctime of 0.
Fixed loss of named pipes when dentries lost .
Fixed loss of inode data when devfs_register ( ) follows mknod ( ) .
v0 .3
19980111 Richard Gooch < rgooch @ atnf . csiro . au >
Fix for when compiling with CONFIG_KERNELD .
19980112 Richard Gooch < rgooch @ atnf . csiro . au >
Fix for readdir ( ) which sometimes didn ' t show entries .
Added < < tolerant > > option to < devfs_register > .
v0 .4
19980113 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_fill_file > function .
v0 .5
19980115 Richard Gooch < rgooch @ atnf . csiro . au >
Added subdirectory support . Major restructuring .
19980116 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed < find_by_dev > to not search major = 0 , minor = 0.
Added symlink support .
v0 .6
19980120 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_mk_dir > function and support directory unregister
19980120 Richard Gooch < rgooch @ atnf . csiro . au >
Auto - ownership uses real uid / gid rather than effective uid / gid .
v0 .7
19980121 Richard Gooch < rgooch @ atnf . csiro . au >
Supported creation of sockets .
v0 .8
19980122 Richard Gooch < rgooch @ atnf . csiro . au >
Added DEVFS_FL_HIDE_UNREG flag .
Interface change to < devfs_mk_symlink > .
Created < devfs_symlink > to support symlink ( 2 ) .
v0 .9
19980123 Richard Gooch < rgooch @ atnf . csiro . au >
Added check to < devfs_fill_file > to check inode is in devfs .
Added optional traversal of symlinks .
v0 .10
19980124 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_get_flags > and < devfs_set_flags > .
v0 .11
19980125 C . Scott Ananian < cananian @ alumni . princeton . edu >
Created < devfs_find_handle > .
19980125 Richard Gooch < rgooch @ atnf . csiro . au >
Allow removal of symlinks .
v0 .12
19980125 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_set_symlink_destination > .
19980126 Richard Gooch < rgooch @ atnf . csiro . au >
Moved DEVFS_SUPER_MAGIC into header file .
Added DEVFS_FL_HIDE flag .
Created < devfs_get_maj_min > .
Created < devfs_get_handle_from_inode > .
Fixed minor bug in < find_by_dev > .
19980127 Richard Gooch < rgooch @ atnf . csiro . au >
Changed interface to < find_by_dev > , < find_entry > ,
< devfs_unregister > , < devfs_fill_file > and < devfs_find_handle > .
Fixed inode times when symlink created with symlink ( 2 ) .
v0 .13
19980129 C . Scott Ananian < cananian @ alumni . princeton . edu >
Exported < devfs_set_symlink_destination > , < devfs_get_maj_min >
and < devfs_get_handle_from_inode > .
19980129 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_unlink > to support unlink ( 2 ) .
v0 .14
19980129 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed kerneld support for entries in devfs subdirectories .
19980130 Richard Gooch < rgooch @ atnf . csiro . au >
Bugfixes in < call_kerneld > .
v0 .15
19980207 Richard Gooch < rgooch @ atnf . csiro . au >
Call kerneld when looking up unregistered entries .
v0 .16
19980326 Richard Gooch < rgooch @ atnf . csiro . au >
Modified interface to < devfs_find_handle > for symlink traversal .
v0 .17
19980331 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed persistence bug with device numbers for manually created
device files .
Fixed problem with recreating symlinks with different content .
v0 .18
19980401 Richard Gooch < rgooch @ atnf . csiro . au >
Changed to CONFIG_KMOD .
Hide entries which are manually unlinked .
Always invalidate devfs dentry cache when registering entries .
Created < devfs_rmdir > to support rmdir ( 2 ) .
Ensure directories created by < devfs_mk_dir > are visible .
v0 .19
19980402 Richard Gooch < rgooch @ atnf . csiro . au >
Invalidate devfs dentry cache when making directories .
Invalidate devfs dentry cache when removing entries .
Fixed persistence bug with fifos .
v0 .20
19980421 Richard Gooch < rgooch @ atnf . csiro . au >
Print process command when debugging kerneld / kmod .
Added debugging for register / unregister / change operations .
19980422 Richard Gooch < rgooch @ atnf . csiro . au >
Added " devfs= " boot options .
v0 .21
19980426 Richard Gooch < rgooch @ atnf . csiro . au >
No longer lock / unlock superblock in < devfs_put_super > .
Drop negative dentries when they are released .
Manage dcache more efficiently .
v0 .22
19980427 Richard Gooch < rgooch @ atnf . csiro . au >
Added DEVFS_FL_AUTO_DEVNUM flag .
v0 .23
19980430 Richard Gooch < rgooch @ atnf . csiro . au >
No longer set unnecessary methods .
v0 .24
19980504 Richard Gooch < rgooch @ atnf . csiro . au >
Added PID display to < call_kerneld > debugging message .
Added " after " debugging message to < call_kerneld > .
19980519 Richard Gooch < rgooch @ atnf . csiro . au >
Added " diread " and " diwrite " boot options .
19980520 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed persistence problem with permissions .
v0 .25
19980602 Richard Gooch < rgooch @ atnf . csiro . au >
Support legacy device nodes .
Fixed bug where recreated inodes were hidden .
v0 .26
19980602 Richard Gooch < rgooch @ atnf . csiro . au >
Improved debugging in < get_vfs_inode > .
19980607 Richard Gooch < rgooch @ atnf . csiro . au >
No longer free old dentries in < devfs_mk_dir > .
Free all dentries for a given entry when deleting inodes .
v0 .27
19980627 Richard Gooch < rgooch @ atnf . csiro . au >
Limit auto - device numbering to majors 128 to 239.
v0 .28
19980629 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed inode times persistence problem .
v0 .29
19980704 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed spelling in < devfs_readlink > debug .
Fixed bug in < devfs_setup > parsing " dilookup " .
v0 .30
19980705 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed devfs inode leak when manually recreating inodes .
Fixed permission persistence problem when recreating inodes .
v0 .31
19980727 Richard Gooch < rgooch @ atnf . csiro . au >
Removed harmless " unused variable " compiler warning .
Fixed modes for manually recreated device nodes .
v0 .32
19980728 Richard Gooch < rgooch @ atnf . csiro . au >
Added NULL devfs inode warning in < devfs_read_inode > .
Force all inode nlink values to 1.
v0 .33
19980730 Richard Gooch < rgooch @ atnf . csiro . au >
Added " dimknod " boot option .
Set inode nlink to 0 when freeing dentries .
Fixed modes for manually recreated symlinks .
v0 .34
19980802 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed bugs in recreated directories and symlinks .
v0 .35
19980806 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed bugs in recreated device nodes .
19980807 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed bug in currently unused < devfs_get_handle_from_inode > .
Defined new < devfs_handle_t > type .
Improved debugging when getting entries .
Fixed bug where directories could be emptied .
v0 .36
19980809 Richard Gooch < rgooch @ atnf . csiro . au >
Replaced dummy . epoch inode with . devfsd character device .
19980810 Richard Gooch < rgooch @ atnf . csiro . au >
Implemented devfsd protocol revision 0.
v0 .37
19980819 Richard Gooch < rgooch @ atnf . csiro . au >
Added soothing message to warning in < devfs_d_iput > .
v0 .38
19980829 Richard Gooch < rgooch @ atnf . csiro . au >
Use GCC extensions for structure initialisations .
Implemented async open notification .
Incremented devfsd protocol revision to 1.
v0 .39
19980908 Richard Gooch < rgooch @ atnf . csiro . au >
Moved async open notification to end of < devfs_open > .
v0 .40
19980910 Richard Gooch < rgooch @ atnf . csiro . au >
Prepended " /dev/ " to module load request .
Renamed < call_kerneld > to < call_kmod > .
v0 .41
19980910 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed typo " AYSNC " - > " ASYNC " .
v0 .42
19980910 Richard Gooch < rgooch @ atnf . csiro . au >
Added open flag for files .
v0 .43
19980927 Richard Gooch < rgooch @ atnf . csiro . au >
Set i_blocks = 0 and i_blksize = 1024 in < devfs_read_inode > .
v0 .44
19981005 Richard Gooch < rgooch @ atnf . csiro . au >
Added test for empty < < name > > in < devfs_find_handle > .
Renamed < generate_path > to < devfs_generate_path > and published .
v0 .45
19981006 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_get_fops > .
v0 .46
19981007 Richard Gooch < rgooch @ atnf . csiro . au >
Limit auto - device numbering to majors 144 to 239.
v0 .47
19981010 Richard Gooch < rgooch @ atnf . csiro . au >
Updated < devfs_follow_link > for VFS change in 2.1 .125 .
v0 .48
19981022 Richard Gooch < rgooch @ atnf . csiro . au >
Created DEVFS_ FL_COMPAT flag .
v0 .49
19981023 Richard Gooch < rgooch @ atnf . csiro . au >
Created " nocompat " boot option .
v0 .50
19981025 Richard Gooch < rgooch @ atnf . csiro . au >
Replaced " mount " boot option with " nomount " .
v0 .51
19981110 Richard Gooch < rgooch @ atnf . csiro . au >
Created " only " boot option .
v0 .52
19981112 Richard Gooch < rgooch @ atnf . csiro . au >
Added DEVFS_FL_REMOVABLE flag .
v0 .53
19981114 Richard Gooch < rgooch @ atnf . csiro . au >
Only call < scan_dir_for_removable > on first call to
< devfs_readdir > .
v0 .54
19981205 Richard Gooch < rgooch @ atnf . csiro . au >
Updated < devfs_rmdir > for VFS change in 2.1 .131 .
v0 .55
19981218 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_mk_compat > .
19981220 Richard Gooch < rgooch @ atnf . csiro . au >
Check for partitions on removable media in < devfs_lookup > .
v0 .56
19990118 Richard Gooch < rgooch @ atnf . csiro . au >
Added support for registering regular files .
Created < devfs_set_file_size > .
Update devfs inodes from entries if not changed through FS .
v0 .57
19990124 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed < devfs_fill_file > to only initialise temporary inodes .
Trap for NULL fops in < devfs_register > .
Return - ENODEV in < devfs_fill_file > for non - driver inodes .
v0 .58
19990126 Richard Gooch < rgooch @ atnf . csiro . au >
Switched from PATH_MAX to DEVFS_PATHLEN .
v0 .59
19990127 Richard Gooch < rgooch @ atnf . csiro . au >
Created " nottycompat " boot option .
v0 .60
19990318 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed < devfsd_read > to not overrun event buffer .
v0 .61
19990329 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_auto_unregister > .
v0 .62
19990330 Richard Gooch < rgooch @ atnf . csiro . au >
Don ' t return unregistred entries in < devfs_find_handle > .
Panic in < devfs_unregister > if entry unregistered .
19990401 Richard Gooch < rgooch @ atnf . csiro . au >
Don ' t panic in < devfs_auto_unregister > for duplicates .
v0 .63
19990402 Richard Gooch < rgooch @ atnf . csiro . au >
Don ' t unregister already unregistered entries in < unregister > .
v0 .64
19990510 Richard Gooch < rgooch @ atnf . csiro . au >
Disable warning messages when unable to read partition table for
removable media .
v0 .65
19990512 Richard Gooch < rgooch @ atnf . csiro . au >
Updated < devfs_lookup > for VFS change in 2.3 .1 - pre1 .
Created " oops-on-panic " boot option .
Improved debugging in < devfs_register > and < devfs_unregister > .
v0 .66
19990519 Richard Gooch < rgooch @ atnf . csiro . au >
Added documentation for some functions .
19990525 Richard Gooch < rgooch @ atnf . csiro . au >
Removed " oops-on-panic " boot option : now always Oops .
v0 .67
19990531 Richard Gooch < rgooch @ atnf . csiro . au >
Improved debugging in < devfs_register > .
v0 .68
19990604 Richard Gooch < rgooch @ atnf . csiro . au >
Added " diunlink " and " nokmod " boot options .
Removed superfluous warning message in < devfs_d_iput > .
v0 .69
19990611 Richard Gooch < rgooch @ atnf . csiro . au >
Took account of change to < d_alloc_root > .
v0 .70
19990614 Richard Gooch < rgooch @ atnf . csiro . au >
Created separate event queue for each mounted devfs .
Removed < devfs_invalidate_dcache > .
Created new ioctl ( ) s .
Incremented devfsd protocol revision to 3.
Fixed bug when re - creating directories : contents were lost .
Block access to inodes until devfsd updates permissions .
19990615 Richard Gooch < rgooch @ atnf . csiro . au >
Support 2.2 . x kernels .
v0 .71
19990623 Richard Gooch < rgooch @ atnf . csiro . au >
Switched to sending process uid / gid to devfsd .
Renamed < call_kmod > to < try_modload > .
Added DEVFSD_NOTIFY_LOOKUP event .
19990624 Richard Gooch < rgooch @ atnf . csiro . au >
Added DEVFSD_NOTIFY_CHANGE event .
Incremented devfsd protocol revision to 4.
v0 .72
19990713 Richard Gooch < rgooch @ atnf . csiro . au >
Return EISDIR rather than EINVAL for read ( 2 ) on directories .
v0 .73
19990809 Richard Gooch < rgooch @ atnf . csiro . au >
Changed < devfs_setup > to new __init scheme .
v0 .74
19990901 Richard Gooch < rgooch @ atnf . csiro . au >
Changed remaining function declarations to new __init scheme .
v0 .75
19991013 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_get_info > , < devfs_set_info > ,
< devfs_get_first_child > and < devfs_get_next_sibling > .
Added < < dir > > parameter to < devfs_register > , < devfs_mk_compat > ,
< devfs_mk_dir > and < devfs_find_handle > .
Work sponsored by SGI .
v0 .76
19991017 Richard Gooch < rgooch @ atnf . csiro . au >
Allow multiple unregistrations .
Work sponsored by SGI .
v0 .77
19991026 Richard Gooch < rgooch @ atnf . csiro . au >
Added major and minor number to devfsd protocol .
Incremented devfsd protocol revision to 5.
Work sponsored by SGI .
v0 .78
19991030 Richard Gooch < rgooch @ atnf . csiro . au >
Support info pointer for all devfs entry types .
Added < < info > > parameter to < devfs_mk_dir > and
< devfs_mk_symlink > .
Work sponsored by SGI .
v0 .79
19991031 Richard Gooch < rgooch @ atnf . csiro . au >
Support " ../ " when searching devfs namespace .
Work sponsored by SGI .
v0 .80
19991101 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_get_unregister_slave > .
Work sponsored by SGI .
v0 .81
19991103 Richard Gooch < rgooch @ atnf . csiro . au >
Exported < devfs_get_parent > .
Work sponsored by SGI .
v0 .82
19991104 Richard Gooch < rgooch @ atnf . csiro . au >
Removed unused < devfs_set_symlink_destination > .
19991105 Richard Gooch < rgooch @ atnf . csiro . au >
Do not hide entries from devfsd or children .
Removed DEVFS_ FL_TTY_COMPAT flag .
Removed " nottycompat " boot option .
Removed < devfs_mk_compat > .
Work sponsored by SGI .
v0 .83
19991107 Richard Gooch < rgooch @ atnf . csiro . au >
Added DEVFS_FL_WAIT flag .
Work sponsored by SGI .
v0 .84
19991107 Richard Gooch < rgooch @ atnf . csiro . au >
Support new " disc " naming scheme in < get_removable_partition > .
Allow NULL fops in < devfs_register > .
Work sponsored by SGI .
v0 .85
19991110 Richard Gooch < rgooch @ atnf . csiro . au >
Fall back to major table if NULL fops given to < devfs_register > .
Work sponsored by SGI .
v0 .86
19991204 Richard Gooch < rgooch @ atnf . csiro . au >
Support fifos when unregistering .
Work sponsored by SGI .
v0 .87
19991209 Richard Gooch < rgooch @ atnf . csiro . au >
Removed obsolete DEVFS_ FL_COMPAT and DEVFS_ FL_TOLERANT flags .
Work sponsored by SGI .
v0 .88
19991214 Richard Gooch < rgooch @ atnf . csiro . au >
Removed kmod support .
Work sponsored by SGI .
v0 .89
19991216 Richard Gooch < rgooch @ atnf . csiro . au >
Improved debugging in < get_vfs_inode > .
Ensure dentries created by devfsd will be cleaned up .
Work sponsored by SGI .
v0 .90
19991223 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_get_name > .
Work sponsored by SGI .
v0 .91
20000203 Richard Gooch < rgooch @ atnf . csiro . au >
Ported to kernel 2.3 .42 .
Removed < devfs_fill_file > .
Work sponsored by SGI .
v0 .92
20000306 Richard Gooch < rgooch @ atnf . csiro . au >
Added DEVFS_ FL_NO_PERSISTENCE flag .
Removed unnecessary call to < update_devfs_inode_from_entry > in
< devfs_readdir > .
Work sponsored by SGI .
v0 .93
20000413 Richard Gooch < rgooch @ atnf . csiro . au >
Set inode - > i_size to correct size for symlinks .
20000414 Richard Gooch < rgooch @ atnf . csiro . au >
Only give lookup ( ) method to directories to comply with new VFS
assumptions .
Work sponsored by SGI .
20000415 Richard Gooch < rgooch @ atnf . csiro . au >
Remove unnecessary tests in symlink methods .
Don ' t kill existing block ops in < devfs_read_inode > .
Work sponsored by SGI .
v0 .94
20000424 Richard Gooch < rgooch @ atnf . csiro . au >
Don ' t create missing directories in < devfs_find_handle > .
Work sponsored by SGI .
v0 .95
20000430 Richard Gooch < rgooch @ atnf . csiro . au >
Added CONFIG_DEVFS_MOUNT .
Work sponsored by SGI .
v0 .96
20000608 Richard Gooch < rgooch @ atnf . csiro . au >
Disabled multi - mount capability ( use VFS bindings instead ) .
Work sponsored by SGI .
v0 .97
20000610 Richard Gooch < rgooch @ atnf . csiro . au >
Switched to FS_SINGLE to disable multi - mounts .
20000612 Richard Gooch < rgooch @ atnf . csiro . au >
Removed module support .
Removed multi - mount code .
Removed compatibility macros : VFS has changed too much .
Work sponsored by SGI .
v0 .98
20000614 Richard Gooch < rgooch @ atnf . csiro . au >
Merged devfs inode into devfs entry .
Work sponsored by SGI .
v0 .99
20000619 Richard Gooch < rgooch @ atnf . csiro . au >
Removed dead code in < devfs_register > which used to call
< free_dentries > .
Work sponsored by SGI .
v0 .100
20000621 Richard Gooch < rgooch @ atnf . csiro . au >
Changed interface to < devfs_register > .
Work sponsored by SGI .
v0 .101
20000622 Richard Gooch < rgooch @ atnf . csiro . au >
Simplified interface to < devfs_mk_symlink > and < devfs_mk_dir > .
Simplified interface to < devfs_find_handle > .
Work sponsored by SGI .
v0 .102
20010519 Richard Gooch < rgooch @ atnf . csiro . au >
Ensure < devfs_generate_path > terminates string for root entry .
Exported < devfs_get_name > to modules .
20010520 Richard Gooch < rgooch @ atnf . csiro . au >
Make < devfs_mk_symlink > send events to devfsd .
Cleaned up option processing in < devfs_setup > .
20010521 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed bugs in handling symlinks : could leak or cause Oops .
20010522 Richard Gooch < rgooch @ atnf . csiro . au >
Cleaned up directory handling by separating fops .
v0 .103
20010601 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed handling of inverted options in < devfs_setup > .
v0 .104
20010604 Richard Gooch < rgooch @ atnf . csiro . au >
Adjusted < try_modload > to account for < devfs_generate_path > fix .
v0 .105
20010617 Richard Gooch < rgooch @ atnf . csiro . au >
Answered question posed by Al Viro and removed his comments .
Moved setting of registered flag after other fields are changed .
Fixed race between < devfsd_close > and < devfsd_notify_one > .
Global VFS changes added bogus BKL to < devfsd_close > : removed .
Widened locking in < devfs_readlink > and < devfs_follow_link > .
Replaced < devfsd_read > stack usage with < devfsd_ioctl > kmalloc .
Simplified locking in < devfsd_ioctl > and fixed memory leak .
v0 .106
20010709 Richard Gooch < rgooch @ atnf . csiro . au >
Removed broken devnum allocation and use < devfs_alloc_devnum > .
Fixed old devnum leak by calling new < devfs_dealloc_devnum > .
v0 .107
20010712 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed bug in < devfs_setup > which could hang boot process .
v0 .108
20010730 Richard Gooch < rgooch @ atnf . csiro . au >
Added DEVFSD_NOTIFY_DELETE event .
20010801 Richard Gooch < rgooch @ atnf . csiro . au >
Removed # include < asm / segment . h > .
v0 .109
20010807 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed inode table races by removing it and using
inode - > u . generic_ip instead .
Moved < devfs_read_inode > into < get_vfs_inode > .
Moved < devfs_write_inode > into < devfs_notify_change > .
v0 .110
20010808 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed race in < devfs_do_symlink > for uni - processor .
v0 .111
20010818 Richard Gooch < rgooch @ atnf . csiro . au >
Removed remnant of multi - mount support in < devfs_mknod > .
Removed unused DEVFS_FL_SHOW_UNREG flag .
v0 .112
20010820 Richard Gooch < rgooch @ atnf . csiro . au >
Removed nlink field from struct devfs_inode .
v0 .113
20010823 Richard Gooch < rgooch @ atnf . csiro . au >
Replaced BKL with global rwsem to protect symlink data ( quick
and dirty hack ) .
v0 .114
20010827 Richard Gooch < rgooch @ atnf . csiro . au >
Replaced global rwsem for symlink with per - link refcount .
v0 .115
20010919 Richard Gooch < rgooch @ atnf . csiro . au >
Set inode - > i_mapping - > a_ops for block nodes in < get_vfs_inode > .
v0 .116
20011008 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed overrun in < devfs_link > by removing function ( not needed ) .
20011009 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed buffer underrun in < try_modload > .
20011029 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed race in < devfsd_ioctl > when setting event mask .
20011114 Richard Gooch < rgooch @ atnf . csiro . au >
First release of new locking code .
v1 .0
20011117 Richard Gooch < rgooch @ atnf . csiro . au >
Discard temporary buffer , now use " %s " for dentry names .
20011118 Richard Gooch < rgooch @ atnf . csiro . au >
Don ' t generate path in < try_modload > : use fake entry instead .
Use " existing " directory in < _devfs_make_parent_for_leaf > .
20011122 Richard Gooch < rgooch @ atnf . csiro . au >
Use slab cache rather than fixed buffer for devfsd events .
v1 .1
20011125 Richard Gooch < rgooch @ atnf . csiro . au >
Send DEVFSD_NOTIFY_REGISTERED events in < devfs_mk_dir > .
20011127 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed locking bug in < devfs_d_revalidate_wait > due to typo .
Do not send CREATE , CHANGE , ASYNC_OPEN or DELETE events from
devfsd or children .
v1 .2
20011202 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed bug in < devfsd_read > : was dereferencing freed pointer .
v1 .3
20011203 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed bug in < devfsd_close > : was dereferencing freed pointer .
Added process group check for devfsd privileges .
v1 .4
20011204 Richard Gooch < rgooch @ atnf . csiro . au >
Use SLAB_ATOMIC in < devfsd_notify_de > from < devfs_d_delete > .
v1 .5
20011211 Richard Gooch < rgooch @ atnf . csiro . au >
Return old entry in < devfs_mk_dir > for 2.4 . x kernels .
20011212 Richard Gooch < rgooch @ atnf . csiro . au >
Increment refcount on module in < check_disc_changed > .
20011215 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_get_handle > and exported < devfs_put > .
Increment refcount on module in < devfs_get_ops > .
Created < devfs_put_ops > .
v1 .6
20011216 Richard Gooch < rgooch @ atnf . csiro . au >
Added poisoning to < devfs_put > .
Improved debugging messages .
v1 .7
20011221 Richard Gooch < rgooch @ atnf . csiro . au >
Corrected ( made useful ) debugging message in < unregister > .
Moved < kmem_cache_create > in < mount_devfs_fs > to < init_devfs_fs >
20011224 Richard Gooch < rgooch @ atnf . csiro . au >
Added magic number to guard against scribbling drivers .
20011226 Richard Gooch < rgooch @ atnf . csiro . au >
Only return old entry in < devfs_mk_dir > if a directory .
Defined macros for error and debug messages .
v1 .8
20020113 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed ( rare , old ) race in < devfs_lookup > .
v1 .9
20020120 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed deadlock bug in < devfs_d_revalidate_wait > .
Tag VFS deletable in < devfs_mk_symlink > if handle ignored .
v1 .10
20020129 Richard Gooch < rgooch @ atnf . csiro . au >
Added KERN_ * to remaining messages .
Cleaned up declaration of < stat_read > .
v1 .11
20020219 Richard Gooch < rgooch @ atnf . csiro . au >
Changed < devfs_rmdir > to allow later additions if not yet empty .
v1 .12
20020406 Richard Gooch < rgooch @ atnf . csiro . au >
Removed silently introduced calls to lock_kernel ( ) and
unlock_kernel ( ) due to recent VFS locking changes . BKL isn ' t
required in devfs .
v1 .13
20020428 Richard Gooch < rgooch @ atnf . csiro . au >
Removed 2.4 . x compatibility code .
v1 .14
20020510 Richard Gooch < rgooch @ atnf . csiro . au >
Added BKL to < devfs_open > because drivers still need it .
v1 .15
20020512 Richard Gooch < rgooch @ atnf . csiro . au >
Protected < scan_dir_for_removable > and < get_removable_partition >
from changing directory contents .
v1 .16
20020514 Richard Gooch < rgooch @ atnf . csiro . au >
Minor cleanup of < scan_dir_for_removable > .
v1 .17
20020721 Richard Gooch < rgooch @ atnf . csiro . au >
Switched to ISO C structure field initialisers .
Switch to set_current_state ( ) and move before add_wait_queue ( ) .
20020722 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed devfs entry leak in < devfs_readdir > when * readdir fails .
v1 .18
20020725 Richard Gooch < rgooch @ atnf . csiro . au >
Created < devfs_find_and_unregister > .
v1 .19
20020728 Richard Gooch < rgooch @ atnf . csiro . au >
Removed deprecated < devfs_find_handle > .
v1 .20
20020820 Richard Gooch < rgooch @ atnf . csiro . au >
Fixed module unload race in < devfs_open > .
v1 .21
20021013 Richard Gooch < rgooch @ atnf . csiro . au >
Removed DEVFS_ FL_AUTO_OWNER .
Switched lingering structure field initialiser to ISO C .
Added locking when updating FCB flags .
v1 .22
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/time.h>
# include <linux/tty.h>
# include <linux/timer.h>
# include <linux/config.h>
# include <linux/kernel.h>
# include <linux/wait.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/ctype.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/devfs_fs.h>
# include <linux/devfs_fs_kernel.h>
# include <linux/smp_lock.h>
# include <linux/smp.h>
# include <linux/rwsem.h>
# include <linux/sched.h>
# include <linux/namei.h>
# include <linux/bitops.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include <asm/processor.h>
# include <asm/system.h>
# include <asm/pgtable.h>
# include <asm/atomic.h>
# define DEVFS_VERSION "2004-01-31"
# define DEVFS_NAME "devfs"
# define FIRST_INODE 1
# define STRING_LENGTH 256
# define FAKE_BLOCK_SIZE 1024
# define POISON_PTR ( *(void **) poison_array )
# define MAGIC_VALUE 0x327db823
# ifndef TRUE
# define TRUE 1
# define FALSE 0
# endif
# define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO)
# define DEBUG_NONE 0x0000000
# define DEBUG_MODULE_LOAD 0x0000001
# define DEBUG_REGISTER 0x0000002
# define DEBUG_UNREGISTER 0x0000004
# define DEBUG_FREE 0x0000008
# define DEBUG_SET_FLAGS 0x0000010
# define DEBUG_S_READ 0x0000100 /* Break */
# define DEBUG_I_LOOKUP 0x0001000 /* Break */
# define DEBUG_I_CREATE 0x0002000
# define DEBUG_I_GET 0x0004000
# define DEBUG_I_CHANGE 0x0008000
# define DEBUG_I_UNLINK 0x0010000
# define DEBUG_I_RLINK 0x0020000
# define DEBUG_I_FLINK 0x0040000
# define DEBUG_I_MKNOD 0x0080000
# define DEBUG_F_READDIR 0x0100000 /* Break */
# define DEBUG_D_DELETE 0x1000000 /* Break */
# define DEBUG_D_RELEASE 0x2000000
# define DEBUG_D_IPUT 0x4000000
# define DEBUG_ALL 0xfffffff
# define DEBUG_DISABLED DEBUG_NONE
# define OPTION_NONE 0x00
# define OPTION_MOUNT 0x01
# define PRINTK(format, args...) \
{ printk ( KERN_ERR " %s " format , __FUNCTION__ , # # args ) ; }
# define OOPS(format, args...) \
{ printk ( KERN_CRIT " %s " format , __FUNCTION__ , # # args ) ; \
printk ( " Forcing Oops \n " ) ; \
BUG ( ) ; }
# ifdef CONFIG_DEVFS_DEBUG
# define VERIFY_ENTRY(de) \
{ if ( ( de ) & & ( de ) - > magic_number ! = MAGIC_VALUE ) \
OOPS ( " (%p): bad magic value: %x \n " , ( de ) , ( de ) - > magic_number ) ; }
# define WRITE_ENTRY_MAGIC(de,magic) (de)->magic_number = (magic)
# define DPRINTK(flag, format, args...) \
{ if ( devfs_debug & flag ) \
printk ( KERN_INFO " %s " format , __FUNCTION__ , # # args ) ; }
# else
# define VERIFY_ENTRY(de)
# define WRITE_ENTRY_MAGIC(de,magic)
# define DPRINTK(flag, format, args...)
# endif
typedef struct devfs_entry * devfs_handle_t ;
struct directory_type {
rwlock_t lock ; /* Lock for searching(R)/updating(W) */
struct devfs_entry * first ;
struct devfs_entry * last ;
unsigned char no_more_additions : 1 ;
} ;
struct symlink_type {
unsigned int length ; /* Not including the NULL-termimator */
char * linkname ; /* This is NULL-terminated */
} ;
struct devfs_inode { /* This structure is for "persistent" inode storage */
struct dentry * dentry ;
struct timespec atime ;
struct timespec mtime ;
struct timespec ctime ;
unsigned int ino ; /* Inode number as seen in the VFS */
uid_t uid ;
gid_t gid ;
} ;
struct devfs_entry {
# ifdef CONFIG_DEVFS_DEBUG
unsigned int magic_number ;
# endif
void * info ;
atomic_t refcount ; /* When this drops to zero, it's unused */
union {
struct directory_type dir ;
dev_t dev ;
struct symlink_type symlink ;
const char * name ; /* Only used for (mode == 0) */
} u ;
struct devfs_entry * prev ; /* Previous entry in the parent directory */
struct devfs_entry * next ; /* Next entry in the parent directory */
struct devfs_entry * parent ; /* The parent directory */
struct devfs_inode inode ;
umode_t mode ;
unsigned short namelen ; /* I think 64k+ filenames are a way off... */
unsigned char vfs : 1 ; /* Whether the VFS may delete the entry */
char name [ 1 ] ; /* This is just a dummy: the allocated array
is bigger . This is NULL - terminated */
} ;
/* The root of the device tree */
static struct devfs_entry * root_entry ;
struct devfsd_buf_entry {
struct devfs_entry * de ; /* The name is generated with this */
unsigned short type ; /* The type of event */
umode_t mode ;
uid_t uid ;
gid_t gid ;
struct devfsd_buf_entry * next ;
} ;
struct fs_info { /* This structure is for the mounted devfs */
struct super_block * sb ;
spinlock_t devfsd_buffer_lock ; /* Lock when inserting/deleting events */
struct devfsd_buf_entry * devfsd_first_event ;
struct devfsd_buf_entry * devfsd_last_event ;
volatile int devfsd_sleeping ;
volatile struct task_struct * devfsd_task ;
volatile pid_t devfsd_pgrp ;
volatile struct file * devfsd_file ;
struct devfsd_notify_struct * devfsd_info ;
volatile unsigned long devfsd_event_mask ;
atomic_t devfsd_overrun_count ;
wait_queue_head_t devfsd_wait_queue ; /* Wake devfsd on input */
wait_queue_head_t revalidate_wait_queue ; /* Wake when devfsd sleeps */
} ;
static struct fs_info fs_info = { . devfsd_buffer_lock = SPIN_LOCK_UNLOCKED } ;
static kmem_cache_t * devfsd_buf_cache ;
# ifdef CONFIG_DEVFS_DEBUG
static unsigned int devfs_debug_init __initdata = DEBUG_NONE ;
static unsigned int devfs_debug = DEBUG_NONE ;
static DEFINE_SPINLOCK ( stat_lock ) ;
static unsigned int stat_num_entries ;
static unsigned int stat_num_bytes ;
# endif
static unsigned char poison_array [ 8 ] =
{ 0x5a , 0x5a , 0x5a , 0x5a , 0x5a , 0x5a , 0x5a , 0x5a } ;
# ifdef CONFIG_DEVFS_MOUNT
static unsigned int boot_options = OPTION_MOUNT ;
# else
static unsigned int boot_options = OPTION_NONE ;
# endif
/* Forward function declarations */
static devfs_handle_t _devfs_walk_path ( struct devfs_entry * dir ,
const char * name , int namelen ,
int traverse_symlink ) ;
static ssize_t devfsd_read ( struct file * file , char __user * buf , size_t len ,
loff_t * ppos ) ;
static int devfsd_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg ) ;
static int devfsd_close ( struct inode * inode , struct file * file ) ;
# ifdef CONFIG_DEVFS_DEBUG
static ssize_t stat_read ( struct file * file , char __user * buf , size_t len ,
loff_t * ppos ) ;
static struct file_operations stat_fops = {
. open = nonseekable_open ,
. read = stat_read ,
} ;
# endif
/* Devfs daemon file operations */
static struct file_operations devfsd_fops = {
. open = nonseekable_open ,
. read = devfsd_read ,
. ioctl = devfsd_ioctl ,
. release = devfsd_close ,
} ;
/* Support functions follow */
/**
* devfs_get - Get a reference to a devfs entry .
* @ de : The devfs entry .
*/
static struct devfs_entry * devfs_get ( struct devfs_entry * de )
{
VERIFY_ENTRY ( de ) ;
if ( de )
atomic_inc ( & de - > refcount ) ;
return de ;
} /* End Function devfs_get */
/**
* devfs_put - Put ( release ) a reference to a devfs entry .
* @ de : The handle to the devfs entry .
*/
static void devfs_put ( devfs_handle_t de )
{
if ( ! de )
return ;
VERIFY_ENTRY ( de ) ;
if ( de - > info = = POISON_PTR )
OOPS ( " (%p): poisoned pointer \n " , de ) ;
if ( ! atomic_dec_and_test ( & de - > refcount ) )
return ;
if ( de = = root_entry )
OOPS ( " (%p): root entry being freed \n " , de ) ;
DPRINTK ( DEBUG_FREE , " (%s): de: %p, parent: %p \" %s \" \n " ,
de - > name , de , de - > parent ,
de - > parent ? de - > parent - > name : " no parent " ) ;
if ( S_ISLNK ( de - > mode ) )
kfree ( de - > u . symlink . linkname ) ;
WRITE_ENTRY_MAGIC ( de , 0 ) ;
# ifdef CONFIG_DEVFS_DEBUG
spin_lock ( & stat_lock ) ;
- - stat_num_entries ;
stat_num_bytes - = sizeof * de + de - > namelen ;
if ( S_ISLNK ( de - > mode ) )
stat_num_bytes - = de - > u . symlink . length + 1 ;
spin_unlock ( & stat_lock ) ;
# endif
de - > info = POISON_PTR ;
kfree ( de ) ;
} /* End Function devfs_put */
/**
* _devfs_search_dir - Search for a devfs entry in a directory .
* @ dir : The directory to search .
* @ name : The name of the entry to search for .
* @ namelen : The number of characters in @ name .
*
* Search for a devfs entry in a directory and returns a pointer to the entry
* on success , else % NULL . The directory must be locked already .
* An implicit devfs_get ( ) is performed on the returned entry .
*/
static struct devfs_entry * _devfs_search_dir ( struct devfs_entry * dir ,
const char * name ,
unsigned int namelen )
{
struct devfs_entry * curr ;
if ( ! S_ISDIR ( dir - > mode ) ) {
PRINTK ( " (%s): not a directory \n " , dir - > name ) ;
return NULL ;
}
for ( curr = dir - > u . dir . first ; curr ! = NULL ; curr = curr - > next ) {
if ( curr - > namelen ! = namelen )
continue ;
if ( memcmp ( curr - > name , name , namelen ) = = 0 )
break ;
/* Not found: try the next one */
}
return devfs_get ( curr ) ;
} /* End Function _devfs_search_dir */
/**
* _devfs_alloc_entry - Allocate a devfs entry .
* @ name : the name of the entry
* @ namelen : the number of characters in @ name
* @ mode : the mode for the entry
*
* Allocate a devfs entry and returns a pointer to the entry on success , else
* % NULL .
*/
static struct devfs_entry * _devfs_alloc_entry ( const char * name ,
unsigned int namelen ,
umode_t mode )
{
struct devfs_entry * new ;
static unsigned long inode_counter = FIRST_INODE ;
static DEFINE_SPINLOCK ( counter_lock ) ;
if ( name & & ( namelen < 1 ) )
namelen = strlen ( name ) ;
if ( ( new = kmalloc ( sizeof * new + namelen , GFP_KERNEL ) ) = = NULL )
return NULL ;
memset ( new , 0 , sizeof * new + namelen ) ; /* Will set '\0' on name */
new - > mode = mode ;
if ( S_ISDIR ( mode ) )
rwlock_init ( & new - > u . dir . lock ) ;
atomic_set ( & new - > refcount , 1 ) ;
spin_lock ( & counter_lock ) ;
new - > inode . ino = inode_counter + + ;
spin_unlock ( & counter_lock ) ;
if ( name )
memcpy ( new - > name , name , namelen ) ;
new - > namelen = namelen ;
WRITE_ENTRY_MAGIC ( new , MAGIC_VALUE ) ;
# ifdef CONFIG_DEVFS_DEBUG
spin_lock ( & stat_lock ) ;
+ + stat_num_entries ;
stat_num_bytes + = sizeof * new + namelen ;
spin_unlock ( & stat_lock ) ;
# endif
return new ;
} /* End Function _devfs_alloc_entry */
/**
* _devfs_append_entry - Append a devfs entry to a directory ' s child list .
* @ dir : The directory to add to .
* @ de : The devfs entry to append .
* @ old_de : If an existing entry exists , it will be written here . This may
* be % NULL . An implicit devfs_get ( ) is performed on this entry .
*
* Append a devfs entry to a directory ' s list of children , checking first to
* see if an entry of the same name exists . The directory will be locked .
* The value 0 is returned on success , else a negative error code .
* On failure , an implicit devfs_put ( ) is performed on % de .
*/
static int _devfs_append_entry ( devfs_handle_t dir , devfs_handle_t de ,
devfs_handle_t * old_de )
{
int retval ;
if ( old_de )
* old_de = NULL ;
if ( ! S_ISDIR ( dir - > mode ) ) {
PRINTK ( " (%s): dir: \" %s \" is not a directory \n " , de - > name ,
dir - > name ) ;
devfs_put ( de ) ;
return - ENOTDIR ;
}
write_lock ( & dir - > u . dir . lock ) ;
if ( dir - > u . dir . no_more_additions )
retval = - ENOENT ;
else {
struct devfs_entry * old ;
old = _devfs_search_dir ( dir , de - > name , de - > namelen ) ;
if ( old_de )
* old_de = old ;
else
devfs_put ( old ) ;
if ( old = = NULL ) {
de - > parent = dir ;
de - > prev = dir - > u . dir . last ;
/* Append to the directory's list of children */
if ( dir - > u . dir . first = = NULL )
dir - > u . dir . first = de ;
else
dir - > u . dir . last - > next = de ;
dir - > u . dir . last = de ;
retval = 0 ;
} else
retval = - EEXIST ;
}
write_unlock ( & dir - > u . dir . lock ) ;
if ( retval )
devfs_put ( de ) ;
return retval ;
} /* End Function _devfs_append_entry */
/**
* _devfs_get_root_entry - Get the root devfs entry .
*
* Returns the root devfs entry on success , else % NULL .
*
* TODO it must be called asynchronously due to the fact
* that devfs is initialized relatively late . Proper way
* is to remove module_init from init_devfs_fs and manually
* call it early enough during system init
*/
static struct devfs_entry * _devfs_get_root_entry ( void )
{
struct devfs_entry * new ;
static DEFINE_SPINLOCK ( root_lock ) ;
if ( root_entry )
return root_entry ;
new = _devfs_alloc_entry ( NULL , 0 , MODE_DIR ) ;
if ( new = = NULL )
return NULL ;
spin_lock ( & root_lock ) ;
if ( root_entry ) {
spin_unlock ( & root_lock ) ;
devfs_put ( new ) ;
return root_entry ;
}
root_entry = new ;
spin_unlock ( & root_lock ) ;
return root_entry ;
} /* End Function _devfs_get_root_entry */
/**
* _devfs_descend - Descend down a tree using the next component name .
* @ dir : The directory to search .
* @ name : The component name to search for .
* @ namelen : The length of % name .
* @ next_pos : The position of the next ' / ' or ' \0 ' is written here .
*
* Descend into a directory , searching for a component . This function forms
* the core of a tree - walking algorithm . The directory will be locked .
* The devfs entry corresponding to the component is returned . If there is
* no matching entry , % NULL is returned .
* An implicit devfs_get ( ) is performed on the returned entry .
*/
static struct devfs_entry * _devfs_descend ( struct devfs_entry * dir ,
const char * name , int namelen ,
int * next_pos )
{
const char * stop , * ptr ;
struct devfs_entry * entry ;
if ( ( namelen > = 3 ) & & ( strncmp ( name , " ../ " , 3 ) = = 0 ) ) { /* Special-case going to parent directory */
* next_pos = 3 ;
return devfs_get ( dir - > parent ) ;
}
stop = name + namelen ;
/* Search for a possible '/' */
for ( ptr = name ; ( ptr < stop ) & & ( * ptr ! = ' / ' ) ; + + ptr ) ;
* next_pos = ptr - name ;
read_lock ( & dir - > u . dir . lock ) ;
entry = _devfs_search_dir ( dir , name , * next_pos ) ;
read_unlock ( & dir - > u . dir . lock ) ;
return entry ;
} /* End Function _devfs_descend */
static devfs_handle_t _devfs_make_parent_for_leaf ( struct devfs_entry * dir ,
const char * name ,
int namelen , int * leaf_pos )
{
int next_pos = 0 ;
if ( dir = = NULL )
dir = _devfs_get_root_entry ( ) ;
if ( dir = = NULL )
return NULL ;
devfs_get ( dir ) ;
/* Search for possible trailing component and ignore it */
for ( - - namelen ; ( namelen > 0 ) & & ( name [ namelen ] ! = ' / ' ) ; - - namelen ) ;
* leaf_pos = ( name [ namelen ] = = ' / ' ) ? ( namelen + 1 ) : 0 ;
for ( ; namelen > 0 ; name + = next_pos , namelen - = next_pos ) {
struct devfs_entry * de , * old = NULL ;
if ( ( de =
_devfs_descend ( dir , name , namelen , & next_pos ) ) = = NULL ) {
de = _devfs_alloc_entry ( name , next_pos , MODE_DIR ) ;
devfs_get ( de ) ;
if ( ! de | | _devfs_append_entry ( dir , de , & old ) ) {
devfs_put ( de ) ;
if ( ! old | | ! S_ISDIR ( old - > mode ) ) {
devfs_put ( old ) ;
devfs_put ( dir ) ;
return NULL ;
}
de = old ; /* Use the existing directory */
}
}
if ( de = = dir - > parent ) {
devfs_put ( dir ) ;
devfs_put ( de ) ;
return NULL ;
}
devfs_put ( dir ) ;
dir = de ;
if ( name [ next_pos ] = = ' / ' )
+ + next_pos ;
}
return dir ;
} /* End Function _devfs_make_parent_for_leaf */
static devfs_handle_t _devfs_prepare_leaf ( devfs_handle_t * dir ,
const char * name , umode_t mode )
{
int namelen , leaf_pos ;
struct devfs_entry * de ;
namelen = strlen ( name ) ;
if ( ( * dir = _devfs_make_parent_for_leaf ( * dir , name , namelen ,
& leaf_pos ) ) = = NULL ) {
PRINTK ( " (%s): could not create parent path \n " , name ) ;
return NULL ;
}
if ( ( de = _devfs_alloc_entry ( name + leaf_pos , namelen - leaf_pos , mode ) )
= = NULL ) {
PRINTK ( " (%s): could not allocate entry \n " , name ) ;
devfs_put ( * dir ) ;
return NULL ;
}
return de ;
} /* End Function _devfs_prepare_leaf */
static devfs_handle_t _devfs_walk_path ( struct devfs_entry * dir ,
const char * name , int namelen ,
int traverse_symlink )
{
int next_pos = 0 ;
if ( dir = = NULL )
dir = _devfs_get_root_entry ( ) ;
if ( dir = = NULL )
return NULL ;
devfs_get ( dir ) ;
for ( ; namelen > 0 ; name + = next_pos , namelen - = next_pos ) {
struct devfs_entry * de , * link ;
if ( ! S_ISDIR ( dir - > mode ) ) {
devfs_put ( dir ) ;
return NULL ;
}
if ( ( de =
_devfs_descend ( dir , name , namelen , & next_pos ) ) = = NULL ) {
devfs_put ( dir ) ;
return NULL ;
}
if ( S_ISLNK ( de - > mode ) & & traverse_symlink ) { /* Need to follow the link: this is a stack chomper */
/* FIXME what if it puts outside of mounted tree? */
link = _devfs_walk_path ( dir , de - > u . symlink . linkname ,
de - > u . symlink . length , TRUE ) ;
devfs_put ( de ) ;
if ( ! link ) {
devfs_put ( dir ) ;
return NULL ;
}
de = link ;
}
devfs_put ( dir ) ;
dir = de ;
if ( name [ next_pos ] = = ' / ' )
+ + next_pos ;
}
return dir ;
} /* End Function _devfs_walk_path */
/**
* _devfs_find_entry - Find a devfs entry .
* @ dir : The handle to the parent devfs directory entry . If this is % NULL the
* name is relative to the root of the devfs .
* @ name : The name of the entry . This may be % NULL .
* @ traverse_symlink : If % TRUE then symbolic links are traversed .
*
* Returns the devfs_entry pointer on success , else % NULL . An implicit
* devfs_get ( ) is performed .
*/
static struct devfs_entry * _devfs_find_entry ( devfs_handle_t dir ,
const char * name ,
int traverse_symlink )
{
unsigned int namelen = strlen ( name ) ;
if ( name [ 0 ] = = ' / ' ) {
/* Skip leading pathname component */
if ( namelen < 2 ) {
PRINTK ( " (%s): too short \n " , name ) ;
return NULL ;
}
for ( + + name , - - namelen ; ( * name ! = ' / ' ) & & ( namelen > 0 ) ;
+ + name , - - namelen ) ;
if ( namelen < 2 ) {
PRINTK ( " (%s): too short \n " , name ) ;
return NULL ;
}
+ + name ;
- - namelen ;
}
return _devfs_walk_path ( dir , name , namelen , traverse_symlink ) ;
} /* End Function _devfs_find_entry */
static struct devfs_entry * get_devfs_entry_from_vfs_inode ( struct inode * inode )
{
if ( inode = = NULL )
return NULL ;
VERIFY_ENTRY ( ( struct devfs_entry * ) inode - > u . generic_ip ) ;
return inode - > u . generic_ip ;
} /* End Function get_devfs_entry_from_vfs_inode */
/**
* free_dentry - Free the dentry for a device entry and invalidate inode .
* @ de : The entry .
*
* This must only be called after the entry has been unhooked from its
* parent directory .
*/
static void free_dentry ( struct devfs_entry * de )
{
struct dentry * dentry = de - > inode . dentry ;
if ( ! dentry )
return ;
spin_lock ( & dcache_lock ) ;
dget_locked ( dentry ) ;
spin_unlock ( & dcache_lock ) ;
/* Forcefully remove the inode */
if ( dentry - > d_inode ! = NULL )
dentry - > d_inode - > i_nlink = 0 ;
d_drop ( dentry ) ;
dput ( dentry ) ;
} /* End Function free_dentry */
/**
* is_devfsd_or_child - Test if the current process is devfsd or one of its children .
* @ fs_info : The filesystem information .
*
* Returns % TRUE if devfsd or child , else % FALSE .
*/
static int is_devfsd_or_child ( struct fs_info * fs_info )
{
struct task_struct * p = current ;
if ( p = = fs_info - > devfsd_task )
return ( TRUE ) ;
if ( process_group ( p ) = = fs_info - > devfsd_pgrp )
return ( TRUE ) ;
read_lock ( & tasklist_lock ) ;
for ( ; p ! = & init_task ; p = p - > real_parent ) {
if ( p = = fs_info - > devfsd_task ) {
read_unlock ( & tasklist_lock ) ;
return ( TRUE ) ;
}
}
read_unlock ( & tasklist_lock ) ;
return ( FALSE ) ;
} /* End Function is_devfsd_or_child */
/**
* devfsd_queue_empty - Test if devfsd has work pending in its event queue .
* @ fs_info : The filesystem information .
*
* Returns % TRUE if the queue is empty , else % FALSE .
*/
static inline int devfsd_queue_empty ( struct fs_info * fs_info )
{
return ( fs_info - > devfsd_last_event ) ? FALSE : TRUE ;
} /* End Function devfsd_queue_empty */
/**
* wait_for_devfsd_finished - Wait for devfsd to finish processing its event queue .
* @ fs_info : The filesystem information .
*
* Returns % TRUE if no more waiting will be required , else % FALSE .
*/
static int wait_for_devfsd_finished ( struct fs_info * fs_info )
{
DECLARE_WAITQUEUE ( wait , current ) ;
if ( fs_info - > devfsd_task = = NULL )
return ( TRUE ) ;
if ( devfsd_queue_empty ( fs_info ) & & fs_info - > devfsd_sleeping )
return TRUE ;
if ( is_devfsd_or_child ( fs_info ) )
return ( FALSE ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & fs_info - > revalidate_wait_queue , & wait ) ;
if ( ! devfsd_queue_empty ( fs_info ) | | ! fs_info - > devfsd_sleeping )
if ( fs_info - > devfsd_task )
schedule ( ) ;
remove_wait_queue ( & fs_info - > revalidate_wait_queue , & wait ) ;
__set_current_state ( TASK_RUNNING ) ;
return ( TRUE ) ;
} /* End Function wait_for_devfsd_finished */
/**
* devfsd_notify_de - Notify the devfsd daemon of a change .
* @ de : The devfs entry that has changed . This and all parent entries will
* have their reference counts incremented if the event was queued .
* @ type : The type of change .
* @ mode : The mode of the entry .
* @ uid : The user ID .
* @ gid : The group ID .
* @ fs_info : The filesystem info .
*
* Returns % TRUE if an event was queued and devfsd woken up , else % FALSE .
*/
static int devfsd_notify_de ( struct devfs_entry * de ,
unsigned short type , umode_t mode ,
uid_t uid , gid_t gid , struct fs_info * fs_info )
{
struct devfsd_buf_entry * entry ;
struct devfs_entry * curr ;
if ( ! ( fs_info - > devfsd_event_mask & ( 1 < < type ) ) )
return ( FALSE ) ;
if ( ( entry = kmem_cache_alloc ( devfsd_buf_cache , SLAB_KERNEL ) ) = = NULL ) {
atomic_inc ( & fs_info - > devfsd_overrun_count ) ;
return ( FALSE ) ;
}
for ( curr = de ; curr ! = NULL ; curr = curr - > parent )
devfs_get ( curr ) ;
entry - > de = de ;
entry - > type = type ;
entry - > mode = mode ;
entry - > uid = uid ;
entry - > gid = gid ;
entry - > next = NULL ;
spin_lock ( & fs_info - > devfsd_buffer_lock ) ;
if ( ! fs_info - > devfsd_first_event )
fs_info - > devfsd_first_event = entry ;
if ( fs_info - > devfsd_last_event )
fs_info - > devfsd_last_event - > next = entry ;
fs_info - > devfsd_last_event = entry ;
spin_unlock ( & fs_info - > devfsd_buffer_lock ) ;
wake_up_interruptible ( & fs_info - > devfsd_wait_queue ) ;
return ( TRUE ) ;
} /* End Function devfsd_notify_de */
/**
* devfsd_notify - Notify the devfsd daemon of a change .
* @ de : The devfs entry that has changed .
* @ type : The type of change event .
* @ wait : If TRUE , the function waits for the daemon to finish processing
* the event .
*/
static void devfsd_notify ( struct devfs_entry * de , unsigned short type )
{
devfsd_notify_de ( de , type , de - > mode , current - > euid ,
current - > egid , & fs_info ) ;
}
static int devfs_mk_dev ( dev_t dev , umode_t mode , const char * fmt , va_list args )
{
struct devfs_entry * dir = NULL , * de ;
char buf [ 64 ] ;
int error , n ;
n = vsnprintf ( buf , sizeof ( buf ) , fmt , args ) ;
if ( n > = sizeof ( buf ) | | ! buf [ 0 ] ) {
printk ( KERN_WARNING " %s: invalid format string %s \n " ,
__FUNCTION__ , fmt ) ;
return - EINVAL ;
}
de = _devfs_prepare_leaf ( & dir , buf , mode ) ;
if ( ! de ) {
printk ( KERN_WARNING " %s: could not prepare leaf for %s \n " ,
__FUNCTION__ , buf ) ;
return - ENOMEM ; /* could be more accurate... */
}
de - > u . dev = dev ;
error = _devfs_append_entry ( dir , de , NULL ) ;
if ( error ) {
printk ( KERN_WARNING " %s: could not append to parent for %s \n " ,
__FUNCTION__ , buf ) ;
goto out ;
}
devfsd_notify ( de , DEVFSD_NOTIFY_REGISTERED ) ;
out :
devfs_put ( dir ) ;
return error ;
}
int devfs_mk_bdev ( dev_t dev , umode_t mode , const char * fmt , . . . )
{
va_list args ;
if ( ! S_ISBLK ( mode ) ) {
printk ( KERN_WARNING " %s: invalide mode (%u) for %s \n " ,
__FUNCTION__ , mode , fmt ) ;
return - EINVAL ;
}
va_start ( args , fmt ) ;
return devfs_mk_dev ( dev , mode , fmt , args ) ;
}
EXPORT_SYMBOL ( devfs_mk_bdev ) ;
int devfs_mk_cdev ( dev_t dev , umode_t mode , const char * fmt , . . . )
{
va_list args ;
if ( ! S_ISCHR ( mode ) ) {
printk ( KERN_WARNING " %s: invalide mode (%u) for %s \n " ,
__FUNCTION__ , mode , fmt ) ;
return - EINVAL ;
}
va_start ( args , fmt ) ;
return devfs_mk_dev ( dev , mode , fmt , args ) ;
}
EXPORT_SYMBOL ( devfs_mk_cdev ) ;
/**
* _devfs_unhook - Unhook a device entry from its parents list
* @ de : The entry to unhook .
*
* Returns % TRUE if the entry was unhooked , else % FALSE if it was
* previously unhooked .
* The caller must have a write lock on the parent directory .
*/
static int _devfs_unhook ( struct devfs_entry * de )
{
struct devfs_entry * parent ;
if ( ! de | | ( de - > prev = = de ) )
return FALSE ;
parent = de - > parent ;
if ( de - > prev = = NULL )
parent - > u . dir . first = de - > next ;
else
de - > prev - > next = de - > next ;
if ( de - > next = = NULL )
parent - > u . dir . last = de - > prev ;
else
de - > next - > prev = de - > prev ;
de - > prev = de ; /* Indicate we're unhooked */
de - > next = NULL ; /* Force early termination for <devfs_readdir> */
return TRUE ;
} /* End Function _devfs_unhook */
/**
* _devfs_unregister - Unregister a device entry from its parent .
* @ dir : The parent directory .
* @ de : The entry to unregister .
*
* The caller must have a write lock on the parent directory , which is
* unlocked by this function .
*/
static void _devfs_unregister ( struct devfs_entry * dir , struct devfs_entry * de )
{
int unhooked = _devfs_unhook ( de ) ;
write_unlock ( & dir - > u . dir . lock ) ;
if ( ! unhooked )
return ;
devfs_get ( dir ) ;
devfsd_notify ( de , DEVFSD_NOTIFY_UNREGISTERED ) ;
free_dentry ( de ) ;
devfs_put ( dir ) ;
if ( ! S_ISDIR ( de - > mode ) )
return ;
while ( TRUE ) { /* Recursively unregister: this is a stack chomper */
struct devfs_entry * child ;
write_lock ( & de - > u . dir . lock ) ;
de - > u . dir . no_more_additions = TRUE ;
child = de - > u . dir . first ;
VERIFY_ENTRY ( child ) ;
_devfs_unregister ( de , child ) ;
if ( ! child )
break ;
DPRINTK ( DEBUG_UNREGISTER , " (%s): child: %p refcount: %d \n " ,
child - > name , child , atomic_read ( & child - > refcount ) ) ;
devfs_put ( child ) ;
}
} /* End Function _devfs_unregister */
static int devfs_do_symlink ( devfs_handle_t dir , const char * name ,
const char * link , devfs_handle_t * handle )
{
int err ;
unsigned int linklength ;
char * newlink ;
struct devfs_entry * de ;
if ( handle ! = NULL )
* handle = NULL ;
if ( name = = NULL ) {
PRINTK ( " (): NULL name pointer \n " ) ;
return - EINVAL ;
}
if ( link = = NULL ) {
PRINTK ( " (%s): NULL link pointer \n " , name ) ;
return - EINVAL ;
}
linklength = strlen ( link ) ;
if ( ( newlink = kmalloc ( linklength + 1 , GFP_KERNEL ) ) = = NULL )
return - ENOMEM ;
memcpy ( newlink , link , linklength ) ;
newlink [ linklength ] = ' \0 ' ;
if ( ( de = _devfs_prepare_leaf ( & dir , name , S_IFLNK | S_IRUGO | S_IXUGO ) )
= = NULL ) {
PRINTK ( " (%s): could not prepare leaf \n " , name ) ;
kfree ( newlink ) ;
return - ENOTDIR ;
}
de - > info = NULL ;
de - > u . symlink . linkname = newlink ;
de - > u . symlink . length = linklength ;
if ( ( err = _devfs_append_entry ( dir , de , NULL ) ) ! = 0 ) {
PRINTK ( " (%s): could not append to parent, err: %d \n " , name ,
err ) ;
devfs_put ( dir ) ;
return err ;
}
devfs_put ( dir ) ;
# ifdef CONFIG_DEVFS_DEBUG
spin_lock ( & stat_lock ) ;
stat_num_bytes + = linklength + 1 ;
spin_unlock ( & stat_lock ) ;
# endif
if ( handle ! = NULL )
* handle = de ;
return 0 ;
} /* End Function devfs_do_symlink */
/**
* devfs_mk_symlink Create a symbolic link in the devfs namespace .
* @ from : The name of the entry .
* @ to : Name of the destination
*
* Returns 0 on success , else a negative error code is returned .
*/
int devfs_mk_symlink ( const char * from , const char * to )
{
devfs_handle_t de ;
int err ;
err = devfs_do_symlink ( NULL , from , to , & de ) ;
if ( ! err ) {
de - > vfs = TRUE ;
devfsd_notify ( de , DEVFSD_NOTIFY_REGISTERED ) ;
}
return err ;
}
/**
* devfs_mk_dir - Create a directory in the devfs namespace .
* new name is relative to the root of the devfs .
* @ fmt : The name of the entry .
*
* Use of this function is optional . The devfs_register ( ) function
* will automatically create intermediate directories as needed . This function
* is provided for efficiency reasons , as it provides a handle to a directory .
* On failure % NULL is returned .
*/
int devfs_mk_dir ( const char * fmt , . . . )
{
struct devfs_entry * dir = NULL , * de = NULL , * old ;
char buf [ 64 ] ;
va_list args ;
int error , n ;
va_start ( args , fmt ) ;
n = vsnprintf ( buf , 64 , fmt , args ) ;
if ( n > = 64 | | ! buf [ 0 ] ) {
printk ( KERN_WARNING " %s: invalid argument. " , __FUNCTION__ ) ;
return - EINVAL ;
}
de = _devfs_prepare_leaf ( & dir , buf , MODE_DIR ) ;
if ( ! de ) {
PRINTK ( " (%s): could not prepare leaf \n " , buf ) ;
return - EINVAL ;
}
error = _devfs_append_entry ( dir , de , & old ) ;
if ( error = = - EEXIST & & S_ISDIR ( old - > mode ) ) {
/*
* devfs_mk_dir ( ) of an already - existing directory will
* return success .
*/
error = 0 ;
goto out_put ;
} else if ( error ) {
PRINTK ( " (%s): could not append to dir: %p \" %s \" \n " ,
buf , dir , dir - > name ) ;
devfs_put ( old ) ;
goto out_put ;
}
devfsd_notify ( de , DEVFSD_NOTIFY_REGISTERED ) ;
out_put :
devfs_put ( dir ) ;
return error ;
}
void devfs_remove ( const char * fmt , . . . )
{
char buf [ 64 ] ;
va_list args ;
int n ;
va_start ( args , fmt ) ;
n = vsnprintf ( buf , sizeof ( buf ) , fmt , args ) ;
if ( n < sizeof ( buf ) & & buf [ 0 ] ) {
devfs_handle_t de = _devfs_find_entry ( NULL , buf , 0 ) ;
if ( ! de ) {
printk ( KERN_ERR " %s: %s not found, cannot remove \n " ,
__FUNCTION__ , buf ) ;
dump_stack ( ) ;
return ;
}
write_lock ( & de - > parent - > u . dir . lock ) ;
_devfs_unregister ( de - > parent , de ) ;
devfs_put ( de ) ;
devfs_put ( de ) ;
}
}
/**
* devfs_generate_path - Generate a pathname for an entry , relative to the devfs root .
* @ de : The devfs entry .
* @ path : The buffer to write the pathname to . The pathname and ' \0 '
* terminator will be written at the end of the buffer .
* @ buflen : The length of the buffer .
*
* Returns the offset in the buffer where the pathname starts on success ,
* else a negative error code .
*/
static int devfs_generate_path ( devfs_handle_t de , char * path , int buflen )
{
int pos ;
# define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name )
if ( de = = NULL )
return - EINVAL ;
VERIFY_ENTRY ( de ) ;
if ( de - > namelen > = buflen )
return - ENAMETOOLONG ; /* Must be first */
path [ buflen - 1 ] = ' \0 ' ;
if ( de - > parent = = NULL )
return buflen - 1 ; /* Don't prepend root */
pos = buflen - de - > namelen - 1 ;
memcpy ( path + pos , NAMEOF ( de ) , de - > namelen ) ;
for ( de = de - > parent ; de - > parent ! = NULL ; de = de - > parent ) {
if ( pos - de - > namelen - 1 < 0 )
return - ENAMETOOLONG ;
path [ - - pos ] = ' / ' ;
pos - = de - > namelen ;
memcpy ( path + pos , NAMEOF ( de ) , de - > namelen ) ;
}
return pos ;
} /* End Function devfs_generate_path */
/**
* devfs_setup - Process kernel boot options .
* @ str : The boot options after the " devfs= " .
*/
static int __init devfs_setup ( char * str )
{
static struct {
char * name ;
unsigned int mask ;
unsigned int * opt ;
} devfs_options_tab [ ] __initdata = {
# ifdef CONFIG_DEVFS_DEBUG
{
" dall " , DEBUG_ALL , & devfs_debug_init } , {
" dmod " , DEBUG_MODULE_LOAD , & devfs_debug_init } , {
" dreg " , DEBUG_REGISTER , & devfs_debug_init } , {
" dunreg " , DEBUG_UNREGISTER , & devfs_debug_init } , {
" dfree " , DEBUG_FREE , & devfs_debug_init } , {
" diget " , DEBUG_I_GET , & devfs_debug_init } , {
" dchange " , DEBUG_SET_FLAGS , & devfs_debug_init } , {
" dsread " , DEBUG_S_READ , & devfs_debug_init } , {
" dichange " , DEBUG_I_CHANGE , & devfs_debug_init } , {
" dimknod " , DEBUG_I_MKNOD , & devfs_debug_init } , {
" dilookup " , DEBUG_I_LOOKUP , & devfs_debug_init } , {
" diunlink " , DEBUG_I_UNLINK , & devfs_debug_init } ,
# endif /* CONFIG_DEVFS_DEBUG */
{
" mount " , OPTION_MOUNT , & boot_options } , {
NULL , 0 , NULL }
} ;
while ( ( * str ! = ' \0 ' ) & & ! isspace ( * str ) ) {
int i , found = 0 , invert = 0 ;
if ( strncmp ( str , " no " , 2 ) = = 0 ) {
invert = 1 ;
str + = 2 ;
}
for ( i = 0 ; devfs_options_tab [ i ] . name ! = NULL ; i + + ) {
int len = strlen ( devfs_options_tab [ i ] . name ) ;
if ( strncmp ( str , devfs_options_tab [ i ] . name , len ) = = 0 ) {
if ( invert )
* devfs_options_tab [ i ] . opt & =
~ devfs_options_tab [ i ] . mask ;
else
* devfs_options_tab [ i ] . opt | =
devfs_options_tab [ i ] . mask ;
str + = len ;
found = 1 ;
break ;
}
}
if ( ! found )
return 0 ; /* No match */
if ( * str ! = ' , ' )
return 0 ; /* No more options */
+ + str ;
}
return 1 ;
} /* End Function devfs_setup */
__setup ( " devfs= " , devfs_setup ) ;
EXPORT_SYMBOL ( devfs_mk_dir ) ;
EXPORT_SYMBOL ( devfs_remove ) ;
/**
* try_modload - Notify devfsd of an inode lookup by a non - devfsd process .
* @ parent : The parent devfs entry .
* @ fs_info : The filesystem info .
* @ name : The device name .
* @ namelen : The number of characters in @ name .
* @ buf : A working area that will be used . This must not go out of scope
* until devfsd is idle again .
*
* Returns 0 on success ( event was queued ) , else a negative error code .
*/
static int try_modload ( struct devfs_entry * parent , struct fs_info * fs_info ,
const char * name , unsigned namelen ,
struct devfs_entry * buf )
{
if ( ! ( fs_info - > devfsd_event_mask & ( 1 < < DEVFSD_NOTIFY_LOOKUP ) ) )
return - ENOENT ;
if ( is_devfsd_or_child ( fs_info ) )
return - ENOENT ;
memset ( buf , 0 , sizeof * buf ) ;
atomic_set ( & buf - > refcount , 1 ) ;
buf - > parent = parent ;
buf - > namelen = namelen ;
buf - > u . name = name ;
WRITE_ENTRY_MAGIC ( buf , MAGIC_VALUE ) ;
if ( ! devfsd_notify_de ( buf , DEVFSD_NOTIFY_LOOKUP , 0 ,
current - > euid , current - > egid , fs_info ) )
return - ENOENT ;
/* Possible success: event has been queued */
return 0 ;
} /* End Function try_modload */
/* Superblock operations follow */
static struct inode_operations devfs_iops ;
static struct inode_operations devfs_dir_iops ;
static struct file_operations devfs_fops ;
static struct file_operations devfs_dir_fops ;
static struct inode_operations devfs_symlink_iops ;
static int devfs_notify_change ( struct dentry * dentry , struct iattr * iattr )
{
int retval ;
struct devfs_entry * de ;
struct inode * inode = dentry - > d_inode ;
struct fs_info * fs_info = inode - > i_sb - > s_fs_info ;
de = get_devfs_entry_from_vfs_inode ( inode ) ;
if ( de = = NULL )
return - ENODEV ;
retval = inode_change_ok ( inode , iattr ) ;
if ( retval ! = 0 )
return retval ;
retval = inode_setattr ( inode , iattr ) ;
if ( retval ! = 0 )
return retval ;
DPRINTK ( DEBUG_I_CHANGE , " (%d): VFS inode: %p devfs_entry: %p \n " ,
( int ) inode - > i_ino , inode , de ) ;
DPRINTK ( DEBUG_I_CHANGE , " (): mode: 0%o uid: %d gid: %d \n " ,
( int ) inode - > i_mode , ( int ) inode - > i_uid , ( int ) inode - > i_gid ) ;
/* Inode is not on hash chains, thus must save permissions here rather
than in a write_inode ( ) method */
de - > mode = inode - > i_mode ;
de - > inode . uid = inode - > i_uid ;
de - > inode . gid = inode - > i_gid ;
de - > inode . atime = inode - > i_atime ;
de - > inode . mtime = inode - > i_mtime ;
de - > inode . ctime = inode - > i_ctime ;
if ( ( iattr - > ia_valid & ( ATTR_MODE | ATTR_UID | ATTR_GID ) ) & &
! is_devfsd_or_child ( fs_info ) )
devfsd_notify_de ( de , DEVFSD_NOTIFY_CHANGE , inode - > i_mode ,
inode - > i_uid , inode - > i_gid , fs_info ) ;
return 0 ;
} /* End Function devfs_notify_change */
static struct super_operations devfs_sops = {
. drop_inode = generic_delete_inode ,
. statfs = simple_statfs ,
} ;
/**
* _devfs_get_vfs_inode - Get a VFS inode .
* @ sb : The super block .
* @ de : The devfs inode .
* @ dentry : The dentry to register with the devfs inode .
*
* Returns the inode on success , else % NULL . An implicit devfs_get ( ) is
* performed if the inode is created .
*/
static struct inode * _devfs_get_vfs_inode ( struct super_block * sb ,
struct devfs_entry * de ,
struct dentry * dentry )
{
struct inode * inode ;
if ( de - > prev = = de )
return NULL ; /* Quick check to see if unhooked */
if ( ( inode = new_inode ( sb ) ) = = NULL ) {
PRINTK ( " (%s): new_inode() failed, de: %p \n " , de - > name , de ) ;
return NULL ;
}
if ( de - > parent ) {
read_lock ( & de - > parent - > u . dir . lock ) ;
if ( de - > prev ! = de )
de - > inode . dentry = dentry ; /* Not unhooked */
read_unlock ( & de - > parent - > u . dir . lock ) ;
} else
de - > inode . dentry = dentry ; /* Root: no locking needed */
if ( de - > inode . dentry ! = dentry ) { /* Must have been unhooked */
iput ( inode ) ;
return NULL ;
}
/* FIXME where is devfs_put? */
inode - > u . generic_ip = devfs_get ( de ) ;
inode - > i_ino = de - > inode . ino ;
DPRINTK ( DEBUG_I_GET , " (%d): VFS inode: %p devfs_entry: %p \n " ,
( int ) inode - > i_ino , inode , de ) ;
inode - > i_blocks = 0 ;
inode - > i_blksize = FAKE_BLOCK_SIZE ;
inode - > i_op = & devfs_iops ;
inode - > i_mode = de - > mode ;
if ( S_ISDIR ( de - > mode ) ) {
inode - > i_op = & devfs_dir_iops ;
inode - > i_fop = & devfs_dir_fops ;
} else if ( S_ISLNK ( de - > mode ) ) {
inode - > i_op = & devfs_symlink_iops ;
inode - > i_size = de - > u . symlink . length ;
} else if ( S_ISCHR ( de - > mode ) | | S_ISBLK ( de - > mode ) ) {
init_special_inode ( inode , de - > mode , de - > u . dev ) ;
} else if ( S_ISFIFO ( de - > mode ) | | S_ISSOCK ( de - > mode ) ) {
init_special_inode ( inode , de - > mode , 0 ) ;
} else {
PRINTK ( " (%s): unknown mode %o de: %p \n " ,
de - > name , de - > mode , de ) ;
iput ( inode ) ;
devfs_put ( de ) ;
return NULL ;
}
inode - > i_uid = de - > inode . uid ;
inode - > i_gid = de - > inode . gid ;
inode - > i_atime = de - > inode . atime ;
inode - > i_mtime = de - > inode . mtime ;
inode - > i_ctime = de - > inode . ctime ;
DPRINTK ( DEBUG_I_GET , " (): mode: 0%o uid: %d gid: %d \n " ,
( int ) inode - > i_mode , ( int ) inode - > i_uid , ( int ) inode - > i_gid ) ;
return inode ;
} /* End Function _devfs_get_vfs_inode */
/* File operations for device entries follow */
static int devfs_readdir ( struct file * file , void * dirent , filldir_t filldir )
{
int err , count ;
int stored = 0 ;
struct fs_info * fs_info ;
struct devfs_entry * parent , * de , * next = NULL ;
struct inode * inode = file - > f_dentry - > d_inode ;
fs_info = inode - > i_sb - > s_fs_info ;
parent = get_devfs_entry_from_vfs_inode ( file - > f_dentry - > d_inode ) ;
if ( ( long ) file - > f_pos < 0 )
return - EINVAL ;
DPRINTK ( DEBUG_F_READDIR , " (%s): fs_info: %p pos: %ld \n " ,
parent - > name , fs_info , ( long ) file - > f_pos ) ;
switch ( ( long ) file - > f_pos ) {
case 0 :
err = ( * filldir ) ( dirent , " .. " , 2 , file - > f_pos ,
parent_ino ( file - > f_dentry ) , DT_DIR ) ;
if ( err = = - EINVAL )
break ;
if ( err < 0 )
return err ;
file - > f_pos + + ;
+ + stored ;
/* Fall through */
case 1 :
err =
( * filldir ) ( dirent , " . " , 1 , file - > f_pos , inode - > i_ino ,
DT_DIR ) ;
if ( err = = - EINVAL )
break ;
if ( err < 0 )
return err ;
file - > f_pos + + ;
+ + stored ;
/* Fall through */
default :
/* Skip entries */
count = file - > f_pos - 2 ;
read_lock ( & parent - > u . dir . lock ) ;
for ( de = parent - > u . dir . first ; de & & ( count > 0 ) ; de = de - > next )
- - count ;
devfs_get ( de ) ;
read_unlock ( & parent - > u . dir . lock ) ;
/* Now add all remaining entries */
while ( de ) {
err = ( * filldir ) ( dirent , de - > name , de - > namelen ,
file - > f_pos , de - > inode . ino ,
de - > mode > > 12 ) ;
if ( err < 0 )
devfs_put ( de ) ;
else {
file - > f_pos + + ;
+ + stored ;
}
if ( err = = - EINVAL )
break ;
if ( err < 0 )
return err ;
read_lock ( & parent - > u . dir . lock ) ;
next = devfs_get ( de - > next ) ;
read_unlock ( & parent - > u . dir . lock ) ;
devfs_put ( de ) ;
de = next ;
}
break ;
}
return stored ;
} /* End Function devfs_readdir */
/* Open devfs specific special files */
static int devfs_open ( struct inode * inode , struct file * file )
{
int err ;
int minor = MINOR ( inode - > i_rdev ) ;
struct file_operations * old_fops , * new_fops ;
switch ( minor ) {
case 0 : /* /dev/.devfsd */
new_fops = fops_get ( & devfsd_fops ) ;
break ;
# ifdef CONFIG_DEVFS_DEBUG
case 1 : /* /dev/.stat */
new_fops = fops_get ( & stat_fops ) ;
break ;
# endif
default :
return - ENODEV ;
}
if ( new_fops = = NULL )
return - ENODEV ;
old_fops = file - > f_op ;
file - > f_op = new_fops ;
err = new_fops - > open ? new_fops - > open ( inode , file ) : 0 ;
if ( err ) {
file - > f_op = old_fops ;
fops_put ( new_fops ) ;
} else
fops_put ( old_fops ) ;
return err ;
} /* End Function devfs_open */
static struct file_operations devfs_fops = {
. open = devfs_open ,
} ;
static struct file_operations devfs_dir_fops = {
. read = generic_read_dir ,
. readdir = devfs_readdir ,
} ;
/* Dentry operations for device entries follow */
/**
* devfs_d_release - Callback for when a dentry is freed .
* @ dentry : The dentry .
*/
static void devfs_d_release ( struct dentry * dentry )
{
DPRINTK ( DEBUG_D_RELEASE , " (%p): inode: %p \n " , dentry , dentry - > d_inode ) ;
} /* End Function devfs_d_release */
/**
* devfs_d_iput - Callback for when a dentry loses its inode .
* @ dentry : The dentry .
* @ inode : The inode .
*/
static void devfs_d_iput ( struct dentry * dentry , struct inode * inode )
{
struct devfs_entry * de ;
de = get_devfs_entry_from_vfs_inode ( inode ) ;
DPRINTK ( DEBUG_D_IPUT ,
" (%s): dentry: %p inode: %p de: %p de->dentry: %p \n " , de - > name ,
dentry , inode , de , de - > inode . dentry ) ;
if ( de - > inode . dentry & & ( de - > inode . dentry ! = dentry ) )
OOPS ( " (%s): de: %p dentry: %p de->dentry: %p \n " ,
de - > name , de , dentry , de - > inode . dentry ) ;
de - > inode . dentry = NULL ;
iput ( inode ) ;
devfs_put ( de ) ;
} /* End Function devfs_d_iput */
static int devfs_d_delete ( struct dentry * dentry ) ;
static struct dentry_operations devfs_dops = {
. d_delete = devfs_d_delete ,
. d_release = devfs_d_release ,
. d_iput = devfs_d_iput ,
} ;
static int devfs_d_revalidate_wait ( struct dentry * dentry , struct nameidata * ) ;
static struct dentry_operations devfs_wait_dops = {
. d_delete = devfs_d_delete ,
. d_release = devfs_d_release ,
. d_iput = devfs_d_iput ,
. d_revalidate = devfs_d_revalidate_wait ,
} ;
/**
* devfs_d_delete - Callback for when all files for a dentry are closed .
* @ dentry : The dentry .
*/
static int devfs_d_delete ( struct dentry * dentry )
{
struct inode * inode = dentry - > d_inode ;
if ( dentry - > d_op = = & devfs_wait_dops )
dentry - > d_op = & devfs_dops ;
/* Unhash dentry if negative (has no inode) */
if ( inode = = NULL ) {
DPRINTK ( DEBUG_D_DELETE , " (%p): dropping negative dentry \n " ,
dentry ) ;
return 1 ;
}
return 0 ;
} /* End Function devfs_d_delete */
struct devfs_lookup_struct {
devfs_handle_t de ;
wait_queue_head_t wait_queue ;
} ;
/* XXX: this doesn't handle the case where we got a negative dentry
but a devfs entry has been registered in the meanwhile */
static int devfs_d_revalidate_wait ( struct dentry * dentry , struct nameidata * nd )
{
struct inode * dir = dentry - > d_parent - > d_inode ;
struct fs_info * fs_info = dir - > i_sb - > s_fs_info ;
devfs_handle_t parent = get_devfs_entry_from_vfs_inode ( dir ) ;
struct devfs_lookup_struct * lookup_info = dentry - > d_fsdata ;
DECLARE_WAITQUEUE ( wait , current ) ;
int need_lock ;
/*
* FIXME HACK
*
* make sure that
* d_instantiate always runs under lock
2006-01-09 15:59:24 -08:00
* we release i_mutex lock before going to sleep
2005-04-16 15:20:36 -07:00
*
* unfortunately sometimes d_revalidate is called with
2006-01-09 15:59:24 -08:00
* and sometimes without i_mutex lock held . The following checks
2005-04-16 15:20:36 -07:00
* attempt to deduce when we need to add ( and drop resp . ) lock
* here . This relies on current ( 2.6 .2 ) calling coventions :
*
2006-01-09 15:59:24 -08:00
* lookup_hash is always run under i_mutex and is passing NULL
2005-04-16 15:20:36 -07:00
* as nd
*
2006-01-09 15:59:24 -08:00
* open ( . . . , O_CREATE , . . . ) calls _lookup_hash under i_mutex
2005-04-16 15:20:36 -07:00
* and sets flags to LOOKUP_OPEN | LOOKUP_CREATE
*
* all other invocations of - > d_revalidate seem to happen
2006-01-09 15:59:24 -08:00
* outside of i_mutex
2005-04-16 15:20:36 -07:00
*/
need_lock = nd & &
( ! ( nd - > flags & LOOKUP_CREATE ) | | ( nd - > flags & LOOKUP_PARENT ) ) ;
if ( need_lock )
2006-01-09 15:59:24 -08:00
mutex_lock ( & dir - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( is_devfsd_or_child ( fs_info ) ) {
devfs_handle_t de = lookup_info - > de ;
struct inode * inode ;
DPRINTK ( DEBUG_I_LOOKUP ,
" (%s): dentry: %p inode: %p de: %p by: \" %s \" \n " ,
dentry - > d_name . name , dentry , dentry - > d_inode , de ,
current - > comm ) ;
if ( dentry - > d_inode )
goto out ;
if ( de = = NULL ) {
read_lock ( & parent - > u . dir . lock ) ;
de = _devfs_search_dir ( parent , dentry - > d_name . name ,
dentry - > d_name . len ) ;
read_unlock ( & parent - > u . dir . lock ) ;
if ( de = = NULL )
goto out ;
lookup_info - > de = de ;
}
/* Create an inode, now that the driver information is available */
inode = _devfs_get_vfs_inode ( dir - > i_sb , de , dentry ) ;
if ( ! inode )
goto out ;
DPRINTK ( DEBUG_I_LOOKUP ,
" (%s): new VFS inode(%u): %p de: %p by: \" %s \" \n " ,
de - > name , de - > inode . ino , inode , de , current - > comm ) ;
d_instantiate ( dentry , inode ) ;
goto out ;
}
if ( lookup_info = = NULL )
goto out ; /* Early termination */
read_lock ( & parent - > u . dir . lock ) ;
if ( dentry - > d_fsdata ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & lookup_info - > wait_queue , & wait ) ;
read_unlock ( & parent - > u . dir . lock ) ;
/* at this point it is always (hopefully) locked */
2006-01-09 15:59:24 -08:00
mutex_unlock ( & dir - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
schedule ( ) ;
2006-01-09 15:59:24 -08:00
mutex_lock ( & dir - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
/*
* This does not need nor should remove wait from wait_queue .
* Wait queue head is never reused - nothing is ever added to it
* after all waiters have been waked up and head itself disappears
* very soon after it . Moreover it is local variable on stack that
* is likely to have already disappeared so any reference to it
* at this point is buggy .
*/
} else
read_unlock ( & parent - > u . dir . lock ) ;
out :
if ( need_lock )
2006-01-09 15:59:24 -08:00
mutex_unlock ( & dir - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
return 1 ;
} /* End Function devfs_d_revalidate_wait */
/* Inode operations for device entries follow */
static struct dentry * devfs_lookup ( struct inode * dir , struct dentry * dentry ,
struct nameidata * nd )
{
struct devfs_entry tmp ; /* Must stay in scope until devfsd idle again */
struct devfs_lookup_struct lookup_info ;
struct fs_info * fs_info = dir - > i_sb - > s_fs_info ;
struct devfs_entry * parent , * de ;
struct inode * inode ;
struct dentry * retval = NULL ;
/* Set up the dentry operations before anything else, to ensure cleaning
up on any error */
dentry - > d_op = & devfs_dops ;
/* First try to get the devfs entry for this directory */
parent = get_devfs_entry_from_vfs_inode ( dir ) ;
DPRINTK ( DEBUG_I_LOOKUP , " (%s): dentry: %p parent: %p by: \" %s \" \n " ,
dentry - > d_name . name , dentry , parent , current - > comm ) ;
if ( parent = = NULL )
return ERR_PTR ( - ENOENT ) ;
read_lock ( & parent - > u . dir . lock ) ;
de = _devfs_search_dir ( parent , dentry - > d_name . name , dentry - > d_name . len ) ;
read_unlock ( & parent - > u . dir . lock ) ;
lookup_info . de = de ;
init_waitqueue_head ( & lookup_info . wait_queue ) ;
dentry - > d_fsdata = & lookup_info ;
if ( de = = NULL ) { /* Try with devfsd. For any kind of failure, leave a negative dentry
so someone else can deal with it ( in the case where the sysadmin
does a mknod ( ) ) . It ' s important to do this before hashing the
dentry , so that the devfsd queue is filled before revalidates
can start */
if ( try_modload ( parent , fs_info , dentry - > d_name . name , dentry - > d_name . len , & tmp ) < 0 ) { /* Lookup event was not queued to devfsd */
d_add ( dentry , NULL ) ;
return NULL ;
}
}
dentry - > d_op = & devfs_wait_dops ;
d_add ( dentry , NULL ) ; /* Open the floodgates */
/* Unlock directory semaphore, which will release any waiters. They
will get the hashed dentry , and may be forced to wait for
revalidation */
2006-01-09 15:59:24 -08:00
mutex_unlock ( & dir - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
wait_for_devfsd_finished ( fs_info ) ; /* If I'm not devfsd, must wait */
2006-01-09 15:59:24 -08:00
mutex_lock ( & dir - > i_mutex ) ; /* Grab it again because them's the rules */
2005-04-16 15:20:36 -07:00
de = lookup_info . de ;
/* If someone else has been so kind as to make the inode, we go home
early */
if ( dentry - > d_inode )
goto out ;
if ( de = = NULL ) {
read_lock ( & parent - > u . dir . lock ) ;
de = _devfs_search_dir ( parent , dentry - > d_name . name ,
dentry - > d_name . len ) ;
read_unlock ( & parent - > u . dir . lock ) ;
if ( de = = NULL )
goto out ;
/* OK, there's an entry now, but no VFS inode yet */
}
/* Create an inode, now that the driver information is available */
inode = _devfs_get_vfs_inode ( dir - > i_sb , de , dentry ) ;
if ( ! inode ) {
retval = ERR_PTR ( - ENOMEM ) ;
goto out ;
}
DPRINTK ( DEBUG_I_LOOKUP ,
" (%s): new VFS inode(%u): %p de: %p by: \" %s \" \n " , de - > name ,
de - > inode . ino , inode , de , current - > comm ) ;
d_instantiate ( dentry , inode ) ;
out :
write_lock ( & parent - > u . dir . lock ) ;
dentry - > d_op = & devfs_dops ;
dentry - > d_fsdata = NULL ;
wake_up ( & lookup_info . wait_queue ) ;
write_unlock ( & parent - > u . dir . lock ) ;
devfs_put ( de ) ;
return retval ;
} /* End Function devfs_lookup */
static int devfs_unlink ( struct inode * dir , struct dentry * dentry )
{
int unhooked ;
struct devfs_entry * de ;
struct inode * inode = dentry - > d_inode ;
struct fs_info * fs_info = dir - > i_sb - > s_fs_info ;
de = get_devfs_entry_from_vfs_inode ( inode ) ;
DPRINTK ( DEBUG_I_UNLINK , " (%s): de: %p \n " , dentry - > d_name . name , de ) ;
if ( de = = NULL )
return - ENOENT ;
if ( ! de - > vfs )
return - EPERM ;
write_lock ( & de - > parent - > u . dir . lock ) ;
unhooked = _devfs_unhook ( de ) ;
write_unlock ( & de - > parent - > u . dir . lock ) ;
if ( ! unhooked )
return - ENOENT ;
if ( ! is_devfsd_or_child ( fs_info ) )
devfsd_notify_de ( de , DEVFSD_NOTIFY_DELETE , inode - > i_mode ,
inode - > i_uid , inode - > i_gid , fs_info ) ;
free_dentry ( de ) ;
devfs_put ( de ) ;
return 0 ;
} /* End Function devfs_unlink */
static int devfs_symlink ( struct inode * dir , struct dentry * dentry ,
const char * symname )
{
int err ;
struct fs_info * fs_info = dir - > i_sb - > s_fs_info ;
struct devfs_entry * parent , * de ;
struct inode * inode ;
/* First try to get the devfs entry for this directory */
parent = get_devfs_entry_from_vfs_inode ( dir ) ;
if ( parent = = NULL )
return - ENOENT ;
err = devfs_do_symlink ( parent , dentry - > d_name . name , symname , & de ) ;
DPRINTK ( DEBUG_DISABLED , " (%s): errcode from <devfs_do_symlink>: %d \n " ,
dentry - > d_name . name , err ) ;
if ( err < 0 )
return err ;
de - > vfs = TRUE ;
de - > inode . uid = current - > euid ;
de - > inode . gid = current - > egid ;
de - > inode . atime = CURRENT_TIME ;
de - > inode . mtime = CURRENT_TIME ;
de - > inode . ctime = CURRENT_TIME ;
if ( ( inode = _devfs_get_vfs_inode ( dir - > i_sb , de , dentry ) ) = = NULL )
return - ENOMEM ;
DPRINTK ( DEBUG_DISABLED , " (%s): new VFS inode(%u): %p dentry: %p \n " ,
dentry - > d_name . name , de - > inode . ino , inode , dentry ) ;
d_instantiate ( dentry , inode ) ;
if ( ! is_devfsd_or_child ( fs_info ) )
devfsd_notify_de ( de , DEVFSD_NOTIFY_CREATE , inode - > i_mode ,
inode - > i_uid , inode - > i_gid , fs_info ) ;
return 0 ;
} /* End Function devfs_symlink */
static int devfs_mkdir ( struct inode * dir , struct dentry * dentry , int mode )
{
int err ;
struct fs_info * fs_info = dir - > i_sb - > s_fs_info ;
struct devfs_entry * parent , * de ;
struct inode * inode ;
mode = ( mode & ~ S_IFMT ) | S_IFDIR ; /* VFS doesn't pass S_IFMT part */
parent = get_devfs_entry_from_vfs_inode ( dir ) ;
if ( parent = = NULL )
return - ENOENT ;
de = _devfs_alloc_entry ( dentry - > d_name . name , dentry - > d_name . len , mode ) ;
if ( ! de )
return - ENOMEM ;
de - > vfs = TRUE ;
if ( ( err = _devfs_append_entry ( parent , de , NULL ) ) ! = 0 )
return err ;
de - > inode . uid = current - > euid ;
de - > inode . gid = current - > egid ;
de - > inode . atime = CURRENT_TIME ;
de - > inode . mtime = CURRENT_TIME ;
de - > inode . ctime = CURRENT_TIME ;
if ( ( inode = _devfs_get_vfs_inode ( dir - > i_sb , de , dentry ) ) = = NULL )
return - ENOMEM ;
DPRINTK ( DEBUG_DISABLED , " (%s): new VFS inode(%u): %p dentry: %p \n " ,
dentry - > d_name . name , de - > inode . ino , inode , dentry ) ;
d_instantiate ( dentry , inode ) ;
if ( ! is_devfsd_or_child ( fs_info ) )
devfsd_notify_de ( de , DEVFSD_NOTIFY_CREATE , inode - > i_mode ,
inode - > i_uid , inode - > i_gid , fs_info ) ;
return 0 ;
} /* End Function devfs_mkdir */
static int devfs_rmdir ( struct inode * dir , struct dentry * dentry )
{
int err = 0 ;
struct devfs_entry * de ;
struct fs_info * fs_info = dir - > i_sb - > s_fs_info ;
struct inode * inode = dentry - > d_inode ;
if ( dir - > i_sb - > s_fs_info ! = inode - > i_sb - > s_fs_info )
return - EINVAL ;
de = get_devfs_entry_from_vfs_inode ( inode ) ;
if ( de = = NULL )
return - ENOENT ;
if ( ! S_ISDIR ( de - > mode ) )
return - ENOTDIR ;
if ( ! de - > vfs )
return - EPERM ;
/* First ensure the directory is empty and will stay that way */
write_lock ( & de - > u . dir . lock ) ;
if ( de - > u . dir . first )
err = - ENOTEMPTY ;
else
de - > u . dir . no_more_additions = TRUE ;
write_unlock ( & de - > u . dir . lock ) ;
if ( err )
return err ;
/* Now unhook the directory from its parent */
write_lock ( & de - > parent - > u . dir . lock ) ;
if ( ! _devfs_unhook ( de ) )
err = - ENOENT ;
write_unlock ( & de - > parent - > u . dir . lock ) ;
if ( err )
return err ;
if ( ! is_devfsd_or_child ( fs_info ) )
devfsd_notify_de ( de , DEVFSD_NOTIFY_DELETE , inode - > i_mode ,
inode - > i_uid , inode - > i_gid , fs_info ) ;
free_dentry ( de ) ;
devfs_put ( de ) ;
return 0 ;
} /* End Function devfs_rmdir */
static int devfs_mknod ( struct inode * dir , struct dentry * dentry , int mode ,
dev_t rdev )
{
int err ;
struct fs_info * fs_info = dir - > i_sb - > s_fs_info ;
struct devfs_entry * parent , * de ;
struct inode * inode ;
DPRINTK ( DEBUG_I_MKNOD , " (%s): mode: 0%o dev: %u:%u \n " ,
dentry - > d_name . name , mode , MAJOR ( rdev ) , MINOR ( rdev ) ) ;
parent = get_devfs_entry_from_vfs_inode ( dir ) ;
if ( parent = = NULL )
return - ENOENT ;
de = _devfs_alloc_entry ( dentry - > d_name . name , dentry - > d_name . len , mode ) ;
if ( ! de )
return - ENOMEM ;
de - > vfs = TRUE ;
if ( S_ISCHR ( mode ) | | S_ISBLK ( mode ) )
de - > u . dev = rdev ;
if ( ( err = _devfs_append_entry ( parent , de , NULL ) ) ! = 0 )
return err ;
de - > inode . uid = current - > euid ;
de - > inode . gid = current - > egid ;
de - > inode . atime = CURRENT_TIME ;
de - > inode . mtime = CURRENT_TIME ;
de - > inode . ctime = CURRENT_TIME ;
if ( ( inode = _devfs_get_vfs_inode ( dir - > i_sb , de , dentry ) ) = = NULL )
return - ENOMEM ;
DPRINTK ( DEBUG_I_MKNOD , " : new VFS inode(%u): %p dentry: %p \n " ,
de - > inode . ino , inode , dentry ) ;
d_instantiate ( dentry , inode ) ;
if ( ! is_devfsd_or_child ( fs_info ) )
devfsd_notify_de ( de , DEVFSD_NOTIFY_CREATE , inode - > i_mode ,
inode - > i_uid , inode - > i_gid , fs_info ) ;
return 0 ;
} /* End Function devfs_mknod */
[PATCH] Fix up symlink function pointers
This fixes up the symlink functions for the calling convention change:
* afs, autofs4, befs, devfs, freevxfs, jffs2, jfs, ncpfs, procfs,
smbfs, sysvfs, ufs, xfs - prototype change for ->follow_link()
* befs, smbfs, xfs - same for ->put_link()
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-08-20 00:17:39 +01:00
static void * devfs_follow_link ( struct dentry * dentry , struct nameidata * nd )
2005-04-16 15:20:36 -07:00
{
struct devfs_entry * p = get_devfs_entry_from_vfs_inode ( dentry - > d_inode ) ;
nd_set_link ( nd , p ? p - > u . symlink . linkname : ERR_PTR ( - ENODEV ) ) ;
[PATCH] Fix up symlink function pointers
This fixes up the symlink functions for the calling convention change:
* afs, autofs4, befs, devfs, freevxfs, jffs2, jfs, ncpfs, procfs,
smbfs, sysvfs, ufs, xfs - prototype change for ->follow_link()
* befs, smbfs, xfs - same for ->put_link()
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-08-20 00:17:39 +01:00
return NULL ;
2005-04-16 15:20:36 -07:00
} /* End Function devfs_follow_link */
static struct inode_operations devfs_iops = {
. setattr = devfs_notify_change ,
} ;
static struct inode_operations devfs_dir_iops = {
. lookup = devfs_lookup ,
. unlink = devfs_unlink ,
. symlink = devfs_symlink ,
. mkdir = devfs_mkdir ,
. rmdir = devfs_rmdir ,
. mknod = devfs_mknod ,
. setattr = devfs_notify_change ,
} ;
static struct inode_operations devfs_symlink_iops = {
. readlink = generic_readlink ,
. follow_link = devfs_follow_link ,
. setattr = devfs_notify_change ,
} ;
static int devfs_fill_super ( struct super_block * sb , void * data , int silent )
{
struct inode * root_inode = NULL ;
if ( _devfs_get_root_entry ( ) = = NULL )
goto out_no_root ;
atomic_set ( & fs_info . devfsd_overrun_count , 0 ) ;
init_waitqueue_head ( & fs_info . devfsd_wait_queue ) ;
init_waitqueue_head ( & fs_info . revalidate_wait_queue ) ;
fs_info . sb = sb ;
sb - > s_fs_info = & fs_info ;
sb - > s_blocksize = 1024 ;
sb - > s_blocksize_bits = 10 ;
sb - > s_magic = DEVFS_SUPER_MAGIC ;
sb - > s_op = & devfs_sops ;
sb - > s_time_gran = 1 ;
if ( ( root_inode = _devfs_get_vfs_inode ( sb , root_entry , NULL ) ) = = NULL )
goto out_no_root ;
sb - > s_root = d_alloc_root ( root_inode ) ;
if ( ! sb - > s_root )
goto out_no_root ;
DPRINTK ( DEBUG_S_READ , " (): made devfs ptr: %p \n " , sb - > s_fs_info ) ;
return 0 ;
out_no_root :
PRINTK ( " (): get root inode failed \n " ) ;
if ( root_inode )
iput ( root_inode ) ;
return - EINVAL ;
} /* End Function devfs_fill_super */
static struct super_block * devfs_get_sb ( struct file_system_type * fs_type ,
int flags , const char * dev_name ,
void * data )
{
return get_sb_single ( fs_type , flags , data , devfs_fill_super ) ;
}
static struct file_system_type devfs_fs_type = {
. name = DEVFS_NAME ,
. get_sb = devfs_get_sb ,
. kill_sb = kill_anon_super ,
} ;
/* File operations for devfsd follow */
static ssize_t devfsd_read ( struct file * file , char __user * buf , size_t len ,
loff_t * ppos )
{
int done = FALSE ;
int ival ;
loff_t pos , devname_offset , tlen , rpos ;
devfs_handle_t de ;
struct devfsd_buf_entry * entry ;
struct fs_info * fs_info = file - > f_dentry - > d_inode - > i_sb - > s_fs_info ;
struct devfsd_notify_struct * info = fs_info - > devfsd_info ;
DECLARE_WAITQUEUE ( wait , current ) ;
/* Verify the task has grabbed the queue */
if ( fs_info - > devfsd_task ! = current )
return - EPERM ;
info - > major = 0 ;
info - > minor = 0 ;
/* Block for a new entry */
set_current_state ( TASK_INTERRUPTIBLE ) ;
add_wait_queue ( & fs_info - > devfsd_wait_queue , & wait ) ;
while ( devfsd_queue_empty ( fs_info ) ) {
fs_info - > devfsd_sleeping = TRUE ;
wake_up ( & fs_info - > revalidate_wait_queue ) ;
schedule ( ) ;
fs_info - > devfsd_sleeping = FALSE ;
if ( signal_pending ( current ) ) {
remove_wait_queue ( & fs_info - > devfsd_wait_queue , & wait ) ;
__set_current_state ( TASK_RUNNING ) ;
return - EINTR ;
}
set_current_state ( TASK_INTERRUPTIBLE ) ;
}
remove_wait_queue ( & fs_info - > devfsd_wait_queue , & wait ) ;
__set_current_state ( TASK_RUNNING ) ;
/* Now play with the data */
ival = atomic_read ( & fs_info - > devfsd_overrun_count ) ;
info - > overrun_count = ival ;
entry = fs_info - > devfsd_first_event ;
info - > type = entry - > type ;
info - > mode = entry - > mode ;
info - > uid = entry - > uid ;
info - > gid = entry - > gid ;
de = entry - > de ;
if ( S_ISCHR ( de - > mode ) | | S_ISBLK ( de - > mode ) ) {
info - > major = MAJOR ( de - > u . dev ) ;
info - > minor = MINOR ( de - > u . dev ) ;
}
pos = devfs_generate_path ( de , info - > devname , DEVFS_PATHLEN ) ;
if ( pos < 0 )
return pos ;
info - > namelen = DEVFS_PATHLEN - pos - 1 ;
if ( info - > mode = = 0 )
info - > mode = de - > mode ;
devname_offset = info - > devname - ( char * ) info ;
rpos = * ppos ;
if ( rpos < devname_offset ) {
/* Copy parts of the header */
tlen = devname_offset - rpos ;
if ( tlen > len )
tlen = len ;
if ( copy_to_user ( buf , ( char * ) info + rpos , tlen ) ) {
return - EFAULT ;
}
rpos + = tlen ;
buf + = tlen ;
len - = tlen ;
}
if ( ( rpos > = devname_offset ) & & ( len > 0 ) ) {
/* Copy the name */
tlen = info - > namelen + 1 ;
if ( tlen > len )
tlen = len ;
else
done = TRUE ;
if ( copy_to_user
( buf , info - > devname + pos + rpos - devname_offset , tlen ) ) {
return - EFAULT ;
}
rpos + = tlen ;
}
tlen = rpos - * ppos ;
if ( done ) {
devfs_handle_t parent ;
spin_lock ( & fs_info - > devfsd_buffer_lock ) ;
fs_info - > devfsd_first_event = entry - > next ;
if ( entry - > next = = NULL )
fs_info - > devfsd_last_event = NULL ;
spin_unlock ( & fs_info - > devfsd_buffer_lock ) ;
for ( ; de ! = NULL ; de = parent ) {
parent = de - > parent ;
devfs_put ( de ) ;
}
kmem_cache_free ( devfsd_buf_cache , entry ) ;
if ( ival > 0 )
atomic_sub ( ival , & fs_info - > devfsd_overrun_count ) ;
* ppos = 0 ;
} else
* ppos = rpos ;
return tlen ;
} /* End Function devfsd_read */
static int devfsd_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
int ival ;
struct fs_info * fs_info = inode - > i_sb - > s_fs_info ;
switch ( cmd ) {
case DEVFSDIOC_GET_PROTO_REV :
ival = DEVFSD_PROTOCOL_REVISION_KERNEL ;
if ( copy_to_user ( ( void __user * ) arg , & ival , sizeof ival ) )
return - EFAULT ;
break ;
case DEVFSDIOC_SET_EVENT_MASK :
/* Ensure only one reader has access to the queue. This scheme will
work even if the global kernel lock were to be removed , because it
doesn ' t matter who gets in first , as long as only one gets it */
if ( fs_info - > devfsd_task = = NULL ) {
static DEFINE_SPINLOCK ( lock ) ;
if ( ! spin_trylock ( & lock ) )
return - EBUSY ;
if ( fs_info - > devfsd_task ! = NULL ) { /* We lost the race... */
spin_unlock ( & lock ) ;
return - EBUSY ;
}
fs_info - > devfsd_task = current ;
spin_unlock ( & lock ) ;
fs_info - > devfsd_pgrp =
( process_group ( current ) = =
current - > pid ) ? process_group ( current ) : 0 ;
fs_info - > devfsd_file = file ;
fs_info - > devfsd_info =
kmalloc ( sizeof * fs_info - > devfsd_info , GFP_KERNEL ) ;
if ( ! fs_info - > devfsd_info ) {
devfsd_close ( inode , file ) ;
return - ENOMEM ;
}
} else if ( fs_info - > devfsd_task ! = current )
return - EBUSY ;
fs_info - > devfsd_event_mask = arg ; /* Let the masses come forth */
break ;
case DEVFSDIOC_RELEASE_EVENT_QUEUE :
if ( fs_info - > devfsd_file ! = file )
return - EPERM ;
return devfsd_close ( inode , file ) ;
/*break; */
# ifdef CONFIG_DEVFS_DEBUG
case DEVFSDIOC_SET_DEBUG_MASK :
if ( copy_from_user ( & ival , ( void __user * ) arg , sizeof ival ) )
return - EFAULT ;
devfs_debug = ival ;
break ;
# endif
default :
return - ENOIOCTLCMD ;
}
return 0 ;
} /* End Function devfsd_ioctl */
static int devfsd_close ( struct inode * inode , struct file * file )
{
struct devfsd_buf_entry * entry , * next ;
struct fs_info * fs_info = inode - > i_sb - > s_fs_info ;
if ( fs_info - > devfsd_file ! = file )
return 0 ;
fs_info - > devfsd_event_mask = 0 ;
fs_info - > devfsd_file = NULL ;
spin_lock ( & fs_info - > devfsd_buffer_lock ) ;
entry = fs_info - > devfsd_first_event ;
fs_info - > devfsd_first_event = NULL ;
fs_info - > devfsd_last_event = NULL ;
2005-11-07 01:01:34 -08:00
kfree ( fs_info - > devfsd_info ) ;
fs_info - > devfsd_info = NULL ;
2005-04-16 15:20:36 -07:00
spin_unlock ( & fs_info - > devfsd_buffer_lock ) ;
fs_info - > devfsd_pgrp = 0 ;
fs_info - > devfsd_task = NULL ;
wake_up ( & fs_info - > revalidate_wait_queue ) ;
for ( ; entry ; entry = next ) {
next = entry - > next ;
kmem_cache_free ( devfsd_buf_cache , entry ) ;
}
return 0 ;
} /* End Function devfsd_close */
# ifdef CONFIG_DEVFS_DEBUG
static ssize_t stat_read ( struct file * file , char __user * buf , size_t len ,
loff_t * ppos )
{
ssize_t num ;
char txt [ 80 ] ;
num = sprintf ( txt , " Number of entries: %u number of bytes: %u \n " ,
stat_num_entries , stat_num_bytes ) + 1 ;
if ( * ppos > = num )
return 0 ;
if ( * ppos + len > num )
len = num - * ppos ;
if ( copy_to_user ( buf , txt + * ppos , len ) )
return - EFAULT ;
* ppos + = len ;
return len ;
} /* End Function stat_read */
# endif
static int __init init_devfs_fs ( void )
{
int err ;
int major ;
struct devfs_entry * devfsd ;
# ifdef CONFIG_DEVFS_DEBUG
struct devfs_entry * stat ;
# endif
if ( _devfs_get_root_entry ( ) = = NULL )
return - ENOMEM ;
printk ( KERN_INFO " %s: %s Richard Gooch (rgooch@atnf.csiro.au) \n " ,
DEVFS_NAME , DEVFS_VERSION ) ;
devfsd_buf_cache = kmem_cache_create ( " devfsd_event " ,
sizeof ( struct devfsd_buf_entry ) ,
0 , 0 , NULL , NULL ) ;
if ( ! devfsd_buf_cache )
OOPS ( " (): unable to allocate event slab \n " ) ;
# ifdef CONFIG_DEVFS_DEBUG
devfs_debug = devfs_debug_init ;
printk ( KERN_INFO " %s: devfs_debug: 0x%0x \n " , DEVFS_NAME , devfs_debug ) ;
# endif
printk ( KERN_INFO " %s: boot_options: 0x%0x \n " , DEVFS_NAME , boot_options ) ;
/* register special device for devfsd communication */
major = register_chrdev ( 0 , " devfs " , & devfs_fops ) ;
if ( major < 0 )
return major ;
/* And create the entry for ".devfsd" */
devfsd = _devfs_alloc_entry ( " .devfsd " , 0 , S_IFCHR | S_IRUSR | S_IWUSR ) ;
if ( devfsd = = NULL )
return - ENOMEM ;
devfsd - > u . dev = MKDEV ( major , 0 ) ;
_devfs_append_entry ( root_entry , devfsd , NULL ) ;
# ifdef CONFIG_DEVFS_DEBUG
stat = _devfs_alloc_entry ( " .stat " , 0 , S_IFCHR | S_IRUGO ) ;
if ( stat = = NULL )
return - ENOMEM ;
stat - > u . dev = MKDEV ( major , 1 ) ;
_devfs_append_entry ( root_entry , stat , NULL ) ;
# endif
err = register_filesystem ( & devfs_fs_type ) ;
return err ;
} /* End Function init_devfs_fs */
void __init mount_devfs_fs ( void )
{
int err ;
if ( ! ( boot_options & OPTION_MOUNT ) )
return ;
err = do_mount ( " none " , " /dev " , " devfs " , 0 , NULL ) ;
if ( err = = 0 )
printk ( KERN_INFO " Mounted devfs on /dev \n " ) ;
else
PRINTK ( " (): unable to mount devfs, err: %d \n " , err ) ;
} /* End Function mount_devfs_fs */
module_init ( init_devfs_fs )