2019-12-10 15:31:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux / fs / nfs / fs_context . c
*
* Copyright ( C ) 1992 Rick Sladkey
2019-12-10 15:31:13 +03:00
* Conversion to new mount api Copyright ( C ) David Howells
2019-12-10 15:31:04 +03:00
*
* NFS mount handling .
*
* Split from fs / nfs / super . c by David Howells < dhowells @ redhat . com >
*/
# include <linux/module.h>
# include <linux/fs.h>
2019-12-10 15:31:12 +03:00
# include <linux/fs_context.h>
# include <linux/fs_parser.h>
2019-12-10 15:31:04 +03:00
# include <linux/nfs_fs.h>
# include <linux/nfs_mount.h>
# include <linux/nfs4_mount.h>
# include "nfs.h"
# include "internal.h"
# define NFSDBG_FACILITY NFSDBG_MOUNT
# if IS_ENABLED(CONFIG_NFS_V3)
# define NFS_DEFAULT_VERSION 3
# else
# define NFS_DEFAULT_VERSION 2
# endif
# define NFS_MAX_CONNECTIONS 16
2019-12-10 15:31:12 +03:00
enum nfs_param {
Opt_ac ,
Opt_acdirmax ,
Opt_acdirmin ,
Opt_acl ,
Opt_acregmax ,
Opt_acregmin ,
2019-12-10 15:31:04 +03:00
Opt_actimeo ,
2019-12-10 15:31:12 +03:00
Opt_addr ,
Opt_bg ,
Opt_bsize ,
Opt_clientaddr ,
Opt_cto ,
Opt_fg ,
Opt_fscache ,
Opt_hard ,
Opt_intr ,
Opt_local_lock ,
Opt_lock ,
Opt_lookupcache ,
Opt_migration ,
Opt_minorversion ,
Opt_mountaddr ,
Opt_mounthost ,
2019-12-10 15:31:04 +03:00
Opt_mountport ,
2019-12-10 15:31:12 +03:00
Opt_mountproto ,
2019-12-10 15:31:04 +03:00
Opt_mountvers ,
2019-12-10 15:31:12 +03:00
Opt_namelen ,
2019-12-10 15:31:04 +03:00
Opt_nconnect ,
2019-12-10 15:31:12 +03:00
Opt_port ,
Opt_posix ,
Opt_proto ,
Opt_rdirplus ,
Opt_rdma ,
Opt_resvport ,
Opt_retrans ,
Opt_retry ,
Opt_rsize ,
Opt_sec ,
Opt_sharecache ,
Opt_sloppy ,
Opt_soft ,
Opt_softerr ,
Opt_source ,
Opt_tcp ,
Opt_timeo ,
Opt_udp ,
Opt_v ,
Opt_vers ,
Opt_wsize ,
2019-12-10 15:31:04 +03:00
} ;
2019-12-10 15:31:12 +03:00
static const struct fs_parameter_spec nfs_param_specs [ ] = {
fsparam_flag_no ( " ac " , Opt_ac ) ,
fsparam_u32 ( " acdirmax " , Opt_acdirmax ) ,
fsparam_u32 ( " acdirmin " , Opt_acdirmin ) ,
fsparam_flag_no ( " acl " , Opt_acl ) ,
fsparam_u32 ( " acregmax " , Opt_acregmax ) ,
fsparam_u32 ( " acregmin " , Opt_acregmin ) ,
fsparam_u32 ( " actimeo " , Opt_actimeo ) ,
fsparam_string ( " addr " , Opt_addr ) ,
fsparam_flag ( " bg " , Opt_bg ) ,
fsparam_u32 ( " bsize " , Opt_bsize ) ,
fsparam_string ( " clientaddr " , Opt_clientaddr ) ,
fsparam_flag_no ( " cto " , Opt_cto ) ,
fsparam_flag ( " fg " , Opt_fg ) ,
__fsparam ( fs_param_is_string , " fsc " , Opt_fscache ,
fs_param_neg_with_no | fs_param_v_optional ) ,
fsparam_flag ( " hard " , Opt_hard ) ,
__fsparam ( fs_param_is_flag , " intr " , Opt_intr ,
fs_param_neg_with_no | fs_param_deprecated ) ,
fsparam_enum ( " local_lock " , Opt_local_lock ) ,
fsparam_flag_no ( " lock " , Opt_lock ) ,
fsparam_enum ( " lookupcache " , Opt_lookupcache ) ,
fsparam_flag_no ( " migration " , Opt_migration ) ,
fsparam_u32 ( " minorversion " , Opt_minorversion ) ,
fsparam_string ( " mountaddr " , Opt_mountaddr ) ,
fsparam_string ( " mounthost " , Opt_mounthost ) ,
fsparam_u32 ( " mountport " , Opt_mountport ) ,
fsparam_string ( " mountproto " , Opt_mountproto ) ,
fsparam_u32 ( " mountvers " , Opt_mountvers ) ,
fsparam_u32 ( " namlen " , Opt_namelen ) ,
fsparam_u32 ( " nconnect " , Opt_nconnect ) ,
fsparam_string ( " nfsvers " , Opt_vers ) ,
fsparam_u32 ( " port " , Opt_port ) ,
fsparam_flag_no ( " posix " , Opt_posix ) ,
fsparam_string ( " proto " , Opt_proto ) ,
fsparam_flag_no ( " rdirplus " , Opt_rdirplus ) ,
fsparam_flag ( " rdma " , Opt_rdma ) ,
fsparam_flag_no ( " resvport " , Opt_resvport ) ,
fsparam_u32 ( " retrans " , Opt_retrans ) ,
fsparam_string ( " retry " , Opt_retry ) ,
fsparam_u32 ( " rsize " , Opt_rsize ) ,
fsparam_string ( " sec " , Opt_sec ) ,
fsparam_flag_no ( " sharecache " , Opt_sharecache ) ,
fsparam_flag ( " sloppy " , Opt_sloppy ) ,
fsparam_flag ( " soft " , Opt_soft ) ,
fsparam_flag ( " softerr " , Opt_softerr ) ,
fsparam_string ( " source " , Opt_source ) ,
fsparam_flag ( " tcp " , Opt_tcp ) ,
fsparam_u32 ( " timeo " , Opt_timeo ) ,
fsparam_flag ( " udp " , Opt_udp ) ,
fsparam_flag ( " v2 " , Opt_v ) ,
fsparam_flag ( " v3 " , Opt_v ) ,
fsparam_flag ( " v4 " , Opt_v ) ,
fsparam_flag ( " v4.0 " , Opt_v ) ,
fsparam_flag ( " v4.1 " , Opt_v ) ,
fsparam_flag ( " v4.2 " , Opt_v ) ,
fsparam_string ( " vers " , Opt_vers ) ,
fsparam_u32 ( " wsize " , Opt_wsize ) ,
{ }
2019-12-10 15:31:04 +03:00
} ;
enum {
2019-12-10 15:31:12 +03:00
Opt_local_lock_all ,
Opt_local_lock_flock ,
Opt_local_lock_none ,
Opt_local_lock_posix ,
2019-12-10 15:31:04 +03:00
} ;
enum {
2019-12-10 15:31:12 +03:00
Opt_lookupcache_all ,
Opt_lookupcache_none ,
Opt_lookupcache_positive ,
2019-12-10 15:31:04 +03:00
} ;
2019-12-10 15:31:12 +03:00
static const struct fs_parameter_enum nfs_param_enums [ ] = {
{ Opt_local_lock , " all " , Opt_local_lock_all } ,
{ Opt_local_lock , " flock " , Opt_local_lock_flock } ,
{ Opt_local_lock , " none " , Opt_local_lock_none } ,
{ Opt_local_lock , " posix " , Opt_local_lock_posix } ,
{ Opt_lookupcache , " all " , Opt_lookupcache_all } ,
{ Opt_lookupcache , " none " , Opt_lookupcache_none } ,
{ Opt_lookupcache , " pos " , Opt_lookupcache_positive } ,
{ Opt_lookupcache , " positive " , Opt_lookupcache_positive } ,
{ }
} ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:12 +03:00
static const struct fs_parameter_description nfs_fs_parameters = {
. name = " nfs " ,
. specs = nfs_param_specs ,
. enums = nfs_param_enums ,
2019-12-10 15:31:04 +03:00
} ;
enum {
2019-12-10 15:31:12 +03:00
Opt_vers_2 ,
Opt_vers_3 ,
Opt_vers_4 ,
Opt_vers_4_0 ,
Opt_vers_4_1 ,
Opt_vers_4_2 ,
2019-12-10 15:31:04 +03:00
} ;
2019-12-10 15:31:12 +03:00
static const struct constant_table nfs_vers_tokens [ ] = {
{ " 2 " , Opt_vers_2 } ,
{ " 3 " , Opt_vers_3 } ,
{ " 4 " , Opt_vers_4 } ,
{ " 4.0 " , Opt_vers_4_0 } ,
{ " 4.1 " , Opt_vers_4_1 } ,
{ " 4.2 " , Opt_vers_4_2 } ,
2019-12-10 15:31:04 +03:00
} ;
enum {
2019-12-10 15:31:12 +03:00
Opt_xprt_rdma ,
Opt_xprt_rdma6 ,
Opt_xprt_tcp ,
Opt_xprt_tcp6 ,
Opt_xprt_udp ,
Opt_xprt_udp6 ,
nr__Opt_xprt
2019-12-10 15:31:04 +03:00
} ;
2019-12-10 15:31:12 +03:00
static const struct constant_table nfs_xprt_protocol_tokens [ nr__Opt_xprt ] = {
{ " rdma " , Opt_xprt_rdma } ,
{ " rdma6 " , Opt_xprt_rdma6 } ,
{ " tcp " , Opt_xprt_tcp } ,
{ " tcp6 " , Opt_xprt_tcp6 } ,
{ " udp " , Opt_xprt_udp } ,
{ " udp6 " , Opt_xprt_udp6 } ,
2019-12-10 15:31:04 +03:00
} ;
enum {
2019-12-10 15:31:12 +03:00
Opt_sec_krb5 ,
Opt_sec_krb5i ,
Opt_sec_krb5p ,
Opt_sec_lkey ,
Opt_sec_lkeyi ,
Opt_sec_lkeyp ,
Opt_sec_none ,
Opt_sec_spkm ,
Opt_sec_spkmi ,
Opt_sec_spkmp ,
Opt_sec_sys ,
nr__Opt_sec
2019-12-10 15:31:04 +03:00
} ;
2019-12-10 15:31:12 +03:00
static const struct constant_table nfs_secflavor_tokens [ ] = {
{ " krb5 " , Opt_sec_krb5 } ,
{ " krb5i " , Opt_sec_krb5i } ,
{ " krb5p " , Opt_sec_krb5p } ,
{ " lkey " , Opt_sec_lkey } ,
{ " lkeyi " , Opt_sec_lkeyi } ,
{ " lkeyp " , Opt_sec_lkeyp } ,
{ " none " , Opt_sec_none } ,
{ " null " , Opt_sec_none } ,
{ " spkm3 " , Opt_sec_spkm } ,
{ " spkm3i " , Opt_sec_spkmi } ,
{ " spkm3p " , Opt_sec_spkmp } ,
{ " sys " , Opt_sec_sys } ,
2019-12-10 15:31:04 +03:00
} ;
/*
* Sanity - check a server address provided by the mount command .
*
* Address family must be initialized , and address must not be
* the ANY address for that family .
*/
static int nfs_verify_server_address ( struct sockaddr * addr )
{
switch ( addr - > sa_family ) {
case AF_INET : {
struct sockaddr_in * sa = ( struct sockaddr_in * ) addr ;
return sa - > sin_addr . s_addr ! = htonl ( INADDR_ANY ) ;
}
case AF_INET6 : {
struct in6_addr * sa = & ( ( struct sockaddr_in6 * ) addr ) - > sin6_addr ;
return ! ipv6_addr_any ( sa ) ;
}
}
dfprintk ( MOUNT , " NFS: Invalid IP address specified \n " ) ;
return 0 ;
}
/*
* Sanity check the NFS transport protocol .
*
*/
2019-12-10 15:31:06 +03:00
static void nfs_validate_transport_protocol ( struct nfs_fs_context * ctx )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:06 +03:00
switch ( ctx - > nfs_server . protocol ) {
2019-12-10 15:31:04 +03:00
case XPRT_TRANSPORT_UDP :
case XPRT_TRANSPORT_TCP :
case XPRT_TRANSPORT_RDMA :
break ;
default :
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . protocol = XPRT_TRANSPORT_TCP ;
2019-12-10 15:31:04 +03:00
}
}
/*
* For text based NFSv2 / v3 mounts , the mount protocol transport default
* settings should depend upon the specified NFS transport .
*/
2019-12-10 15:31:06 +03:00
static void nfs_set_mount_transport_protocol ( struct nfs_fs_context * ctx )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:06 +03:00
nfs_validate_transport_protocol ( ctx ) ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:06 +03:00
if ( ctx - > mount_server . protocol = = XPRT_TRANSPORT_UDP | |
ctx - > mount_server . protocol = = XPRT_TRANSPORT_TCP )
2019-12-10 15:31:04 +03:00
return ;
2019-12-10 15:31:06 +03:00
switch ( ctx - > nfs_server . protocol ) {
2019-12-10 15:31:04 +03:00
case XPRT_TRANSPORT_UDP :
2019-12-10 15:31:06 +03:00
ctx - > mount_server . protocol = XPRT_TRANSPORT_UDP ;
2019-12-10 15:31:04 +03:00
break ;
case XPRT_TRANSPORT_TCP :
case XPRT_TRANSPORT_RDMA :
2019-12-10 15:31:06 +03:00
ctx - > mount_server . protocol = XPRT_TRANSPORT_TCP ;
2019-12-10 15:31:04 +03:00
}
}
/*
* Add ' flavor ' to ' auth_info ' if not already present .
* Returns true if ' flavor ' ends up in the list , false otherwise
*/
2019-12-10 15:31:14 +03:00
static int nfs_auth_info_add ( struct fs_context * fc ,
2019-12-10 15:31:10 +03:00
struct nfs_auth_info * auth_info ,
rpc_authflavor_t flavor )
2019-12-10 15:31:04 +03:00
{
unsigned int i ;
unsigned int max_flavor_len = ARRAY_SIZE ( auth_info - > flavors ) ;
/* make sure this flavor isn't already in the list */
for ( i = 0 ; i < auth_info - > flavor_len ; i + + ) {
if ( flavor = = auth_info - > flavors [ i ] )
2019-12-10 15:31:10 +03:00
return 0 ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:15 +03:00
if ( auth_info - > flavor_len + 1 > = max_flavor_len )
return nfs_invalf ( fc , " NFS: too many sec= flavors " ) ;
2019-12-10 15:31:04 +03:00
auth_info - > flavors [ auth_info - > flavor_len + + ] = flavor ;
2019-12-10 15:31:10 +03:00
return 0 ;
2019-12-10 15:31:04 +03:00
}
/*
* Parse the value of the ' sec = ' option .
*/
2019-12-10 15:31:14 +03:00
static int nfs_parse_security_flavors ( struct fs_context * fc ,
2019-12-10 15:31:12 +03:00
struct fs_parameter * param )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:14 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
2019-12-10 15:31:04 +03:00
rpc_authflavor_t pseudoflavor ;
2019-12-10 15:31:12 +03:00
char * string = param - > string , * p ;
2019-12-10 15:31:10 +03:00
int ret ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:12 +03:00
dfprintk ( MOUNT , " NFS: parsing %s=%s option \n " , param - > key , param - > string ) ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:12 +03:00
while ( ( p = strsep ( & string , " : " ) ) ! = NULL ) {
if ( ! * p )
continue ;
switch ( lookup_constant ( nfs_secflavor_tokens , p , - 1 ) ) {
2019-12-10 15:31:04 +03:00
case Opt_sec_none :
pseudoflavor = RPC_AUTH_NULL ;
break ;
case Opt_sec_sys :
pseudoflavor = RPC_AUTH_UNIX ;
break ;
case Opt_sec_krb5 :
pseudoflavor = RPC_AUTH_GSS_KRB5 ;
break ;
case Opt_sec_krb5i :
pseudoflavor = RPC_AUTH_GSS_KRB5I ;
break ;
case Opt_sec_krb5p :
pseudoflavor = RPC_AUTH_GSS_KRB5P ;
break ;
case Opt_sec_lkey :
pseudoflavor = RPC_AUTH_GSS_LKEY ;
break ;
case Opt_sec_lkeyi :
pseudoflavor = RPC_AUTH_GSS_LKEYI ;
break ;
case Opt_sec_lkeyp :
pseudoflavor = RPC_AUTH_GSS_LKEYP ;
break ;
case Opt_sec_spkm :
pseudoflavor = RPC_AUTH_GSS_SPKM ;
break ;
case Opt_sec_spkmi :
pseudoflavor = RPC_AUTH_GSS_SPKMI ;
break ;
case Opt_sec_spkmp :
pseudoflavor = RPC_AUTH_GSS_SPKMP ;
break ;
default :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: sec=%s option not recognized " , p ) ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:14 +03:00
ret = nfs_auth_info_add ( fc , & ctx - > auth_info , pseudoflavor ) ;
2019-12-10 15:31:10 +03:00
if ( ret < 0 )
return ret ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:10 +03:00
return 0 ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:14 +03:00
static int nfs_parse_version_string ( struct fs_context * fc ,
2019-12-10 15:31:12 +03:00
const char * string )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:14 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
2019-12-10 15:31:06 +03:00
ctx - > flags & = ~ NFS_MOUNT_VER3 ;
2019-12-10 15:31:12 +03:00
switch ( lookup_constant ( nfs_vers_tokens , string , - 1 ) ) {
2019-12-10 15:31:04 +03:00
case Opt_vers_2 :
2019-12-10 15:31:06 +03:00
ctx - > version = 2 ;
2019-12-10 15:31:04 +03:00
break ;
case Opt_vers_3 :
2019-12-10 15:31:06 +03:00
ctx - > flags | = NFS_MOUNT_VER3 ;
ctx - > version = 3 ;
2019-12-10 15:31:04 +03:00
break ;
case Opt_vers_4 :
/* Backward compatibility option. In future,
* the mount program should always supply
* a NFSv4 minor version number .
*/
2019-12-10 15:31:06 +03:00
ctx - > version = 4 ;
2019-12-10 15:31:04 +03:00
break ;
case Opt_vers_4_0 :
2019-12-10 15:31:06 +03:00
ctx - > version = 4 ;
ctx - > minorversion = 0 ;
2019-12-10 15:31:04 +03:00
break ;
case Opt_vers_4_1 :
2019-12-10 15:31:06 +03:00
ctx - > version = 4 ;
ctx - > minorversion = 1 ;
2019-12-10 15:31:04 +03:00
break ;
case Opt_vers_4_2 :
2019-12-10 15:31:06 +03:00
ctx - > version = 4 ;
ctx - > minorversion = 2 ;
2019-12-10 15:31:04 +03:00
break ;
default :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Unsupported NFS version " ) ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:10 +03:00
return 0 ;
2019-12-10 15:31:04 +03:00
}
/*
2019-12-10 15:31:12 +03:00
* Parse a single mount parameter .
2019-12-10 15:31:04 +03:00
*/
2019-12-10 15:31:13 +03:00
static int nfs_fs_context_parse_param ( struct fs_context * fc ,
2019-12-10 15:31:12 +03:00
struct fs_parameter * param )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:12 +03:00
struct fs_parse_result result ;
2019-12-10 15:31:13 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
2019-12-10 15:31:12 +03:00
unsigned short protofamily , mountfamily ;
unsigned int len ;
int ret , opt ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:12 +03:00
dfprintk ( MOUNT , " NFS: parsing nfs mount option '%s' \n " , param - > key ) ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:13 +03:00
opt = fs_parse ( fc , & nfs_fs_parameters , param , & result ) ;
2019-12-10 15:31:12 +03:00
if ( opt < 0 )
return ctx - > sloppy ? 1 : opt ;
switch ( opt ) {
2019-12-10 15:31:13 +03:00
case Opt_source :
2019-12-10 15:31:15 +03:00
if ( fc - > source )
return nfs_invalf ( fc , " NFS: Multiple sources not supported " ) ;
2019-12-10 15:31:13 +03:00
fc - > source = param - > string ;
param - > string = NULL ;
break ;
2019-12-10 15:31:04 +03:00
/*
* boolean options : foo / nofoo
*/
2019-12-10 15:31:08 +03:00
case Opt_soft :
ctx - > flags | = NFS_MOUNT_SOFT ;
ctx - > flags & = ~ NFS_MOUNT_SOFTERR ;
break ;
case Opt_softerr :
ctx - > flags | = NFS_MOUNT_SOFTERR ;
ctx - > flags & = ~ NFS_MOUNT_SOFT ;
break ;
case Opt_hard :
ctx - > flags & = ~ ( NFS_MOUNT_SOFT | NFS_MOUNT_SOFTERR ) ;
break ;
case Opt_posix :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > flags & = ~ NFS_MOUNT_POSIX ;
else
ctx - > flags | = NFS_MOUNT_POSIX ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_cto :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > flags | = NFS_MOUNT_NOCTO ;
else
ctx - > flags & = ~ NFS_MOUNT_NOCTO ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_ac :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > flags | = NFS_MOUNT_NOAC ;
else
ctx - > flags & = ~ NFS_MOUNT_NOAC ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_lock :
2019-12-10 15:31:12 +03:00
if ( result . negated ) {
ctx - > flags | = NFS_MOUNT_NONLM ;
ctx - > flags | = ( NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL ) ;
} else {
ctx - > flags & = ~ NFS_MOUNT_NONLM ;
ctx - > flags & = ~ ( NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL ) ;
}
2019-12-10 15:31:08 +03:00
break ;
case Opt_udp :
ctx - > flags & = ~ NFS_MOUNT_TCP ;
ctx - > nfs_server . protocol = XPRT_TRANSPORT_UDP ;
break ;
case Opt_tcp :
ctx - > flags | = NFS_MOUNT_TCP ;
ctx - > nfs_server . protocol = XPRT_TRANSPORT_TCP ;
break ;
case Opt_rdma :
ctx - > flags | = NFS_MOUNT_TCP ; /* for side protocols */
ctx - > nfs_server . protocol = XPRT_TRANSPORT_RDMA ;
2019-12-10 15:31:12 +03:00
xprt_load_transport ( param - > key ) ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_acl :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > flags | = NFS_MOUNT_NOACL ;
else
ctx - > flags & = ~ NFS_MOUNT_NOACL ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_rdirplus :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > flags | = NFS_MOUNT_NORDIRPLUS ;
else
ctx - > flags & = ~ NFS_MOUNT_NORDIRPLUS ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_sharecache :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > flags | = NFS_MOUNT_UNSHARED ;
else
ctx - > flags & = ~ NFS_MOUNT_UNSHARED ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_resvport :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > flags | = NFS_MOUNT_NORESVPORT ;
else
ctx - > flags & = ~ NFS_MOUNT_NORESVPORT ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_fscache :
kfree ( ctx - > fscache_uniq ) ;
2019-12-10 15:31:12 +03:00
ctx - > fscache_uniq = param - > string ;
param - > string = NULL ;
if ( result . negated )
ctx - > options & = ~ NFS_OPTION_FSCACHE ;
else
ctx - > options | = NFS_OPTION_FSCACHE ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_migration :
2019-12-10 15:31:12 +03:00
if ( result . negated )
ctx - > options & = ~ NFS_OPTION_MIGRATION ;
else
ctx - > options | = NFS_OPTION_MIGRATION ;
2019-12-10 15:31:08 +03:00
break ;
2019-12-10 15:31:04 +03:00
/*
* options that take numeric values
*/
2019-12-10 15:31:08 +03:00
case Opt_port :
2019-12-10 15:31:12 +03:00
if ( result . uint_32 > USHRT_MAX )
goto out_of_bounds ;
ctx - > nfs_server . port = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_rsize :
2019-12-10 15:31:12 +03:00
ctx - > rsize = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_wsize :
2019-12-10 15:31:12 +03:00
ctx - > wsize = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_bsize :
2019-12-10 15:31:12 +03:00
ctx - > bsize = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_timeo :
2019-12-10 15:31:12 +03:00
if ( result . uint_32 < 1 | | result . uint_32 > INT_MAX )
goto out_of_bounds ;
ctx - > timeo = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_retrans :
2019-12-10 15:31:12 +03:00
if ( result . uint_32 > INT_MAX )
goto out_of_bounds ;
ctx - > retrans = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_acregmin :
2019-12-10 15:31:12 +03:00
ctx - > acregmin = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_acregmax :
2019-12-10 15:31:12 +03:00
ctx - > acregmax = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_acdirmin :
2019-12-10 15:31:12 +03:00
ctx - > acdirmin = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_acdirmax :
2019-12-10 15:31:12 +03:00
ctx - > acdirmax = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_actimeo :
2019-12-10 15:31:12 +03:00
ctx - > acregmin = result . uint_32 ;
ctx - > acregmax = result . uint_32 ;
ctx - > acdirmin = result . uint_32 ;
ctx - > acdirmax = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_namelen :
2019-12-10 15:31:12 +03:00
ctx - > namlen = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_mountport :
2019-12-10 15:31:12 +03:00
if ( result . uint_32 > USHRT_MAX )
goto out_of_bounds ;
ctx - > mount_server . port = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_mountvers :
2019-12-10 15:31:12 +03:00
if ( result . uint_32 < NFS_MNT_VERSION | |
result . uint_32 > NFS_MNT3_VERSION )
goto out_of_bounds ;
ctx - > mount_server . version = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_minorversion :
2019-12-10 15:31:12 +03:00
if ( result . uint_32 > NFS4_MAX_MINOR_VERSION )
goto out_of_bounds ;
ctx - > minorversion = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
2019-12-10 15:31:04 +03:00
/*
* options that take text values
*/
2019-12-10 15:31:12 +03:00
case Opt_v :
2019-12-10 15:31:14 +03:00
ret = nfs_parse_version_string ( fc , param - > key + 1 ) ;
2019-12-10 15:31:12 +03:00
if ( ret < 0 )
return ret ;
break ;
case Opt_vers :
2019-12-10 15:31:14 +03:00
ret = nfs_parse_version_string ( fc , param - > string ) ;
2019-12-10 15:31:10 +03:00
if ( ret < 0 )
return ret ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_sec :
2019-12-10 15:31:14 +03:00
ret = nfs_parse_security_flavors ( fc , param ) ;
2019-12-10 15:31:10 +03:00
if ( ret < 0 )
return ret ;
2019-12-10 15:31:08 +03:00
break ;
2019-12-10 15:31:12 +03:00
case Opt_proto :
protofamily = AF_INET ;
switch ( lookup_constant ( nfs_xprt_protocol_tokens , param - > string , - 1 ) ) {
2019-12-10 15:31:08 +03:00
case Opt_xprt_udp6 :
2019-12-10 15:31:12 +03:00
protofamily = AF_INET6 ;
2019-12-10 15:31:08 +03:00
/* fall through */
case Opt_xprt_udp :
ctx - > flags & = ~ NFS_MOUNT_TCP ;
ctx - > nfs_server . protocol = XPRT_TRANSPORT_UDP ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
case Opt_xprt_tcp6 :
2019-12-10 15:31:12 +03:00
protofamily = AF_INET6 ;
2019-12-10 15:31:08 +03:00
/* fall through */
case Opt_xprt_tcp :
ctx - > flags | = NFS_MOUNT_TCP ;
ctx - > nfs_server . protocol = XPRT_TRANSPORT_TCP ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
case Opt_xprt_rdma6 :
2019-12-10 15:31:12 +03:00
protofamily = AF_INET6 ;
2019-12-10 15:31:08 +03:00
/* fall through */
case Opt_xprt_rdma :
/* vector side protocols to TCP */
ctx - > flags | = NFS_MOUNT_TCP ;
ctx - > nfs_server . protocol = XPRT_TRANSPORT_RDMA ;
2019-12-10 15:31:12 +03:00
xprt_load_transport ( param - > string ) ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
default :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Unrecognized transport protocol " ) ;
2019-12-10 15:31:08 +03:00
}
2019-12-10 15:31:12 +03:00
ctx - > protofamily = protofamily ;
2019-12-10 15:31:08 +03:00
break ;
2019-12-10 15:31:12 +03:00
2019-12-10 15:31:08 +03:00
case Opt_mountproto :
2019-12-10 15:31:12 +03:00
mountfamily = AF_INET ;
switch ( lookup_constant ( nfs_xprt_protocol_tokens , param - > string , - 1 ) ) {
2019-12-10 15:31:08 +03:00
case Opt_xprt_udp6 :
2019-12-10 15:31:12 +03:00
mountfamily = AF_INET6 ;
2019-12-10 15:31:08 +03:00
/* fall through */
case Opt_xprt_udp :
ctx - > mount_server . protocol = XPRT_TRANSPORT_UDP ;
break ;
case Opt_xprt_tcp6 :
2019-12-10 15:31:12 +03:00
mountfamily = AF_INET6 ;
2019-12-10 15:31:08 +03:00
/* fall through */
case Opt_xprt_tcp :
ctx - > mount_server . protocol = XPRT_TRANSPORT_TCP ;
break ;
case Opt_xprt_rdma : /* not used for side protocols */
default :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Unrecognized transport protocol " ) ;
2019-12-10 15:31:08 +03:00
}
2019-12-10 15:31:12 +03:00
ctx - > mountfamily = mountfamily ;
2019-12-10 15:31:08 +03:00
break ;
2019-12-10 15:31:12 +03:00
2019-12-10 15:31:08 +03:00
case Opt_addr :
2019-12-10 15:31:14 +03:00
len = rpc_pton ( fc - > net_ns , param - > string , param - > size ,
2019-12-10 15:31:12 +03:00
& ctx - > nfs_server . address ,
sizeof ( ctx - > nfs_server . _address ) ) ;
if ( len = = 0 )
2019-12-10 15:31:08 +03:00
goto out_invalid_address ;
2019-12-10 15:31:12 +03:00
ctx - > nfs_server . addrlen = len ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_clientaddr :
2019-12-10 15:31:12 +03:00
kfree ( ctx - > client_address ) ;
ctx - > client_address = param - > string ;
param - > string = NULL ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_mounthost :
2019-12-10 15:31:12 +03:00
kfree ( ctx - > mount_server . hostname ) ;
ctx - > mount_server . hostname = param - > string ;
param - > string = NULL ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_mountaddr :
2019-12-10 15:31:14 +03:00
len = rpc_pton ( fc - > net_ns , param - > string , param - > size ,
2019-12-10 15:31:12 +03:00
& ctx - > mount_server . address ,
sizeof ( ctx - > mount_server . _address ) ) ;
if ( len = = 0 )
2019-12-10 15:31:08 +03:00
goto out_invalid_address ;
2019-12-10 15:31:12 +03:00
ctx - > mount_server . addrlen = len ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_nconnect :
2019-12-10 15:31:12 +03:00
if ( result . uint_32 < 1 | | result . uint_32 > NFS_MAX_CONNECTIONS )
goto out_of_bounds ;
ctx - > nfs_server . nconnect = result . uint_32 ;
2019-12-10 15:31:08 +03:00
break ;
case Opt_lookupcache :
2019-12-10 15:31:12 +03:00
switch ( result . uint_32 ) {
2019-12-10 15:31:08 +03:00
case Opt_lookupcache_all :
ctx - > flags & = ~ ( NFS_MOUNT_LOOKUP_CACHE_NONEG | NFS_MOUNT_LOOKUP_CACHE_NONE ) ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
case Opt_lookupcache_positive :
ctx - > flags & = ~ NFS_MOUNT_LOOKUP_CACHE_NONE ;
ctx - > flags | = NFS_MOUNT_LOOKUP_CACHE_NONEG ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
case Opt_lookupcache_none :
ctx - > flags | = NFS_MOUNT_LOOKUP_CACHE_NONEG | NFS_MOUNT_LOOKUP_CACHE_NONE ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
default :
2019-12-10 15:31:12 +03:00
goto out_invalid_value ;
2019-12-10 15:31:08 +03:00
}
break ;
case Opt_local_lock :
2019-12-10 15:31:12 +03:00
switch ( result . uint_32 ) {
2019-12-10 15:31:08 +03:00
case Opt_local_lock_all :
ctx - > flags | = ( NFS_MOUNT_LOCAL_FLOCK |
NFS_MOUNT_LOCAL_FCNTL ) ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
case Opt_local_lock_flock :
ctx - > flags | = NFS_MOUNT_LOCAL_FLOCK ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
case Opt_local_lock_posix :
ctx - > flags | = NFS_MOUNT_LOCAL_FCNTL ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
case Opt_local_lock_none :
ctx - > flags & = ~ ( NFS_MOUNT_LOCAL_FLOCK |
NFS_MOUNT_LOCAL_FCNTL ) ;
2019-12-10 15:31:04 +03:00
break ;
2019-12-10 15:31:08 +03:00
default :
2019-12-10 15:31:12 +03:00
goto out_invalid_value ;
2019-12-10 15:31:08 +03:00
}
break ;
2019-12-10 15:31:04 +03:00
/*
* Special options
*/
2019-12-10 15:31:08 +03:00
case Opt_sloppy :
2019-12-10 15:31:12 +03:00
ctx - > sloppy = true ;
2019-12-10 15:31:08 +03:00
dfprintk ( MOUNT , " NFS: relaxing parsing rules \n " ) ;
break ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:07 +03:00
return 0 ;
out_invalid_value :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Bad mount option value specified " ) ;
2019-12-10 15:31:12 +03:00
out_invalid_address :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Bad IP address specified " ) ;
2019-12-10 15:31:12 +03:00
out_of_bounds :
2019-12-10 15:31:15 +03:00
nfs_invalf ( fc , " NFS: Value for '%s' out of range " , param - > key ) ;
2019-12-10 15:31:12 +03:00
return - ERANGE ;
}
2019-12-10 15:31:04 +03:00
/*
2019-12-10 15:31:14 +03:00
* Split fc - > source into " hostname:export_path " .
2019-12-10 15:31:04 +03:00
*
* The leftmost colon demarks the split between the server ' s hostname
* and the export path . If the hostname starts with a left square
* bracket , then it may contain colons .
*
* Note : caller frees hostname and export path , even on error .
*/
2019-12-10 15:31:14 +03:00
static int nfs_parse_source ( struct fs_context * fc ,
size_t maxnamlen , size_t maxpathlen )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:14 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
const char * dev_name = fc - > source ;
2019-12-10 15:31:04 +03:00
size_t len ;
2019-12-10 15:31:14 +03:00
const char * end ;
2019-12-10 15:31:04 +03:00
if ( unlikely ( ! dev_name | | ! * dev_name ) ) {
dfprintk ( MOUNT , " NFS: device name not specified \n " ) ;
return - EINVAL ;
}
/* Is the host name protected with square brakcets? */
if ( * dev_name = = ' [ ' ) {
end = strchr ( + + dev_name , ' ] ' ) ;
if ( end = = NULL | | end [ 1 ] ! = ' : ' )
goto out_bad_devname ;
len = end - dev_name ;
end + + ;
} else {
2019-12-10 15:31:14 +03:00
const char * comma ;
2019-12-10 15:31:04 +03:00
end = strchr ( dev_name , ' : ' ) ;
if ( end = = NULL )
goto out_bad_devname ;
len = end - dev_name ;
/* kill possible hostname list: not supported */
2019-12-10 15:31:14 +03:00
comma = memchr ( dev_name , ' , ' , len ) ;
if ( comma )
2019-12-10 15:31:04 +03:00
len = comma - dev_name ;
}
if ( len > maxnamlen )
goto out_hostname ;
/* N.B. caller will free nfs_server.hostname in all cases */
2019-12-10 15:31:10 +03:00
ctx - > nfs_server . hostname = kmemdup_nul ( dev_name , len , GFP_KERNEL ) ;
if ( ! ctx - > nfs_server . hostname )
2019-12-10 15:31:04 +03:00
goto out_nomem ;
len = strlen ( + + end ) ;
if ( len > maxpathlen )
goto out_path ;
2019-12-10 15:31:10 +03:00
ctx - > nfs_server . export_path = kmemdup_nul ( end , len , GFP_KERNEL ) ;
if ( ! ctx - > nfs_server . export_path )
2019-12-10 15:31:04 +03:00
goto out_nomem ;
2019-12-10 15:31:10 +03:00
dfprintk ( MOUNT , " NFS: MNTPATH: '%s' \n " , ctx - > nfs_server . export_path ) ;
2019-12-10 15:31:04 +03:00
return 0 ;
out_bad_devname :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: device name not in host:path format " ) ;
2019-12-10 15:31:04 +03:00
out_nomem :
2019-12-10 15:31:15 +03:00
nfs_errorf ( fc , " NFS: not enough memory to parse device name " ) ;
2019-12-10 15:31:04 +03:00
return - ENOMEM ;
out_hostname :
2019-12-10 15:31:15 +03:00
nfs_errorf ( fc , " NFS: server hostname too long " ) ;
2019-12-10 15:31:04 +03:00
return - ENAMETOOLONG ;
out_path :
2019-12-10 15:31:15 +03:00
nfs_errorf ( fc , " NFS: export pathname too long " ) ;
2019-12-10 15:31:04 +03:00
return - ENAMETOOLONG ;
}
2019-12-10 15:31:13 +03:00
static inline bool is_remount_fc ( struct fs_context * fc )
{
return fc - > root ! = NULL ;
}
2019-12-10 15:31:04 +03:00
/*
2019-12-10 15:31:10 +03:00
* Parse monolithic NFS2 / NFS3 mount data
2019-12-10 15:31:04 +03:00
* - fills in the mount root filehandle
*
* For option strings , user space handles the following behaviors :
*
* + DNS : mapping server host name to IP address ( " addr= " option )
*
* + failure mode : how to behave if a mount request can ' t be handled
* immediately ( " fg/bg " option )
*
* + retry : how often to retry a mount request ( " retry= " option )
*
* + breaking back : trying proto = udp after proto = tcp , v2 after v3 ,
* mountproto = tcp after mountproto = udp , and so on
*/
2019-12-10 15:31:13 +03:00
static int nfs23_parse_monolithic ( struct fs_context * fc ,
struct nfs_mount_data * data )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:13 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
2019-12-10 15:31:14 +03:00
struct nfs_fh * mntfh = ctx - > mntfh ;
2019-12-10 15:31:06 +03:00
struct sockaddr * sap = ( struct sockaddr * ) & ctx - > nfs_server . address ;
2019-12-10 15:31:04 +03:00
int extra_flags = NFS_MOUNT_LEGACY_INTERFACE ;
if ( data = = NULL )
goto out_no_data ;
2019-12-10 15:31:06 +03:00
ctx - > version = NFS_DEFAULT_VERSION ;
2019-12-10 15:31:04 +03:00
switch ( data - > version ) {
case 1 :
data - > namlen = 0 ; /* fall through */
case 2 :
data - > bsize = 0 ; /* fall through */
case 3 :
if ( data - > flags & NFS_MOUNT_VER3 )
goto out_no_v3 ;
data - > root . size = NFS2_FHSIZE ;
memcpy ( data - > root . data , data - > old_root . data , NFS2_FHSIZE ) ;
/* Turn off security negotiation */
extra_flags | = NFS_MOUNT_SECFLAVOUR ;
/* fall through */
case 4 :
if ( data - > flags & NFS_MOUNT_SECFLAVOUR )
goto out_no_sec ;
/* fall through */
case 5 :
memset ( data - > context , 0 , sizeof ( data - > context ) ) ;
/* fall through */
case 6 :
if ( data - > flags & NFS_MOUNT_VER3 ) {
if ( data - > root . size > NFS3_FHSIZE | | data - > root . size = = 0 )
goto out_invalid_fh ;
mntfh - > size = data - > root . size ;
2019-12-10 15:31:06 +03:00
ctx - > version = 3 ;
2019-12-10 15:31:04 +03:00
} else {
mntfh - > size = NFS2_FHSIZE ;
2019-12-10 15:31:06 +03:00
ctx - > version = 2 ;
2019-12-10 15:31:04 +03:00
}
memcpy ( mntfh - > data , data - > root . data , mntfh - > size ) ;
if ( mntfh - > size < sizeof ( mntfh - > data ) )
memset ( mntfh - > data + mntfh - > size , 0 ,
sizeof ( mntfh - > data ) - mntfh - > size ) ;
/*
2019-12-10 15:31:06 +03:00
* Translate to nfs_fs_context , which nfs_fill_super
2019-12-10 15:31:04 +03:00
* can deal with .
*/
2019-12-10 15:31:06 +03:00
ctx - > flags = data - > flags & NFS_MOUNT_FLAGMASK ;
ctx - > flags | = extra_flags ;
ctx - > rsize = data - > rsize ;
ctx - > wsize = data - > wsize ;
ctx - > timeo = data - > timeo ;
ctx - > retrans = data - > retrans ;
ctx - > acregmin = data - > acregmin ;
ctx - > acregmax = data - > acregmax ;
ctx - > acdirmin = data - > acdirmin ;
ctx - > acdirmax = data - > acdirmax ;
ctx - > need_mount = false ;
2019-12-10 15:31:04 +03:00
memcpy ( sap , & data - > addr , sizeof ( data - > addr ) ) ;
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . addrlen = sizeof ( data - > addr ) ;
ctx - > nfs_server . port = ntohs ( data - > addr . sin_port ) ;
2019-12-10 15:31:04 +03:00
if ( sap - > sa_family ! = AF_INET | |
! nfs_verify_server_address ( sap ) )
goto out_no_address ;
if ( ! ( data - > flags & NFS_MOUNT_TCP ) )
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . protocol = XPRT_TRANSPORT_UDP ;
2019-12-10 15:31:04 +03:00
/* N.B. caller will free nfs_server.hostname in all cases */
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . hostname = kstrdup ( data - > hostname , GFP_KERNEL ) ;
2019-12-10 15:31:13 +03:00
if ( ! ctx - > nfs_server . hostname )
goto out_nomem ;
2019-12-10 15:31:06 +03:00
ctx - > namlen = data - > namlen ;
ctx - > bsize = data - > bsize ;
2019-12-10 15:31:04 +03:00
if ( data - > flags & NFS_MOUNT_SECFLAVOUR )
2019-12-10 15:31:06 +03:00
ctx - > selected_flavor = data - > pseudoflavor ;
2019-12-10 15:31:04 +03:00
else
2019-12-10 15:31:06 +03:00
ctx - > selected_flavor = RPC_AUTH_UNIX ;
2019-12-10 15:31:04 +03:00
if ( ! ( data - > flags & NFS_MOUNT_NONLM ) )
2019-12-10 15:31:06 +03:00
ctx - > flags & = ~ ( NFS_MOUNT_LOCAL_FLOCK |
2019-12-10 15:31:04 +03:00
NFS_MOUNT_LOCAL_FCNTL ) ;
else
2019-12-10 15:31:06 +03:00
ctx - > flags | = ( NFS_MOUNT_LOCAL_FLOCK |
2019-12-10 15:31:04 +03:00
NFS_MOUNT_LOCAL_FCNTL ) ;
2019-12-10 15:31:14 +03:00
2019-12-10 15:31:04 +03:00
/*
* The legacy version 6 binary mount data from userspace has a
* field used only to transport selinux information into the
* the kernel . To continue to support that functionality we
* have a touch of selinux knowledge here in the NFS code . The
* userspace code converted context = blah to just blah so we are
* converting back to the full string selinux understands .
*/
if ( data - > context [ 0 ] ) {
# ifdef CONFIG_SECURITY_SELINUX
2019-12-10 15:31:13 +03:00
int ret ;
2019-12-10 15:31:04 +03:00
data - > context [ NFS_MAX_CONTEXT_LEN ] = ' \0 ' ;
2019-12-10 15:31:13 +03:00
ret = vfs_parse_fs_string ( fc , " context " ,
data - > context , strlen ( data - > context ) ) ;
if ( ret < 0 )
return ret ;
2019-12-10 15:31:04 +03:00
# else
return - EINVAL ;
# endif
}
break ;
default :
2019-12-10 15:31:13 +03:00
goto generic ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:13 +03:00
ctx - > skip_reconfig_option_check = true ;
2019-12-10 15:31:04 +03:00
return 0 ;
2019-12-10 15:31:13 +03:00
generic :
return generic_parse_monolithic ( fc , data ) ;
2019-12-10 15:31:04 +03:00
out_no_data :
2019-12-10 15:31:13 +03:00
if ( is_remount_fc ( fc ) ) {
ctx - > skip_reconfig_option_check = true ;
return 0 ;
}
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: mount program didn't pass any mount data " ) ;
2019-12-10 15:31:04 +03:00
out_no_v3 :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: nfs_mount_data version does not support v3 " ) ;
2019-12-10 15:31:04 +03:00
out_no_sec :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: nfs_mount_data version supports only AUTH_SYS " ) ;
2019-12-10 15:31:04 +03:00
out_nomem :
2019-12-10 15:31:15 +03:00
dfprintk ( MOUNT , " NFS: not enough memory to handle mount options " ) ;
2019-12-10 15:31:04 +03:00
return - ENOMEM ;
out_no_address :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: mount program didn't pass remote address " ) ;
2019-12-10 15:31:04 +03:00
out_invalid_fh :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: invalid root filehandle " ) ;
2019-12-10 15:31:04 +03:00
}
# if IS_ENABLED(CONFIG_NFS_V4)
/*
* Validate NFSv4 mount options
*/
2019-12-10 15:31:13 +03:00
static int nfs4_parse_monolithic ( struct fs_context * fc ,
struct nfs4_mount_data * data )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:13 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
2019-12-10 15:31:06 +03:00
struct sockaddr * sap = ( struct sockaddr * ) & ctx - > nfs_server . address ;
2019-12-10 15:31:04 +03:00
char * c ;
if ( data = = NULL )
goto out_no_data ;
2019-12-10 15:31:06 +03:00
ctx - > version = 4 ;
2019-12-10 15:31:04 +03:00
switch ( data - > version ) {
case 1 :
2019-12-10 15:31:06 +03:00
if ( data - > host_addrlen > sizeof ( ctx - > nfs_server . address ) )
2019-12-10 15:31:04 +03:00
goto out_no_address ;
if ( data - > host_addrlen = = 0 )
goto out_no_address ;
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . addrlen = data - > host_addrlen ;
2019-12-10 15:31:04 +03:00
if ( copy_from_user ( sap , data - > host_addr , data - > host_addrlen ) )
return - EFAULT ;
if ( ! nfs_verify_server_address ( sap ) )
goto out_no_address ;
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . port = ntohs ( ( ( struct sockaddr_in * ) sap ) - > sin_port ) ;
2019-12-10 15:31:04 +03:00
if ( data - > auth_flavourlen ) {
rpc_authflavor_t pseudoflavor ;
if ( data - > auth_flavourlen > 1 )
goto out_inval_auth ;
if ( copy_from_user ( & pseudoflavor ,
data - > auth_flavours ,
sizeof ( pseudoflavor ) ) )
return - EFAULT ;
2019-12-10 15:31:06 +03:00
ctx - > selected_flavor = pseudoflavor ;
2019-12-10 15:31:04 +03:00
} else
2019-12-10 15:31:06 +03:00
ctx - > selected_flavor = RPC_AUTH_UNIX ;
2019-12-10 15:31:04 +03:00
c = strndup_user ( data - > hostname . data , NFS4_MAXNAMLEN ) ;
if ( IS_ERR ( c ) )
return PTR_ERR ( c ) ;
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . hostname = c ;
2019-12-10 15:31:04 +03:00
c = strndup_user ( data - > mnt_path . data , NFS4_MAXPATHLEN ) ;
if ( IS_ERR ( c ) )
return PTR_ERR ( c ) ;
2019-12-10 15:31:06 +03:00
ctx - > nfs_server . export_path = c ;
2019-12-10 15:31:04 +03:00
dfprintk ( MOUNT , " NFS: MNTPATH: '%s' \n " , c ) ;
c = strndup_user ( data - > client_addr . data , 16 ) ;
if ( IS_ERR ( c ) )
return PTR_ERR ( c ) ;
2019-12-10 15:31:06 +03:00
ctx - > client_address = c ;
2019-12-10 15:31:04 +03:00
/*
2019-12-10 15:31:13 +03:00
* Translate to nfs_fs_context , which nfs_fill_super
2019-12-10 15:31:04 +03:00
* can deal with .
*/
2019-12-10 15:31:06 +03:00
ctx - > flags = data - > flags & NFS4_MOUNT_FLAGMASK ;
ctx - > rsize = data - > rsize ;
ctx - > wsize = data - > wsize ;
ctx - > timeo = data - > timeo ;
ctx - > retrans = data - > retrans ;
ctx - > acregmin = data - > acregmin ;
ctx - > acregmax = data - > acregmax ;
ctx - > acdirmin = data - > acdirmin ;
ctx - > acdirmax = data - > acdirmax ;
ctx - > nfs_server . protocol = data - > proto ;
nfs_validate_transport_protocol ( ctx ) ;
if ( ctx - > nfs_server . protocol = = XPRT_TRANSPORT_UDP )
2019-12-10 15:31:04 +03:00
goto out_invalid_transport_udp ;
break ;
default :
2019-12-10 15:31:13 +03:00
goto generic ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:13 +03:00
ctx - > skip_reconfig_option_check = true ;
2019-12-10 15:31:04 +03:00
return 0 ;
2019-12-10 15:31:13 +03:00
generic :
return generic_parse_monolithic ( fc , data ) ;
2019-12-10 15:31:04 +03:00
out_no_data :
2019-12-10 15:31:13 +03:00
if ( is_remount_fc ( fc ) ) {
ctx - > skip_reconfig_option_check = true ;
return 0 ;
}
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS4: mount program didn't pass any mount data " ) ;
2019-12-10 15:31:04 +03:00
out_inval_auth :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS4: Invalid number of RPC auth flavours %d " ,
data - > auth_flavourlen ) ;
2019-12-10 15:31:04 +03:00
out_no_address :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS4: mount program didn't pass remote address " ) ;
2019-12-10 15:31:04 +03:00
out_invalid_transport_udp :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFSv4: Unsupported transport protocol udp " ) ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:13 +03:00
# endif
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:13 +03:00
/*
* Parse a monolithic block of data from sys_mount ( ) .
*/
static int nfs_fs_context_parse_monolithic ( struct fs_context * fc ,
void * data )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:13 +03:00
if ( fc - > fs_type = = & nfs_fs_type )
return nfs23_parse_monolithic ( fc , data ) ;
# if IS_ENABLED(CONFIG_NFS_V4)
if ( fc - > fs_type = = & nfs4_fs_type )
return nfs4_parse_monolithic ( fc , data ) ;
2019-12-10 15:31:04 +03:00
# endif
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Unsupported monolithic data version " ) ;
2019-12-10 15:31:13 +03:00
}
/*
* Validate the preparsed information in the config .
*/
static int nfs_fs_context_validate ( struct fs_context * fc )
2019-12-10 15:31:04 +03:00
{
2019-12-10 15:31:13 +03:00
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
struct nfs_subversion * nfs_mod ;
struct sockaddr * sap = ( struct sockaddr * ) & ctx - > nfs_server . address ;
2019-12-10 15:31:04 +03:00
int max_namelen = PAGE_SIZE ;
int max_pathlen = NFS_MAXPATHLEN ;
2019-12-10 15:31:13 +03:00
int port = 0 ;
int ret ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:13 +03:00
if ( ! fc - > source )
goto out_no_device_name ;
/* Check for sanity first. */
if ( ctx - > minorversion & & ctx - > version ! = 4 )
goto out_minorversion_mismatch ;
if ( ctx - > options & NFS_OPTION_MIGRATION & &
( ctx - > version ! = 4 | | ctx - > minorversion ! = 0 ) )
goto out_migration_misuse ;
/* Verify that any proto=/mountproto= options match the address
* families in the addr = / mountaddr = options .
*/
if ( ctx - > protofamily ! = AF_UNSPEC & &
ctx - > protofamily ! = ctx - > nfs_server . address . sa_family )
goto out_proto_mismatch ;
if ( ctx - > mountfamily ! = AF_UNSPEC ) {
if ( ctx - > mount_server . addrlen ) {
if ( ctx - > mountfamily ! = ctx - > mount_server . address . sa_family )
goto out_mountproto_mismatch ;
} else {
if ( ctx - > mountfamily ! = ctx - > nfs_server . address . sa_family )
goto out_mountproto_mismatch ;
}
}
2019-12-10 15:31:04 +03:00
if ( ! nfs_verify_server_address ( sap ) )
goto out_no_address ;
2019-12-10 15:31:06 +03:00
if ( ctx - > version = = 4 ) {
2019-12-10 15:31:14 +03:00
if ( IS_ENABLED ( CONFIG_NFS_V4 ) ) {
if ( ctx - > nfs_server . protocol = = XPRT_TRANSPORT_RDMA )
port = NFS_RDMA_PORT ;
else
port = NFS_PORT ;
max_namelen = NFS4_MAXNAMLEN ;
max_pathlen = NFS4_MAXPATHLEN ;
nfs_validate_transport_protocol ( ctx ) ;
if ( ctx - > nfs_server . protocol = = XPRT_TRANSPORT_UDP )
goto out_invalid_transport_udp ;
ctx - > flags & = ~ ( NFS_MOUNT_NONLM | NFS_MOUNT_NOACL |
NFS_MOUNT_VER3 | NFS_MOUNT_LOCAL_FLOCK |
NFS_MOUNT_LOCAL_FCNTL ) ;
} else {
goto out_v4_not_compiled ;
}
2019-12-10 15:31:04 +03:00
} else {
2019-12-10 15:31:06 +03:00
nfs_set_mount_transport_protocol ( ctx ) ;
if ( ctx - > nfs_server . protocol = = XPRT_TRANSPORT_RDMA )
2019-12-10 15:31:04 +03:00
port = NFS_RDMA_PORT ;
}
2019-12-10 15:31:06 +03:00
nfs_set_port ( sap , & ctx - > nfs_server . port , port ) ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:14 +03:00
ret = nfs_parse_source ( fc , max_namelen , max_pathlen ) ;
2019-12-10 15:31:13 +03:00
if ( ret < 0 )
return ret ;
/* Load the NFS protocol module if we haven't done so yet */
2019-12-10 15:31:14 +03:00
if ( ! ctx - > nfs_mod ) {
2019-12-10 15:31:13 +03:00
nfs_mod = get_nfs_version ( ctx - > version ) ;
if ( IS_ERR ( nfs_mod ) ) {
ret = PTR_ERR ( nfs_mod ) ;
goto out_version_unavailable ;
}
2019-12-10 15:31:14 +03:00
ctx - > nfs_mod = nfs_mod ;
2019-12-10 15:31:13 +03:00
}
return 0 ;
2019-12-10 15:31:04 +03:00
2019-12-10 15:31:13 +03:00
out_no_device_name :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Device name not specified " ) ;
2019-12-10 15:31:04 +03:00
out_v4_not_compiled :
2019-12-10 15:31:15 +03:00
nfs_errorf ( fc , " NFS: NFSv4 is not compiled into kernel " ) ;
2019-12-10 15:31:04 +03:00
return - EPROTONOSUPPORT ;
out_invalid_transport_udp :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFSv4: Unsupported transport protocol udp " ) ;
2019-12-10 15:31:04 +03:00
out_no_address :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: mount program didn't pass remote address " ) ;
2019-12-10 15:31:13 +03:00
out_mountproto_mismatch :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Mount server address does not match mountproto= option " ) ;
2019-12-10 15:31:13 +03:00
out_proto_mismatch :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Server address does not match proto= option " ) ;
2019-12-10 15:31:13 +03:00
out_minorversion_mismatch :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: Mount option vers=%u does not support minorversion=%u " ,
2019-12-10 15:31:13 +03:00
ctx - > version , ctx - > minorversion ) ;
out_migration_misuse :
2019-12-10 15:31:15 +03:00
return nfs_invalf ( fc , " NFS: 'Migration' not supported for this NFS version " ) ;
2019-12-10 15:31:13 +03:00
out_version_unavailable :
2019-12-10 15:31:15 +03:00
nfs_errorf ( fc , " NFS: Version unavailable " ) ;
2019-12-10 15:31:13 +03:00
return ret ;
}
/*
* Create an NFS superblock by the appropriate method .
*/
static int nfs_get_tree ( struct fs_context * fc )
{
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
int err = nfs_fs_context_validate ( fc ) ;
if ( err )
return err ;
if ( ! ctx - > internal )
2019-12-10 15:31:14 +03:00
return ctx - > nfs_mod - > rpc_ops - > try_get_tree ( fc ) ;
2019-12-10 15:31:13 +03:00
else
return nfs_get_tree_common ( fc ) ;
2019-12-10 15:31:04 +03:00
}
2019-12-10 15:31:13 +03:00
/*
* Handle duplication of a configuration . The caller copied * src into * sc , but
* it can ' t deal with resource pointers in the filesystem context , so we have
* to do that . We need to clear pointers , copy data or get extra refs as
* appropriate .
*/
static int nfs_fs_context_dup ( struct fs_context * fc , struct fs_context * src_fc )
{
struct nfs_fs_context * src = nfs_fc2context ( src_fc ) , * ctx ;
ctx = kmemdup ( src , sizeof ( struct nfs_fs_context ) , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
2019-12-10 15:31:14 +03:00
ctx - > mntfh = nfs_alloc_fhandle ( ) ;
if ( ! ctx - > mntfh ) {
2019-12-10 15:31:13 +03:00
kfree ( ctx ) ;
return - ENOMEM ;
}
2019-12-10 15:31:14 +03:00
nfs_copy_fh ( ctx - > mntfh , src - > mntfh ) ;
2019-12-10 15:31:13 +03:00
2019-12-10 15:31:14 +03:00
__module_get ( ctx - > nfs_mod - > owner ) ;
2019-12-10 15:31:13 +03:00
ctx - > client_address = NULL ;
ctx - > mount_server . hostname = NULL ;
ctx - > nfs_server . export_path = NULL ;
ctx - > nfs_server . hostname = NULL ;
ctx - > fscache_uniq = NULL ;
ctx - > clone_data . fattr = NULL ;
fc - > fs_private = ctx ;
return 0 ;
}
static void nfs_fs_context_free ( struct fs_context * fc )
{
struct nfs_fs_context * ctx = nfs_fc2context ( fc ) ;
if ( ctx ) {
2019-12-10 15:31:14 +03:00
if ( ctx - > server )
nfs_free_server ( ctx - > server ) ;
if ( ctx - > nfs_mod )
put_nfs_version ( ctx - > nfs_mod ) ;
2019-12-10 15:31:13 +03:00
kfree ( ctx - > client_address ) ;
kfree ( ctx - > mount_server . hostname ) ;
kfree ( ctx - > nfs_server . export_path ) ;
kfree ( ctx - > nfs_server . hostname ) ;
kfree ( ctx - > fscache_uniq ) ;
2019-12-10 15:31:14 +03:00
nfs_free_fhandle ( ctx - > mntfh ) ;
2019-12-10 15:31:13 +03:00
nfs_free_fattr ( ctx - > clone_data . fattr ) ;
kfree ( ctx ) ;
}
}
static const struct fs_context_operations nfs_fs_context_ops = {
. free = nfs_fs_context_free ,
. dup = nfs_fs_context_dup ,
. parse_param = nfs_fs_context_parse_param ,
. parse_monolithic = nfs_fs_context_parse_monolithic ,
. get_tree = nfs_get_tree ,
. reconfigure = nfs_reconfigure ,
} ;
/*
* Prepare superblock configuration . We use the namespaces attached to the
* context . This may be the current process ' s namespaces , or it may be a
* container ' s namespaces .
*/
static int nfs_init_fs_context ( struct fs_context * fc )
{
struct nfs_fs_context * ctx ;
ctx = kzalloc ( sizeof ( struct nfs_fs_context ) , GFP_KERNEL ) ;
if ( unlikely ( ! ctx ) )
return - ENOMEM ;
2019-12-10 15:31:14 +03:00
ctx - > mntfh = nfs_alloc_fhandle ( ) ;
if ( unlikely ( ! ctx - > mntfh ) ) {
2019-12-10 15:31:13 +03:00
kfree ( ctx ) ;
return - ENOMEM ;
}
ctx - > protofamily = AF_UNSPEC ;
ctx - > mountfamily = AF_UNSPEC ;
ctx - > mount_server . port = NFS_UNSPEC_PORT ;
if ( fc - > root ) {
/* reconfigure, start with the current config */
struct nfs_server * nfss = fc - > root - > d_sb - > s_fs_info ;
struct net * net = nfss - > nfs_client - > cl_net ;
ctx - > flags = nfss - > flags ;
ctx - > rsize = nfss - > rsize ;
ctx - > wsize = nfss - > wsize ;
ctx - > retrans = nfss - > client - > cl_timeout - > to_retries ;
ctx - > selected_flavor = nfss - > client - > cl_auth - > au_flavor ;
ctx - > acregmin = nfss - > acregmin / HZ ;
ctx - > acregmax = nfss - > acregmax / HZ ;
ctx - > acdirmin = nfss - > acdirmin / HZ ;
ctx - > acdirmax = nfss - > acdirmax / HZ ;
ctx - > timeo = 10U * nfss - > client - > cl_timeout - > to_initval / HZ ;
ctx - > nfs_server . port = nfss - > port ;
ctx - > nfs_server . addrlen = nfss - > nfs_client - > cl_addrlen ;
ctx - > version = nfss - > nfs_client - > rpc_ops - > version ;
ctx - > minorversion = nfss - > nfs_client - > cl_minorversion ;
memcpy ( & ctx - > nfs_server . address , & nfss - > nfs_client - > cl_addr ,
ctx - > nfs_server . addrlen ) ;
if ( fc - > net_ns ! = net ) {
put_net ( fc - > net_ns ) ;
fc - > net_ns = get_net ( net ) ;
}
2019-12-10 15:31:14 +03:00
ctx - > nfs_mod = nfss - > nfs_client - > cl_nfs_mod ;
__module_get ( ctx - > nfs_mod - > owner ) ;
2019-12-10 15:31:13 +03:00
} else {
/* defaults */
ctx - > timeo = NFS_UNSPEC_TIMEO ;
ctx - > retrans = NFS_UNSPEC_RETRANS ;
ctx - > acregmin = NFS_DEF_ACREGMIN ;
ctx - > acregmax = NFS_DEF_ACREGMAX ;
ctx - > acdirmin = NFS_DEF_ACDIRMIN ;
ctx - > acdirmax = NFS_DEF_ACDIRMAX ;
ctx - > nfs_server . port = NFS_UNSPEC_PORT ;
ctx - > nfs_server . protocol = XPRT_TRANSPORT_TCP ;
ctx - > selected_flavor = RPC_AUTH_MAXFLAVOR ;
ctx - > minorversion = 0 ;
ctx - > need_mount = true ;
}
fc - > fs_private = ctx ;
fc - > ops = & nfs_fs_context_ops ;
return 0 ;
}
struct file_system_type nfs_fs_type = {
. owner = THIS_MODULE ,
. name = " nfs " ,
. init_fs_context = nfs_init_fs_context ,
. parameters = & nfs_fs_parameters ,
. kill_sb = nfs_kill_super ,
. fs_flags = FS_RENAME_DOES_D_MOVE | FS_BINARY_MOUNTDATA ,
} ;
MODULE_ALIAS_FS ( " nfs " ) ;
EXPORT_SYMBOL_GPL ( nfs_fs_type ) ;
# if IS_ENABLED(CONFIG_NFS_V4)
struct file_system_type nfs4_fs_type = {
. owner = THIS_MODULE ,
. name = " nfs4 " ,
. init_fs_context = nfs_init_fs_context ,
. parameters = & nfs_fs_parameters ,
. kill_sb = nfs_kill_super ,
. fs_flags = FS_RENAME_DOES_D_MOVE | FS_BINARY_MOUNTDATA ,
} ;
MODULE_ALIAS_FS ( " nfs4 " ) ;
MODULE_ALIAS ( " nfs4 " ) ;
EXPORT_SYMBOL_GPL ( nfs4_fs_type ) ;
# endif /* CONFIG_NFS_V4 */