diff options
Diffstat (limited to 'fs/nfs/super.c')
| -rw-r--r-- | fs/nfs/super.c | 229 | 
1 files changed, 141 insertions, 88 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a03b9c6f948..084af1060d7 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -497,7 +497,8 @@ static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)  	static const struct {  		rpc_authflavor_t flavour;  		const char *str; -	} sec_flavours[] = { +	} sec_flavours[NFS_AUTH_INFO_MAX_FLAVORS] = { +		/* update NFS_AUTH_INFO_MAX_FLAVORS when this list changes! */  		{ RPC_AUTH_NULL, "null" },  		{ RPC_AUTH_UNIX, "sys" },  		{ RPC_AUTH_GSS_KRB5, "krb5" }, @@ -923,8 +924,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)  		data->mount_server.port	= NFS_UNSPEC_PORT;  		data->nfs_server.port	= NFS_UNSPEC_PORT;  		data->nfs_server.protocol = XPRT_TRANSPORT_TCP; -		data->auth_flavors[0]	= RPC_AUTH_MAXFLAVOR; -		data->auth_flavor_len	= 0; +		data->selected_flavor	= RPC_AUTH_MAXFLAVOR;  		data->minorversion	= 0;  		data->need_mount	= true;  		data->net		= current->nsproxy->net_ns; @@ -1019,14 +1019,53 @@ static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt)  	}  } -static void nfs_set_auth_parsed_mount_data(struct nfs_parsed_mount_data *data, -		rpc_authflavor_t pseudoflavor) +/* + * Add 'flavor' to 'auth_info' if not already present. + * Returns true if 'flavor' ends up in the list, false otherwise + */ +static bool nfs_auth_info_add(struct nfs_auth_info *auth_info, +			      rpc_authflavor_t flavor)  { -	data->auth_flavors[0] = pseudoflavor; -	data->auth_flavor_len = 1; +	unsigned int i; +	unsigned int max_flavor_len = (sizeof(auth_info->flavors) / +				       sizeof(auth_info->flavors[0])); + +	/* 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]) +			return true; +	} + +	if (auth_info->flavor_len + 1 >= max_flavor_len) { +		dfprintk(MOUNT, "NFS: too many sec= flavors\n"); +		return false; +	} + +	auth_info->flavors[auth_info->flavor_len++] = flavor; +	return true;  }  /* + * Return true if 'match' is in auth_info or auth_info is empty. + * Return false otherwise. + */ +bool nfs_auth_info_match(const struct nfs_auth_info *auth_info, +			 rpc_authflavor_t match) +{ +	int i; + +	if (!auth_info->flavor_len) +		return true; + +	for (i = 0; i < auth_info->flavor_len; i++) { +		if (auth_info->flavors[i] == match) +			return true; +	} +	return false; +} +EXPORT_SYMBOL_GPL(nfs_auth_info_match); + +/*   * Parse the value of the 'sec=' option.   */  static int nfs_parse_security_flavors(char *value, @@ -1034,49 +1073,55 @@ static int nfs_parse_security_flavors(char *value,  {  	substring_t args[MAX_OPT_ARGS];  	rpc_authflavor_t pseudoflavor; +	char *p;  	dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value); -	switch (match_token(value, nfs_secflavor_tokens, args)) { -	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: -		return 0; +	while ((p = strsep(&value, ":")) != NULL) { +		switch (match_token(p, nfs_secflavor_tokens, args)) { +		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: +			dfprintk(MOUNT, +				 "NFS: sec= option '%s' not recognized\n", p); +			return 0; +		} + +		if (!nfs_auth_info_add(&mnt->auth_info, pseudoflavor)) +			return 0;  	} -	mnt->flags |= NFS_MOUNT_SECFLAVOUR; -	nfs_set_auth_parsed_mount_data(mnt, pseudoflavor);  	return 1;  } @@ -1569,7 +1614,7 @@ static int nfs_parse_mount_options(char *raw,  		goto out_minorversion_mismatch;  	if (mnt->options & NFS_OPTION_MIGRATION && -	    mnt->version != 4 && mnt->minorversion != 0) +	    (mnt->version != 4 || mnt->minorversion != 0))  		goto out_migration_misuse;  	/* @@ -1623,12 +1668,14 @@ out_security_failure:  }  /* - * Ensure that the specified authtype in args->auth_flavors[0] is supported by - * the server. Returns 0 if it's ok, and -EACCES if not. + * Ensure that a specified authtype in args->auth_info is supported by + * the server. Returns 0 and sets args->selected_flavor if it's ok, and + * -EACCES if not.   */ -static int nfs_verify_authflavor(struct nfs_parsed_mount_data *args, +static int nfs_verify_authflavors(struct nfs_parsed_mount_data *args,  			rpc_authflavor_t *server_authlist, unsigned int count)  { +	rpc_authflavor_t flavor = RPC_AUTH_MAXFLAVOR;  	unsigned int i;  	/* @@ -1640,17 +1687,20 @@ static int nfs_verify_authflavor(struct nfs_parsed_mount_data *args,  	 * can be used.  	 */  	for (i = 0; i < count; i++) { -		if (args->auth_flavors[0] == server_authlist[i] || -		    server_authlist[i] == RPC_AUTH_NULL) +		flavor = server_authlist[i]; + +		if (nfs_auth_info_match(&args->auth_info, flavor) || +		    flavor == RPC_AUTH_NULL)  			goto out;  	} -	dfprintk(MOUNT, "NFS: auth flavor %u not supported by server\n", -		args->auth_flavors[0]); +	dfprintk(MOUNT, +		 "NFS: specified auth flavors not supported by server\n");  	return -EACCES;  out: -	dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]); +	args->selected_flavor = flavor; +	dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->selected_flavor);  	return 0;  } @@ -1738,9 +1788,10 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf  	 * Was a sec= authflavor specified in the options? First, verify  	 * whether the server supports it, and then just try to use it if so.  	 */ -	if (args->auth_flavor_len > 0) { -		status = nfs_verify_authflavor(args, authlist, authlist_len); -		dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]); +	if (args->auth_info.flavor_len > 0) { +		status = nfs_verify_authflavors(args, authlist, authlist_len); +		dfprintk(MOUNT, "NFS: using auth flavor %u\n", +			 args->selected_flavor);  		if (status)  			return ERR_PTR(status);  		return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); @@ -1769,7 +1820,7 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf  			/* Fallthrough */  		}  		dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", flavor); -		nfs_set_auth_parsed_mount_data(args, flavor); +		args->selected_flavor = flavor;  		server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);  		if (!IS_ERR(server))  			return server; @@ -1785,7 +1836,7 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf  	/* Last chance! Try AUTH_UNIX */  	dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", RPC_AUTH_UNIX); -	nfs_set_auth_parsed_mount_data(args, RPC_AUTH_UNIX); +	args->selected_flavor = RPC_AUTH_UNIX;  	return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);  } @@ -1972,9 +2023,9 @@ static int nfs23_validate_mount_data(void *options,  		args->bsize		= data->bsize;  		if (data->flags & NFS_MOUNT_SECFLAVOUR) -			nfs_set_auth_parsed_mount_data(args, data->pseudoflavor); +			args->selected_flavor = data->pseudoflavor;  		else -			nfs_set_auth_parsed_mount_data(args, RPC_AUTH_UNIX); +			args->selected_flavor = RPC_AUTH_UNIX;  		if (!args->nfs_server.hostname)  			goto out_nomem; @@ -2108,9 +2159,6 @@ static int nfs_validate_text_mount_data(void *options,  	nfs_set_port(sap, &args->nfs_server.port, port); -	if (args->auth_flavor_len > 1) -		goto out_bad_auth; -  	return nfs_parse_devname(dev_name,  				   &args->nfs_server.hostname,  				   max_namelen, @@ -2130,21 +2178,31 @@ out_invalid_transport_udp:  out_no_address:  	dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n");  	return -EINVAL; - -out_bad_auth: -	dfprintk(MOUNT, "NFS: Too many RPC auth flavours specified\n"); -	return -EINVAL;  } +#define NFS_MOUNT_CMP_FLAGMASK ~(NFS_MOUNT_INTR \ +		| NFS_MOUNT_SECURE \ +		| NFS_MOUNT_TCP \ +		| NFS_MOUNT_VER3 \ +		| NFS_MOUNT_KERBEROS \ +		| NFS_MOUNT_NONLM \ +		| NFS_MOUNT_BROKEN_SUID \ +		| NFS_MOUNT_STRICTLOCK \ +		| NFS_MOUNT_UNSHARED \ +		| NFS_MOUNT_NORESVPORT \ +		| NFS_MOUNT_LEGACY_INTERFACE) +  static int  nfs_compare_remount_data(struct nfs_server *nfss,  			 struct nfs_parsed_mount_data *data)  { -	if (data->flags != nfss->flags || +	if ((data->flags ^ nfss->flags) & NFS_MOUNT_CMP_FLAGMASK ||  	    data->rsize != nfss->rsize ||  	    data->wsize != nfss->wsize || +	    data->version != nfss->nfs_client->rpc_ops->version || +	    data->minorversion != nfss->nfs_client->cl_minorversion ||  	    data->retrans != nfss->client->cl_timeout->to_retries || -	    data->auth_flavors[0] != nfss->client->cl_auth->au_flavor || +	    data->selected_flavor != nfss->client->cl_auth->au_flavor ||  	    data->acregmin != nfss->acregmin / HZ ||  	    data->acregmax != nfss->acregmax / HZ ||  	    data->acdirmin != nfss->acdirmin / HZ || @@ -2169,6 +2227,8 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)  	struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data;  	u32 nfsvers = nfss->nfs_client->rpc_ops->version; +	sync_filesystem(sb); +  	/*  	 * Userspace mount programs that send binary options generally send  	 * them populated with default values. We have no way to know which @@ -2189,7 +2249,8 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)  	data->rsize = nfss->rsize;  	data->wsize = nfss->wsize;  	data->retrans = nfss->client->cl_timeout->to_retries; -	nfs_set_auth_parsed_mount_data(data, nfss->client->cl_auth->au_flavor); +	data->selected_flavor = nfss->client->cl_auth->au_flavor; +	data->auth_info = nfss->auth_info;  	data->acregmin = nfss->acregmin / HZ;  	data->acregmax = nfss->acregmax / HZ;  	data->acdirmin = nfss->acdirmin / HZ; @@ -2197,12 +2258,15 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)  	data->timeo = 10U * nfss->client->cl_timeout->to_initval / HZ;  	data->nfs_server.port = nfss->port;  	data->nfs_server.addrlen = nfss->nfs_client->cl_addrlen; +	data->version = nfsvers; +	data->minorversion = nfss->nfs_client->cl_minorversion; +	data->net = current->nsproxy->net_ns;  	memcpy(&data->nfs_server.address, &nfss->nfs_client->cl_addr,  		data->nfs_server.addrlen);  	/* overwrite those values with any that were specified */ -	error = nfs_parse_mount_options((char *)options, data); -	if (error < 0) +	error = -EINVAL; +	if (!nfs_parse_mount_options((char *)options, data))  		goto out;  	/* @@ -2296,18 +2360,6 @@ void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)   	nfs_initialise_sb(sb);  } -#define NFS_MOUNT_CMP_FLAGMASK ~(NFS_MOUNT_INTR \ -		| NFS_MOUNT_SECURE \ -		| NFS_MOUNT_TCP \ -		| NFS_MOUNT_VER3 \ -		| NFS_MOUNT_KERBEROS \ -		| NFS_MOUNT_NONLM \ -		| NFS_MOUNT_BROKEN_SUID \ -		| NFS_MOUNT_STRICTLOCK \ -		| NFS_MOUNT_UNSHARED \ -		| NFS_MOUNT_NORESVPORT \ -		| NFS_MOUNT_LEGACY_INTERFACE) -  static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags)  {  	const struct nfs_server *a = s->s_fs_info; @@ -2332,7 +2384,7 @@ static int nfs_compare_mount_options(const struct super_block *s, const struct n  		goto Ebusy;  	if (a->acdirmax != b->acdirmax)  		goto Ebusy; -	if (b->flags & NFS_MOUNT_SECFLAVOUR && +	if (b->auth_info.flavor_len > 0 &&  	   clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor)  		goto Ebusy;  	return 1; @@ -2530,6 +2582,7 @@ struct dentry *nfs_fs_mount_common(struct nfs_server *server,  			mntroot = ERR_PTR(error);  			goto error_splat_bdi;  		} +		server->super = s;  	}  	if (!s->s_root) { @@ -2713,9 +2766,9 @@ static int nfs4_validate_mount_data(void *options,  					   data->auth_flavours,  					   sizeof(pseudoflavor)))  				return -EFAULT; -			nfs_set_auth_parsed_mount_data(args, pseudoflavor); +			args->selected_flavor = pseudoflavor;  		} else -			nfs_set_auth_parsed_mount_data(args, RPC_AUTH_UNIX); +			args->selected_flavor = RPC_AUTH_UNIX;  		c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);  		if (IS_ERR(c))  | 
