2005-09-09 13:04:20 -07:00
/*
* linux / fs / 9 p / v9fs . c
*
* This file contains functions assisting in mapping VFS to 9 P2000
*
2008-02-06 19:25:03 -06:00
* Copyright ( C ) 2004 - 2008 by Eric Van Hensbergen < ericvh @ gmail . com >
2005-09-09 13:04:20 -07:00
* Copyright ( C ) 2002 by Ron Minnich < rminnich @ lanl . gov >
*
* This program is free software ; you can redistribute it and / or modify
2006-03-25 03:07:28 -08:00
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
2005-09-09 13:04:20 -07:00
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to :
* Free Software Foundation
* 51 Franklin Street , Fifth Floor
* Boston , MA 02111 - 1301 USA
*
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/fs.h>
2006-10-18 13:55:46 -04:00
# include <linux/sched.h>
2005-09-09 13:04:20 -07:00
# include <linux/parser.h>
# include <linux/idr.h>
2007-07-10 17:57:28 -05:00
# include <net/9p/9p.h>
# include <net/9p/client.h>
2008-10-13 18:45:25 -05:00
# include <net/9p/transport.h>
2005-09-09 13:04:20 -07:00
# include "v9fs.h"
# include "v9fs_vfs.h"
2009-09-23 13:00:27 -05:00
# include "cache.h"
static DEFINE_SPINLOCK ( v9fs_sessionlist_lock ) ;
static LIST_HEAD ( v9fs_sessionlist ) ;
2005-09-09 13:04:20 -07:00
/*
2009-09-23 13:00:27 -05:00
* Option Parsing ( code inspired by NFS code )
* NOTE : each transport will parse its own options
*/
2005-09-09 13:04:20 -07:00
enum {
/* Options that take integer arguments */
2008-02-06 19:25:03 -06:00
Opt_debug , Opt_dfltuid , Opt_dfltgid , Opt_afid ,
2005-09-09 13:04:20 -07:00
/* String options */
2009-09-23 13:00:27 -05:00
Opt_uname , Opt_remotename , Opt_trans , Opt_cache , Opt_cachetag ,
2005-09-09 13:04:20 -07:00
/* Options that take no arguments */
2008-02-06 19:25:03 -06:00
Opt_nodevmap ,
2007-02-11 13:21:39 -06:00
/* Cache options */
2009-09-23 13:00:27 -05:00
Opt_cache_loose , Opt_fscache ,
2007-10-17 14:31:07 -05:00
/* Access options */
Opt_access ,
2005-09-09 13:04:20 -07:00
/* Error token */
Opt_err
} ;
2008-10-13 10:46:57 +01:00
static const match_table_t tokens = {
2007-07-13 13:05:21 -05:00
{ Opt_debug , " debug=%x " } ,
2007-10-17 14:31:07 -05:00
{ Opt_dfltuid , " dfltuid=%u " } ,
{ Opt_dfltgid , " dfltgid=%u " } ,
2005-09-09 13:04:20 -07:00
{ Opt_afid , " afid=%u " } ,
2006-03-25 03:07:29 -08:00
{ Opt_uname , " uname=%s " } ,
2005-09-09 13:04:20 -07:00
{ Opt_remotename , " aname=%s " } ,
{ Opt_nodevmap , " nodevmap " } ,
2009-09-23 13:00:27 -05:00
{ Opt_cache , " cache=%s " } ,
2007-02-11 13:21:39 -06:00
{ Opt_cache_loose , " loose " } ,
2009-09-23 13:00:27 -05:00
{ Opt_fscache , " fscache " } ,
{ Opt_cachetag , " cachetag=%s " } ,
2007-10-17 14:31:07 -05:00
{ Opt_access , " access=%s " } ,
2005-09-09 13:04:20 -07:00
{ Opt_err , NULL }
} ;
/**
* v9fs_parse_options - parse mount options into session structure
* @ v9ses : existing v9fs session information
*
2008-03-06 17:10:28 -06:00
* Return 0 upon success , - ERRNO upon failure .
2005-09-09 13:04:20 -07:00
*/
2009-08-17 16:42:28 -05:00
static int v9fs_parse_options ( struct v9fs_session_info * v9ses , char * opts )
2005-09-09 13:04:20 -07:00
{
2007-11-06 08:02:53 -06:00
char * options ;
2005-09-09 13:04:20 -07:00
substring_t args [ MAX_OPT_ARGS ] ;
2007-10-17 14:31:07 -05:00
char * p ;
2008-02-06 19:25:03 -06:00
int option = 0 ;
2007-10-17 14:31:07 -05:00
char * s , * e ;
2008-03-06 17:10:28 -06:00
int ret = 0 ;
2005-09-09 13:04:20 -07:00
/* setup defaults */
v9ses - > afid = ~ 0 ;
v9ses - > debug = 0 ;
2007-02-11 13:21:39 -06:00
v9ses - > cache = 0 ;
2009-09-23 13:00:27 -05:00
# ifdef CONFIG_9P_FSCACHE
v9ses - > cachetag = NULL ;
# endif
2005-09-09 13:04:20 -07:00
2009-08-17 16:42:28 -05:00
if ( ! opts )
2008-03-06 17:10:28 -06:00
return 0 ;
2005-09-09 13:04:20 -07:00
2009-08-17 16:42:28 -05:00
options = kstrdup ( opts , GFP_KERNEL ) ;
2009-09-23 13:00:27 -05:00
if ( ! options )
goto fail_option_alloc ;
2008-03-06 17:10:28 -06:00
2005-09-09 13:04:20 -07:00
while ( ( p = strsep ( & options , " , " ) ) ! = NULL ) {
int token ;
if ( ! * p )
continue ;
token = match_token ( p , tokens , args ) ;
2006-03-25 03:07:29 -08:00
if ( token < Opt_uname ) {
2008-03-06 17:10:28 -06:00
int r = match_int ( & args [ 0 ] , & option ) ;
if ( r < 0 ) {
2007-07-10 17:57:28 -05:00
P9_DPRINTK ( P9_DEBUG_ERROR ,
2005-09-09 13:04:20 -07:00
" integer field, but no integer? \n " ) ;
2008-03-06 17:10:28 -06:00
ret = r ;
2005-09-09 13:04:20 -07:00
continue ;
}
}
switch ( token ) {
2007-07-13 13:05:21 -05:00
case Opt_debug :
v9ses - > debug = option ;
2007-07-16 16:02:49 -05:00
# ifdef CONFIG_NET_9P_DEBUG
2007-07-13 13:05:21 -05:00
p9_debug_level = option ;
2007-07-16 16:02:49 -05:00
# endif
2007-07-13 13:05:21 -05:00
break ;
2008-02-06 19:25:03 -06:00
2007-10-17 14:31:07 -05:00
case Opt_dfltuid :
v9ses - > dfltuid = option ;
2005-09-09 13:04:20 -07:00
break ;
2007-10-17 14:31:07 -05:00
case Opt_dfltgid :
v9ses - > dfltgid = option ;
2005-09-09 13:04:20 -07:00
break ;
case Opt_afid :
v9ses - > afid = option ;
break ;
2006-03-25 03:07:29 -08:00
case Opt_uname :
2008-02-26 09:57:11 -06:00
match_strlcpy ( v9ses - > uname , & args [ 0 ] , PATH_MAX ) ;
2005-09-09 13:04:20 -07:00
break ;
case Opt_remotename :
2008-02-26 09:57:11 -06:00
match_strlcpy ( v9ses - > aname , & args [ 0 ] , PATH_MAX ) ;
2005-09-09 13:04:20 -07:00
break ;
case Opt_nodevmap :
v9ses - > nodev = 1 ;
break ;
2007-02-11 13:21:39 -06:00
case Opt_cache_loose :
v9ses - > cache = CACHE_LOOSE ;
break ;
2009-09-23 13:00:27 -05:00
case Opt_fscache :
v9ses - > cache = CACHE_FSCACHE ;
break ;
case Opt_cachetag :
# ifdef CONFIG_9P_FSCACHE
v9ses - > cachetag = match_strdup ( & args [ 0 ] ) ;
# endif
break ;
case Opt_cache :
s = match_strdup ( & args [ 0 ] ) ;
if ( ! s )
goto fail_option_alloc ;
if ( strcmp ( s , " loose " ) = = 0 )
v9ses - > cache = CACHE_LOOSE ;
else if ( strcmp ( s , " fscache " ) = = 0 )
v9ses - > cache = CACHE_FSCACHE ;
else
v9ses - > cache = CACHE_NONE ;
kfree ( s ) ;
break ;
2007-10-17 14:31:07 -05:00
case Opt_access :
s = match_strdup ( & args [ 0 ] ) ;
2009-09-23 13:00:27 -05:00
if ( ! s )
goto fail_option_alloc ;
2007-10-17 14:31:07 -05:00
v9ses - > flags & = ~ V9FS_ACCESS_MASK ;
if ( strcmp ( s , " user " ) = = 0 )
v9ses - > flags | = V9FS_ACCESS_USER ;
else if ( strcmp ( s , " any " ) = = 0 )
v9ses - > flags | = V9FS_ACCESS_ANY ;
else {
v9ses - > flags | = V9FS_ACCESS_SINGLE ;
2008-12-19 16:50:22 -06:00
v9ses - > uid = simple_strtoul ( s , & e , 10 ) ;
2007-10-17 14:31:07 -05:00
if ( * e ! = ' \0 ' )
v9ses - > uid = ~ 0 ;
}
2007-10-23 13:48:50 -05:00
kfree ( s ) ;
2007-10-17 14:31:07 -05:00
break ;
2005-09-09 13:04:20 -07:00
default :
continue ;
}
}
2007-11-06 08:02:53 -06:00
kfree ( options ) ;
2008-03-06 17:10:28 -06:00
return ret ;
2009-09-23 13:00:27 -05:00
fail_option_alloc :
P9_DPRINTK ( P9_DEBUG_ERROR ,
" failed to allocate copy of option argument \n " ) ;
return - ENOMEM ;
2005-09-09 13:04:20 -07:00
}
/**
* v9fs_session_init - initialize session
* @ v9ses : session information structure
* @ dev_name : device being mounted
* @ data : options
*
*/
2007-07-10 17:57:28 -05:00
struct p9_fid * v9fs_session_init ( struct v9fs_session_info * v9ses ,
2005-09-09 13:04:20 -07:00
const char * dev_name , char * data )
{
int retval = - EINVAL ;
2007-07-10 17:57:28 -05:00
struct p9_fid * fid ;
2008-03-06 17:10:28 -06:00
int rc ;
2005-09-09 13:04:20 -07:00
2007-10-17 14:31:07 -05:00
v9ses - > uname = __getname ( ) ;
if ( ! v9ses - > uname )
2007-07-10 17:57:28 -05:00
return ERR_PTR ( - ENOMEM ) ;
2005-09-09 13:04:20 -07:00
2007-10-17 14:31:07 -05:00
v9ses - > aname = __getname ( ) ;
if ( ! v9ses - > aname ) {
__putname ( v9ses - > uname ) ;
2007-07-10 17:57:28 -05:00
return ERR_PTR ( - ENOMEM ) ;
2005-09-09 13:04:20 -07:00
}
2009-09-23 13:00:27 -05:00
spin_lock ( & v9fs_sessionlist_lock ) ;
list_add ( & v9ses - > slist , & v9fs_sessionlist ) ;
spin_unlock ( & v9fs_sessionlist_lock ) ;
2007-10-17 14:31:07 -05:00
v9ses - > flags = V9FS_EXTENDED | V9FS_ACCESS_USER ;
strcpy ( v9ses - > uname , V9FS_DEFUSER ) ;
strcpy ( v9ses - > aname , V9FS_DEFANAME ) ;
v9ses - > uid = ~ 0 ;
2007-10-17 14:31:07 -05:00
v9ses - > dfltuid = V9FS_DEFUID ;
v9ses - > dfltgid = V9FS_DEFGID ;
2008-03-06 17:10:28 -06:00
2009-08-17 16:42:28 -05:00
rc = v9fs_parse_options ( v9ses , data ) ;
2008-03-06 17:10:28 -06:00
if ( rc < 0 ) {
retval = rc ;
goto error ;
}
2007-10-17 14:31:07 -05:00
2009-08-17 16:42:28 -05:00
v9ses - > clnt = p9_client_create ( dev_name , data ) ;
2007-07-10 17:57:28 -05:00
if ( IS_ERR ( v9ses - > clnt ) ) {
retval = PTR_ERR ( v9ses - > clnt ) ;
v9ses - > clnt = NULL ;
P9_DPRINTK ( P9_DEBUG_ERROR , " problem initializing 9p client \n " ) ;
goto error ;
2005-09-09 13:04:20 -07:00
}
2007-10-17 14:31:07 -05:00
if ( ! v9ses - > clnt - > dotu )
v9ses - > flags & = ~ V9FS_EXTENDED ;
2008-10-13 20:36:16 -05:00
v9ses - > maxdata = v9ses - > clnt - > msize - P9_IOHDRSZ ;
2008-02-06 19:25:03 -06:00
2007-10-17 14:31:07 -05:00
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
if ( ! v9fs_extended ( v9ses ) & &
( ( v9ses - > flags & V9FS_ACCESS_MASK ) = = V9FS_ACCESS_USER ) ) {
v9ses - > flags & = ~ V9FS_ACCESS_MASK ;
v9ses - > flags | = V9FS_ACCESS_ANY ;
v9ses - > uid = ~ 0 ;
}
fid = p9_client_attach ( v9ses - > clnt , NULL , v9ses - > uname , ~ 0 ,
v9ses - > aname ) ;
2007-07-10 17:57:28 -05:00
if ( IS_ERR ( fid ) ) {
retval = PTR_ERR ( fid ) ;
fid = NULL ;
P9_DPRINTK ( P9_DEBUG_ERROR , " cannot attach \n " ) ;
goto error ;
2005-09-09 13:04:20 -07:00
}
2007-10-17 14:31:07 -05:00
if ( ( v9ses - > flags & V9FS_ACCESS_MASK ) = = V9FS_ACCESS_SINGLE )
fid - > uid = v9ses - > uid ;
else
fid - > uid = ~ 0 ;
2009-09-23 13:00:27 -05:00
# ifdef CONFIG_9P_FSCACHE
/* register the session for caching */
v9fs_cache_session_get_cookie ( v9ses ) ;
# endif
2007-07-10 17:57:28 -05:00
return fid ;
2005-09-09 13:04:20 -07:00
2007-07-10 17:57:28 -05:00
error :
return ERR_PTR ( retval ) ;
2005-09-09 13:04:20 -07:00
}
/**
* v9fs_session_close - shutdown a session
* @ v9ses : session information structure
*
*/
void v9fs_session_close ( struct v9fs_session_info * v9ses )
{
2007-07-10 17:57:28 -05:00
if ( v9ses - > clnt ) {
p9_client_destroy ( v9ses - > clnt ) ;
v9ses - > clnt = NULL ;
2006-01-08 01:04:58 -08:00
}
2005-09-09 13:04:20 -07:00
2009-09-23 13:00:27 -05:00
# ifdef CONFIG_9P_FSCACHE
if ( v9ses - > fscache ) {
v9fs_cache_session_put_cookie ( v9ses ) ;
kfree ( v9ses - > cachetag ) ;
}
# endif
2007-10-17 14:31:07 -05:00
__putname ( v9ses - > uname ) ;
__putname ( v9ses - > aname ) ;
2009-09-23 13:00:27 -05:00
spin_lock ( & v9fs_sessionlist_lock ) ;
list_del ( & v9ses - > slist ) ;
spin_unlock ( & v9fs_sessionlist_lock ) ;
2005-09-09 13:04:20 -07:00
}
2005-09-09 13:04:23 -07:00
/**
2008-03-05 07:08:09 -06:00
* v9fs_session_cancel - terminate a session
* @ v9ses : session to terminate
*
* mark transport as disconnected and cancel all pending requests .
2005-09-09 13:04:23 -07:00
*/
2008-03-05 07:08:09 -06:00
2005-09-09 13:04:23 -07:00
void v9fs_session_cancel ( struct v9fs_session_info * v9ses ) {
2007-07-10 17:57:28 -05:00
P9_DPRINTK ( P9_DEBUG_ERROR , " cancel session %p \n " , v9ses ) ;
p9_client_disconnect ( v9ses - > clnt ) ;
2005-09-09 13:04:23 -07:00
}
2005-09-09 13:04:20 -07:00
extern int v9fs_error_init ( void ) ;
2009-09-23 13:00:27 -05:00
static struct kobject * v9fs_kobj ;
# ifdef CONFIG_9P_FSCACHE
2005-09-09 13:04:20 -07:00
/**
2009-09-23 13:00:27 -05:00
* caches_show - list caches associated with a session
*
* Returns the size of buffer written .
*/
static ssize_t caches_show ( struct kobject * kobj ,
struct kobj_attribute * attr ,
char * buf )
{
ssize_t n = 0 , count = 0 , limit = PAGE_SIZE ;
struct v9fs_session_info * v9ses ;
spin_lock ( & v9fs_sessionlist_lock ) ;
list_for_each_entry ( v9ses , & v9fs_sessionlist , slist ) {
if ( v9ses - > cachetag ) {
n = snprintf ( buf , limit , " %s \n " , v9ses - > cachetag ) ;
if ( n < 0 ) {
count = n ;
break ;
}
count + = n ;
limit - = n ;
}
}
spin_unlock ( & v9fs_sessionlist_lock ) ;
return count ;
}
static struct kobj_attribute v9fs_attr_cache = __ATTR_RO ( caches ) ;
# endif /* CONFIG_9P_FSCACHE */
static struct attribute * v9fs_attrs [ ] = {
# ifdef CONFIG_9P_FSCACHE
& v9fs_attr_cache . attr ,
# endif
NULL ,
} ;
static struct attribute_group v9fs_attr_group = {
. attrs = v9fs_attrs ,
} ;
/**
* v9fs_sysfs_init - Initialize the v9fs sysfs interface
*
*/
static int v9fs_sysfs_init ( void )
{
v9fs_kobj = kobject_create_and_add ( " 9p " , fs_kobj ) ;
if ( ! v9fs_kobj )
return - ENOMEM ;
if ( sysfs_create_group ( v9fs_kobj , & v9fs_attr_group ) ) {
kobject_put ( v9fs_kobj ) ;
return - ENOMEM ;
}
return 0 ;
}
/**
* v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
*
*/
static void v9fs_sysfs_cleanup ( void )
{
sysfs_remove_group ( v9fs_kobj , & v9fs_attr_group ) ;
kobject_put ( v9fs_kobj ) ;
}
/**
* init_v9fs - Initialize module
2005-09-09 13:04:20 -07:00
*
*/
static int __init init_v9fs ( void )
{
2009-09-23 13:00:27 -05:00
int err ;
2007-01-26 00:57:04 -08:00
printk ( KERN_INFO " Installing v9fs 9p2000 file system support \n " ) ;
2007-10-17 14:31:07 -05:00
/* TODO: Setup list of registered trasnport modules */
2009-09-23 13:00:27 -05:00
err = register_filesystem ( & v9fs_fs_type ) ;
if ( err < 0 ) {
printk ( KERN_ERR " Failed to register filesystem \n " ) ;
return err ;
}
err = v9fs_cache_register ( ) ;
if ( err < 0 ) {
printk ( KERN_ERR " Failed to register v9fs for caching \n " ) ;
goto out_fs_unreg ;
}
err = v9fs_sysfs_init ( ) ;
if ( err < 0 ) {
printk ( KERN_ERR " Failed to register with sysfs \n " ) ;
goto out_sysfs_cleanup ;
}
return 0 ;
out_sysfs_cleanup :
v9fs_sysfs_cleanup ( ) ;
out_fs_unreg :
unregister_filesystem ( & v9fs_fs_type ) ;
return err ;
2005-09-09 13:04:20 -07:00
}
/**
2009-09-23 13:00:27 -05:00
* exit_v9fs - shutdown module
2005-09-09 13:04:20 -07:00
*
*/
static void __exit exit_v9fs ( void )
{
2009-09-23 13:00:27 -05:00
v9fs_sysfs_cleanup ( ) ;
v9fs_cache_unregister ( ) ;
2005-09-09 13:04:20 -07:00
unregister_filesystem ( & v9fs_fs_type ) ;
}
module_init ( init_v9fs )
module_exit ( exit_v9fs )
2007-07-10 17:57:28 -05:00
MODULE_AUTHOR ( " Latchesar Ionkov <lucho@ionkov.net> " ) ;
2005-09-09 13:04:20 -07:00
MODULE_AUTHOR ( " Eric Van Hensbergen <ericvh@gmail.com> " ) ;
MODULE_AUTHOR ( " Ron Minnich <rminnich@lanl.gov> " ) ;
MODULE_LICENSE ( " GPL " ) ;