2005-04-17 02:20:36 +04:00
/*
2007-07-01 20:13:33 +04:00
* In - kernel MOUNT protocol client
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 1997 , Olaf Kirch < okir @ monad . swb . de >
*/
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/uio.h>
# include <linux/net.h>
# include <linux/in.h>
# include <linux/sunrpc/clnt.h>
# include <linux/sunrpc/sched.h>
# include <linux/nfs_fs.h>
2008-04-12 04:03:06 +04:00
# include "internal.h"
2005-04-17 02:20:36 +04:00
2012-03-18 22:07:42 +04:00
# ifdef NFS_DEBUG
2007-07-01 20:13:27 +04:00
# define NFSDBG_FACILITY NFSDBG_MOUNT
2005-04-17 02:20:36 +04:00
# endif
2009-06-18 05:02:11 +04:00
/*
* Defined by RFC 1094 , section A .3 ; and RFC 1813 , section 5.1 .4
*/
# define MNTPATHLEN (1024)
/*
* XDR data type sizes
*/
# define encode_dirpath_sz (1 + XDR_QUADLEN(MNTPATHLEN))
2009-06-18 05:02:12 +04:00
# define MNT_status_sz (1)
# define MNT_fhs_status_sz (1)
2009-06-18 05:02:12 +04:00
# define MNT_fhandle_sz XDR_QUADLEN(NFS2_FHSIZE)
# define MNT_fhandle3_sz (1 + XDR_QUADLEN(NFS3_FHSIZE))
2009-06-18 05:02:12 +04:00
# define MNT_authflav3_sz (1 + NFS_MAX_SECFLAVORS)
2009-06-18 05:02:11 +04:00
/*
* XDR argument and result sizes
*/
# define MNT_enc_dirpath_sz encode_dirpath_sz
2009-06-18 05:02:13 +04:00
# define MNT_dec_mountres_sz (MNT_status_sz + MNT_fhandle_sz)
# define MNT_dec_mountres3_sz (MNT_status_sz + MNT_fhandle_sz + \
MNT_authflav3_sz )
2009-06-18 05:02:11 +04:00
2009-06-18 05:02:11 +04:00
/*
* Defined by RFC 1094 , section A .5
*/
enum {
MOUNTPROC_NULL = 0 ,
MOUNTPROC_MNT = 1 ,
MOUNTPROC_DUMP = 2 ,
MOUNTPROC_UMNT = 3 ,
MOUNTPROC_UMNTALL = 4 ,
MOUNTPROC_EXPORT = 5 ,
} ;
/*
* Defined by RFC 1813 , section 5.2
*/
enum {
MOUNTPROC3_NULL = 0 ,
MOUNTPROC3_MNT = 1 ,
MOUNTPROC3_DUMP = 2 ,
MOUNTPROC3_UMNT = 3 ,
MOUNTPROC3_UMNTALL = 4 ,
MOUNTPROC3_EXPORT = 5 ,
} ;
2012-01-20 22:53:56 +04:00
static const struct rpc_program mnt_program ;
2005-04-17 02:20:36 +04:00
2009-06-18 05:02:12 +04:00
/*
* Defined by OpenGroup XNFS Version 3 W , chapter 8
*/
enum mountstat {
MNT_OK = 0 ,
MNT_EPERM = 1 ,
MNT_ENOENT = 2 ,
MNT_EACCES = 13 ,
MNT_EINVAL = 22 ,
} ;
static struct {
u32 status ;
int errno ;
} mnt_errtbl [ ] = {
{ . status = MNT_OK , . errno = 0 , } ,
{ . status = MNT_EPERM , . errno = - EPERM , } ,
{ . status = MNT_ENOENT , . errno = - ENOENT , } ,
{ . status = MNT_EACCES , . errno = - EACCES , } ,
{ . status = MNT_EINVAL , . errno = - EINVAL , } ,
} ;
/*
* Defined by RFC 1813 , section 5.1 .5
*/
enum mountstat3 {
MNT3_OK = 0 , /* no error */
MNT3ERR_PERM = 1 , /* Not owner */
MNT3ERR_NOENT = 2 , /* No such file or directory */
MNT3ERR_IO = 5 , /* I/O error */
MNT3ERR_ACCES = 13 , /* Permission denied */
MNT3ERR_NOTDIR = 20 , /* Not a directory */
MNT3ERR_INVAL = 22 , /* Invalid argument */
MNT3ERR_NAMETOOLONG = 63 , /* Filename too long */
MNT3ERR_NOTSUPP = 10004 , /* Operation not supported */
MNT3ERR_SERVERFAULT = 10006 , /* A failure on the server */
} ;
static struct {
u32 status ;
int errno ;
} mnt3_errtbl [ ] = {
{ . status = MNT3_OK , . errno = 0 , } ,
{ . status = MNT3ERR_PERM , . errno = - EPERM , } ,
{ . status = MNT3ERR_NOENT , . errno = - ENOENT , } ,
{ . status = MNT3ERR_IO , . errno = - EIO , } ,
{ . status = MNT3ERR_ACCES , . errno = - EACCES , } ,
{ . status = MNT3ERR_NOTDIR , . errno = - ENOTDIR , } ,
{ . status = MNT3ERR_INVAL , . errno = - EINVAL , } ,
{ . status = MNT3ERR_NAMETOOLONG , . errno = - ENAMETOOLONG , } ,
{ . status = MNT3ERR_NOTSUPP , . errno = - ENOTSUPP , } ,
2010-02-08 17:32:40 +03:00
{ . status = MNT3ERR_SERVERFAULT , . errno = - EREMOTEIO , } ,
2009-06-18 05:02:12 +04:00
} ;
struct mountres {
int errno ;
struct nfs_fh * fh ;
2009-06-18 05:02:12 +04:00
unsigned int * auth_count ;
rpc_authflavor_t * auth_flavors ;
2009-06-18 05:02:12 +04:00
} ;
2005-04-17 02:20:36 +04:00
struct mnt_fhstatus {
2007-07-01 20:13:33 +04:00
u32 status ;
struct nfs_fh * fh ;
2005-04-17 02:20:36 +04:00
} ;
2007-07-01 20:13:27 +04:00
/**
* nfs_mount - Obtain an NFS file handle for the given host and path
2008-12-23 23:21:35 +03:00
* @ info : pointer to mount request arguments
2007-07-01 20:13:27 +04:00
*
* Uses default timeout parameters specified by underlying transport .
2005-04-17 02:20:36 +04:00
*/
2008-12-23 23:21:35 +03:00
int nfs_mount ( struct nfs_mount_request * info )
2005-04-17 02:20:36 +04:00
{
2009-06-18 05:02:13 +04:00
struct mountres result = {
. fh = info - > fh ,
. auth_count = info - > auth_flav_len ,
. auth_flavors = info - > auth_flavs ,
2005-04-17 02:20:36 +04:00
} ;
2006-03-20 21:44:23 +03:00
struct rpc_message msg = {
2008-12-23 23:21:35 +03:00
. rpc_argp = info - > dirpath ,
2006-03-20 21:44:23 +03:00
. rpc_resp = & result ,
} ;
2007-07-01 20:13:27 +04:00
struct rpc_create_args args = {
2012-01-10 16:12:54 +04:00
. net = info - > net ,
2008-12-23 23:21:35 +03:00
. protocol = info - > protocol ,
. address = info - > sap ,
. addrsize = info - > salen ,
. servername = info - > hostname ,
2007-07-01 20:13:27 +04:00
. program = & mnt_program ,
2008-12-23 23:21:35 +03:00
. version = info - > version ,
2007-07-01 20:13:27 +04:00
. authflavor = RPC_AUTH_UNIX ,
} ;
struct rpc_clnt * mnt_clnt ;
2005-04-17 02:20:36 +04:00
int status ;
2007-07-01 20:13:27 +04:00
dprintk ( " NFS: sending MNT request for %s:%s \n " ,
2008-12-23 23:21:35 +03:00
( info - > hostname ? info - > hostname : " server " ) ,
info - > dirpath ) ;
2005-04-17 02:20:36 +04:00
2008-12-23 23:21:37 +03:00
if ( info - > noresvport )
args . flags | = RPC_CLNT_CREATE_NONPRIVPORT ;
2007-07-01 20:13:27 +04:00
mnt_clnt = rpc_create ( & args ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( mnt_clnt ) )
2007-07-01 20:13:38 +04:00
goto out_clnt_err ;
2005-04-17 02:20:36 +04:00
2008-12-23 23:21:35 +03:00
if ( info - > version = = NFS_MNT3_VERSION )
2006-03-20 21:44:23 +03:00
msg . rpc_proc = & mnt_clnt - > cl_procinfo [ MOUNTPROC3_MNT ] ;
else
2009-06-18 05:02:11 +04:00
msg . rpc_proc = & mnt_clnt - > cl_procinfo [ MOUNTPROC_MNT ] ;
2006-03-20 21:44:23 +03:00
2012-10-16 21:22:19 +04:00
status = rpc_call_sync ( mnt_clnt , & msg , RPC_TASK_SOFT | RPC_TASK_TIMEOUT ) ;
2007-06-10 03:49:36 +04:00
rpc_shutdown_client ( mnt_clnt ) ;
2007-07-01 20:13:38 +04:00
if ( status < 0 )
goto out_call_err ;
2009-06-18 05:02:13 +04:00
if ( result . errno ! = 0 )
2007-07-01 20:13:38 +04:00
goto out_mnt_err ;
dprintk ( " NFS: MNT request succeeded \n " ) ;
status = 0 ;
out :
return status ;
out_clnt_err :
status = PTR_ERR ( mnt_clnt ) ;
2009-06-18 05:02:13 +04:00
dprintk ( " NFS: failed to create MNT RPC client, status=%d \n " , status ) ;
2007-07-01 20:13:38 +04:00
goto out ;
out_call_err :
2009-06-18 05:02:13 +04:00
dprintk ( " NFS: MNT request failed, status=%d \n " , status ) ;
2007-07-01 20:13:38 +04:00
goto out ;
out_mnt_err :
2009-06-18 05:02:13 +04:00
dprintk ( " NFS: MNT server returned result %d \n " , result . errno ) ;
status = result . errno ;
2007-07-01 20:13:38 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2009-08-09 23:09:30 +04:00
/**
* nfs_umount - Notify a server that we have unmounted this export
* @ info : pointer to umount request arguments
*
* MOUNTPROC_UMNT is advisory , so we set a short timeout , and always
* use UDP .
*/
void nfs_umount ( const struct nfs_mount_request * info )
{
static const struct rpc_timeout nfs_umnt_timeout = {
. to_initval = 1 * HZ ,
. to_maxval = 3 * HZ ,
. to_retries = 2 ,
} ;
struct rpc_create_args args = {
2012-01-10 16:12:54 +04:00
. net = info - > net ,
2009-08-09 23:09:30 +04:00
. protocol = IPPROTO_UDP ,
. address = info - > sap ,
. addrsize = info - > salen ,
. timeout = & nfs_umnt_timeout ,
. servername = info - > hostname ,
. program = & mnt_program ,
. version = info - > version ,
. authflavor = RPC_AUTH_UNIX ,
. flags = RPC_CLNT_CREATE_NOPING ,
} ;
struct rpc_message msg = {
. rpc_argp = info - > dirpath ,
} ;
struct rpc_clnt * clnt ;
int status ;
if ( info - > noresvport )
args . flags | = RPC_CLNT_CREATE_NONPRIVPORT ;
clnt = rpc_create ( & args ) ;
2010-12-09 17:53:28 +03:00
if ( IS_ERR ( clnt ) )
2009-08-09 23:09:30 +04:00
goto out_clnt_err ;
dprintk ( " NFS: sending UMNT request for %s:%s \n " ,
( info - > hostname ? info - > hostname : " server " ) , info - > dirpath ) ;
if ( info - > version = = NFS_MNT3_VERSION )
msg . rpc_proc = & clnt - > cl_procinfo [ MOUNTPROC3_UMNT ] ;
else
msg . rpc_proc = & clnt - > cl_procinfo [ MOUNTPROC_UMNT ] ;
status = rpc_call_sync ( clnt , & msg , 0 ) ;
rpc_shutdown_client ( clnt ) ;
if ( unlikely ( status < 0 ) )
goto out_call_err ;
return ;
out_clnt_err :
dprintk ( " NFS: failed to create UMNT RPC client, status=%ld \n " ,
PTR_ERR ( clnt ) ) ;
return ;
out_call_err :
dprintk ( " NFS: UMNT request failed, status=%d \n " , status ) ;
}
2005-04-17 02:20:36 +04:00
/*
* XDR encode / decode functions for MOUNT
*/
2010-12-14 17:58:40 +03:00
static void encode_mntdirpath ( struct xdr_stream * xdr , const char * pathname )
2009-06-18 05:02:11 +04:00
{
const u32 pathname_len = strlen ( pathname ) ;
__be32 * p ;
2010-12-14 17:58:40 +03:00
BUG_ON ( pathname_len > MNTPATHLEN ) ;
p = xdr_reserve_space ( xdr , 4 + pathname_len ) ;
2009-06-18 05:02:11 +04:00
xdr_encode_opaque ( p , pathname , pathname_len ) ;
}
2010-12-14 17:59:18 +03:00
static void mnt_xdr_enc_dirpath ( struct rpc_rqst * req , struct xdr_stream * xdr ,
const char * dirpath )
2009-06-18 05:02:11 +04:00
{
2010-12-14 17:59:18 +03:00
encode_mntdirpath ( xdr , dirpath ) ;
2009-06-18 05:02:11 +04:00
}
2009-06-18 05:02:12 +04:00
/*
* RFC 1094 : " A non-zero status indicates some sort of error. In this
* case , the status is a UNIX error number . " This can be problematic
* if the server and client use different errno values for the same
* error .
*
* However , the OpenGroup XNFS spec provides a simple mapping that is
* independent of local errno values on the server and the client .
*/
static int decode_status ( struct xdr_stream * xdr , struct mountres * res )
{
unsigned int i ;
u32 status ;
__be32 * p ;
2010-12-14 17:58:40 +03:00
p = xdr_inline_decode ( xdr , 4 ) ;
2009-06-18 05:02:12 +04:00
if ( unlikely ( p = = NULL ) )
return - EIO ;
2010-12-14 17:58:40 +03:00
status = be32_to_cpup ( p ) ;
2009-06-18 05:02:12 +04:00
2009-08-09 23:06:19 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( mnt_errtbl ) ; i + + ) {
2009-06-18 05:02:12 +04:00
if ( mnt_errtbl [ i ] . status = = status ) {
res - > errno = mnt_errtbl [ i ] . errno ;
return 0 ;
}
}
dprintk ( " NFS: unrecognized MNT status code: %u \n " , status ) ;
res - > errno = - EACCES ;
return 0 ;
}
2009-06-18 05:02:12 +04:00
static int decode_fhandle ( struct xdr_stream * xdr , struct mountres * res )
{
struct nfs_fh * fh = res - > fh ;
__be32 * p ;
p = xdr_inline_decode ( xdr , NFS2_FHSIZE ) ;
if ( unlikely ( p = = NULL ) )
return - EIO ;
fh - > size = NFS2_FHSIZE ;
memcpy ( fh - > data , p , NFS2_FHSIZE ) ;
return 0 ;
}
2010-12-14 17:59:29 +03:00
static int mnt_xdr_dec_mountres ( struct rpc_rqst * req ,
struct xdr_stream * xdr ,
struct mountres * res )
2009-06-18 05:02:13 +04:00
{
int status ;
2010-12-14 17:59:29 +03:00
status = decode_status ( xdr , res ) ;
2009-06-18 05:02:13 +04:00
if ( unlikely ( status ! = 0 | | res - > errno ! = 0 ) )
return status ;
2010-12-14 17:59:29 +03:00
return decode_fhandle ( xdr , res ) ;
2009-06-18 05:02:13 +04:00
}
2009-06-18 05:02:12 +04:00
static int decode_fhs_status ( struct xdr_stream * xdr , struct mountres * res )
{
unsigned int i ;
u32 status ;
__be32 * p ;
2010-12-14 17:58:40 +03:00
p = xdr_inline_decode ( xdr , 4 ) ;
2009-06-18 05:02:12 +04:00
if ( unlikely ( p = = NULL ) )
return - EIO ;
2010-12-14 17:58:40 +03:00
status = be32_to_cpup ( p ) ;
2009-06-18 05:02:12 +04:00
2009-08-09 23:06:19 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( mnt3_errtbl ) ; i + + ) {
2009-06-18 05:02:12 +04:00
if ( mnt3_errtbl [ i ] . status = = status ) {
res - > errno = mnt3_errtbl [ i ] . errno ;
return 0 ;
}
}
dprintk ( " NFS: unrecognized MNT3 status code: %u \n " , status ) ;
res - > errno = - EACCES ;
return 0 ;
}
2009-06-18 05:02:12 +04:00
static int decode_fhandle3 ( struct xdr_stream * xdr , struct mountres * res )
{
struct nfs_fh * fh = res - > fh ;
u32 size ;
__be32 * p ;
2010-12-14 17:58:40 +03:00
p = xdr_inline_decode ( xdr , 4 ) ;
2009-06-18 05:02:12 +04:00
if ( unlikely ( p = = NULL ) )
return - EIO ;
2010-12-14 17:58:40 +03:00
size = be32_to_cpup ( p ) ;
2009-06-18 05:02:12 +04:00
if ( size > NFS3_FHSIZE | | size = = 0 )
return - EIO ;
p = xdr_inline_decode ( xdr , size ) ;
if ( unlikely ( p = = NULL ) )
return - EIO ;
fh - > size = size ;
memcpy ( fh - > data , p , size ) ;
return 0 ;
}
2009-06-18 05:02:12 +04:00
static int decode_auth_flavors ( struct xdr_stream * xdr , struct mountres * res )
{
rpc_authflavor_t * flavors = res - > auth_flavors ;
unsigned int * count = res - > auth_count ;
u32 entries , i ;
__be32 * p ;
if ( * count = = 0 )
return 0 ;
2010-12-14 17:58:40 +03:00
p = xdr_inline_decode ( xdr , 4 ) ;
2009-06-18 05:02:12 +04:00
if ( unlikely ( p = = NULL ) )
return - EIO ;
2010-12-14 17:58:40 +03:00
entries = be32_to_cpup ( p ) ;
2009-06-18 05:02:12 +04:00
dprintk ( " NFS: received %u auth flavors \n " , entries ) ;
if ( entries > NFS_MAX_SECFLAVORS )
entries = NFS_MAX_SECFLAVORS ;
2010-12-14 17:58:40 +03:00
p = xdr_inline_decode ( xdr , 4 * entries ) ;
2009-06-18 05:02:12 +04:00
if ( unlikely ( p = = NULL ) )
return - EIO ;
if ( entries > * count )
entries = * count ;
for ( i = 0 ; i < entries ; i + + ) {
2010-12-14 17:58:40 +03:00
flavors [ i ] = be32_to_cpup ( p + + ) ;
2010-09-17 18:54:37 +04:00
dprintk ( " NFS: auth flavor[%u]: %d \n " , i , flavors [ i ] ) ;
2009-06-18 05:02:12 +04:00
}
* count = i ;
return 0 ;
}
2010-12-14 17:59:29 +03:00
static int mnt_xdr_dec_mountres3 ( struct rpc_rqst * req ,
struct xdr_stream * xdr ,
struct mountres * res )
2009-06-18 05:02:13 +04:00
{
int status ;
2010-12-14 17:59:29 +03:00
status = decode_fhs_status ( xdr , res ) ;
2009-06-18 05:02:13 +04:00
if ( unlikely ( status ! = 0 | | res - > errno ! = 0 ) )
return status ;
2010-12-14 17:59:29 +03:00
status = decode_fhandle3 ( xdr , res ) ;
2009-06-18 05:02:13 +04:00
if ( unlikely ( status ! = 0 ) ) {
res - > errno = - EBADHANDLE ;
return 0 ;
}
2010-12-14 17:59:29 +03:00
return decode_auth_flavors ( xdr , res ) ;
2009-06-18 05:02:13 +04:00
}
2007-07-01 20:13:33 +04:00
static struct rpc_procinfo mnt_procedures [ ] = {
2009-06-18 05:02:11 +04:00
[ MOUNTPROC_MNT ] = {
. p_proc = MOUNTPROC_MNT ,
2010-12-14 17:59:18 +03:00
. p_encode = ( kxdreproc_t ) mnt_xdr_enc_dirpath ,
2010-12-14 17:59:29 +03:00
. p_decode = ( kxdrdproc_t ) mnt_xdr_dec_mountres ,
2009-06-18 05:02:11 +04:00
. p_arglen = MNT_enc_dirpath_sz ,
2009-06-18 05:02:13 +04:00
. p_replen = MNT_dec_mountres_sz ,
2009-06-18 05:02:11 +04:00
. p_statidx = MOUNTPROC_MNT ,
2007-07-01 20:13:33 +04:00
. p_name = " MOUNT " ,
2005-04-17 02:20:36 +04:00
} ,
2009-08-09 23:09:30 +04:00
[ MOUNTPROC_UMNT ] = {
. p_proc = MOUNTPROC_UMNT ,
2010-12-14 17:59:18 +03:00
. p_encode = ( kxdreproc_t ) mnt_xdr_enc_dirpath ,
2009-08-09 23:09:30 +04:00
. p_arglen = MNT_enc_dirpath_sz ,
. p_statidx = MOUNTPROC_UMNT ,
. p_name = " UMOUNT " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
static struct rpc_procinfo mnt3_procedures [ ] = {
2007-07-01 20:13:33 +04:00
[ MOUNTPROC3_MNT ] = {
. p_proc = MOUNTPROC3_MNT ,
2010-12-14 17:59:18 +03:00
. p_encode = ( kxdreproc_t ) mnt_xdr_enc_dirpath ,
2010-12-14 17:59:29 +03:00
. p_decode = ( kxdrdproc_t ) mnt_xdr_dec_mountres3 ,
2009-06-18 05:02:11 +04:00
. p_arglen = MNT_enc_dirpath_sz ,
2009-06-18 05:02:13 +04:00
. p_replen = MNT_dec_mountres3_sz ,
2007-07-01 20:13:33 +04:00
. p_statidx = MOUNTPROC3_MNT ,
. p_name = " MOUNT " ,
2005-04-17 02:20:36 +04:00
} ,
2009-08-09 23:09:30 +04:00
[ MOUNTPROC3_UMNT ] = {
. p_proc = MOUNTPROC3_UMNT ,
2010-12-14 17:59:18 +03:00
. p_encode = ( kxdreproc_t ) mnt_xdr_enc_dirpath ,
2009-08-09 23:09:30 +04:00
. p_arglen = MNT_enc_dirpath_sz ,
. p_statidx = MOUNTPROC3_UMNT ,
. p_name = " UMOUNT " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2012-01-20 22:53:56 +04:00
static const struct rpc_version mnt_version1 = {
2007-07-01 20:13:33 +04:00
. number = 1 ,
2010-12-10 20:31:14 +03:00
. nrprocs = ARRAY_SIZE ( mnt_procedures ) ,
2007-07-01 20:13:33 +04:00
. procs = mnt_procedures ,
2005-04-17 02:20:36 +04:00
} ;
2012-01-20 22:53:56 +04:00
static const struct rpc_version mnt_version3 = {
2007-07-01 20:13:33 +04:00
. number = 3 ,
2010-12-10 20:31:14 +03:00
. nrprocs = ARRAY_SIZE ( mnt3_procedures ) ,
2007-07-01 20:13:33 +04:00
. procs = mnt3_procedures ,
2005-04-17 02:20:36 +04:00
} ;
2012-01-20 22:53:56 +04:00
static const struct rpc_version * mnt_version [ ] = {
2005-04-17 02:20:36 +04:00
NULL ,
& mnt_version1 ,
NULL ,
& mnt_version3 ,
} ;
2007-07-01 20:13:33 +04:00
static struct rpc_stat mnt_stats ;
2005-04-17 02:20:36 +04:00
2012-01-20 22:53:56 +04:00
static const struct rpc_program mnt_program = {
2005-04-17 02:20:36 +04:00
. name = " mount " ,
. number = NFS_MNT_PROGRAM ,
2006-03-24 14:15:34 +03:00
. nrvers = ARRAY_SIZE ( mnt_version ) ,
2005-04-17 02:20:36 +04:00
. version = mnt_version ,
. stats = & mnt_stats ,
} ;