linux/fs/nfs/mount_clnt.c
Steve Dickson 8491945f11 NFS: Client mounts hang when exported directory do not exist
This patch fixes a regression that was introduced by the string based mounts.

nfs_mount() statically returns -EACCES for every error returned
by the remote mounted. This is incorrect because -EACCES is
an non-fatal error to the mount.nfs command. This error causes
mount.nfs to retry the mount even in the case when the exported
directory does not exist.

This patch maps the errors returned by the remote mountd into
valid errno values, exactly how it was done pre-string based
mounts. By returning the correct errno enables mount.nfs
to do the right thing.

Signed-off-by: Steve Dickson <steved@redhat.com>
[Trond.Myklebust@netapp.com: nfs_stat_to_errno() now correctly returns
 negative errors, so remove the sign change.]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2008-10-07 18:19:01 -04:00

204 lines
4.5 KiB
C

/*
* In-kernel MOUNT protocol client
*
* 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>
#include "internal.h"
#ifdef RPC_DEBUG
# define NFSDBG_FACILITY NFSDBG_MOUNT
#endif
static struct rpc_program mnt_program;
struct mnt_fhstatus {
u32 status;
struct nfs_fh *fh;
};
/**
* nfs_mount - Obtain an NFS file handle for the given host and path
* @addr: pointer to server's address
* @len: size of server's address
* @hostname: name of server host, or NULL
* @path: pointer to string containing export path to mount
* @version: mount version to use for this request
* @protocol: transport protocol to use for thie request
* @fh: pointer to location to place returned file handle
*
* Uses default timeout parameters specified by underlying transport.
*/
int nfs_mount(struct sockaddr *addr, size_t len, char *hostname, char *path,
int version, int protocol, struct nfs_fh *fh)
{
struct mnt_fhstatus result = {
.fh = fh
};
struct rpc_message msg = {
.rpc_argp = path,
.rpc_resp = &result,
};
struct rpc_create_args args = {
.protocol = protocol,
.address = addr,
.addrsize = len,
.servername = hostname,
.program = &mnt_program,
.version = version,
.authflavor = RPC_AUTH_UNIX,
.flags = 0,
};
struct rpc_clnt *mnt_clnt;
int status;
dprintk("NFS: sending MNT request for %s:%s\n",
(hostname ? hostname : "server"), path);
mnt_clnt = rpc_create(&args);
if (IS_ERR(mnt_clnt))
goto out_clnt_err;
if (version == NFS_MNT3_VERSION)
msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT];
else
msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT];
status = rpc_call_sync(mnt_clnt, &msg, 0);
rpc_shutdown_client(mnt_clnt);
if (status < 0)
goto out_call_err;
if (result.status != 0)
goto out_mnt_err;
dprintk("NFS: MNT request succeeded\n");
status = 0;
out:
return status;
out_clnt_err:
status = PTR_ERR(mnt_clnt);
dprintk("NFS: failed to create RPC client, status=%d\n", status);
goto out;
out_call_err:
dprintk("NFS: failed to start MNT request, status=%d\n", status);
goto out;
out_mnt_err:
dprintk("NFS: MNT server returned result %d\n", result.status);
status = nfs_stat_to_errno(result.status);
goto out;
}
/*
* XDR encode/decode functions for MOUNT
*/
static int xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p,
const char *path)
{
p = xdr_encode_string(p, path);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p,
struct mnt_fhstatus *res)
{
struct nfs_fh *fh = res->fh;
if ((res->status = ntohl(*p++)) == 0) {
fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE);
}
return 0;
}
static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p,
struct mnt_fhstatus *res)
{
struct nfs_fh *fh = res->fh;
unsigned size;
if ((res->status = ntohl(*p++)) == 0) {
size = ntohl(*p++);
if (size <= NFS3_FHSIZE && size != 0) {
fh->size = size;
memcpy(fh->data, p, size);
} else
res->status = -EBADHANDLE;
}
return 0;
}
#define MNT_dirpath_sz (1 + 256)
#define MNT_fhstatus_sz (1 + 8)
#define MNT_fhstatus3_sz (1 + 16)
static struct rpc_procinfo mnt_procedures[] = {
[MNTPROC_MNT] = {
.p_proc = MNTPROC_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus,
.p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus_sz,
.p_statidx = MNTPROC_MNT,
.p_name = "MOUNT",
},
};
static struct rpc_procinfo mnt3_procedures[] = {
[MOUNTPROC3_MNT] = {
.p_proc = MOUNTPROC3_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus3,
.p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus3_sz,
.p_statidx = MOUNTPROC3_MNT,
.p_name = "MOUNT",
},
};
static struct rpc_version mnt_version1 = {
.number = 1,
.nrprocs = 2,
.procs = mnt_procedures,
};
static struct rpc_version mnt_version3 = {
.number = 3,
.nrprocs = 2,
.procs = mnt3_procedures,
};
static struct rpc_version *mnt_version[] = {
NULL,
&mnt_version1,
NULL,
&mnt_version3,
};
static struct rpc_stat mnt_stats;
static struct rpc_program mnt_program = {
.name = "mount",
.number = NFS_MNT_PROGRAM,
.nrvers = ARRAY_SIZE(mnt_version),
.version = mnt_version,
.stats = &mnt_stats,
};