NFS: Add separate mountd status code decoders for each mountd version
[pandora-kernel.git] / fs / nfs / mount_clnt.c
1 /*
2  * In-kernel MOUNT protocol client
3  *
4  * Copyright (C) 1997, Olaf Kirch <okir@monad.swb.de>
5  */
6
7 #include <linux/types.h>
8 #include <linux/socket.h>
9 #include <linux/kernel.h>
10 #include <linux/errno.h>
11 #include <linux/uio.h>
12 #include <linux/net.h>
13 #include <linux/in.h>
14 #include <linux/sunrpc/clnt.h>
15 #include <linux/sunrpc/sched.h>
16 #include <linux/nfs_fs.h>
17 #include "internal.h"
18
19 #ifdef RPC_DEBUG
20 # define NFSDBG_FACILITY        NFSDBG_MOUNT
21 #endif
22
23 /*
24  * Defined by RFC 1094, section A.3; and RFC 1813, section 5.1.4
25  */
26 #define MNTPATHLEN              (1024)
27
28 /*
29  * XDR data type sizes
30  */
31 #define encode_dirpath_sz       (1 + XDR_QUADLEN(MNTPATHLEN))
32 #define MNT_status_sz           (1)
33 #define MNT_fhs_status_sz       (1)
34
35 /*
36  * XDR argument and result sizes
37  */
38 #define MNT_enc_dirpath_sz      encode_dirpath_sz
39
40 /*
41  * Defined by RFC 1094, section A.5
42  */
43 enum {
44         MOUNTPROC_NULL          = 0,
45         MOUNTPROC_MNT           = 1,
46         MOUNTPROC_DUMP          = 2,
47         MOUNTPROC_UMNT          = 3,
48         MOUNTPROC_UMNTALL       = 4,
49         MOUNTPROC_EXPORT        = 5,
50 };
51
52 /*
53  * Defined by RFC 1813, section 5.2
54  */
55 enum {
56         MOUNTPROC3_NULL         = 0,
57         MOUNTPROC3_MNT          = 1,
58         MOUNTPROC3_DUMP         = 2,
59         MOUNTPROC3_UMNT         = 3,
60         MOUNTPROC3_UMNTALL      = 4,
61         MOUNTPROC3_EXPORT       = 5,
62 };
63
64 static struct rpc_program       mnt_program;
65
66 /*
67  * Defined by OpenGroup XNFS Version 3W, chapter 8
68  */
69 enum mountstat {
70         MNT_OK                  = 0,
71         MNT_EPERM               = 1,
72         MNT_ENOENT              = 2,
73         MNT_EACCES              = 13,
74         MNT_EINVAL              = 22,
75 };
76
77 static struct {
78         u32 status;
79         int errno;
80 } mnt_errtbl[] = {
81         { .status = MNT_OK,                     .errno = 0,             },
82         { .status = MNT_EPERM,                  .errno = -EPERM,        },
83         { .status = MNT_ENOENT,                 .errno = -ENOENT,       },
84         { .status = MNT_EACCES,                 .errno = -EACCES,       },
85         { .status = MNT_EINVAL,                 .errno = -EINVAL,       },
86 };
87
88 /*
89  * Defined by RFC 1813, section 5.1.5
90  */
91 enum mountstat3 {
92         MNT3_OK                 = 0,            /* no error */
93         MNT3ERR_PERM            = 1,            /* Not owner */
94         MNT3ERR_NOENT           = 2,            /* No such file or directory */
95         MNT3ERR_IO              = 5,            /* I/O error */
96         MNT3ERR_ACCES           = 13,           /* Permission denied */
97         MNT3ERR_NOTDIR          = 20,           /* Not a directory */
98         MNT3ERR_INVAL           = 22,           /* Invalid argument */
99         MNT3ERR_NAMETOOLONG     = 63,           /* Filename too long */
100         MNT3ERR_NOTSUPP         = 10004,        /* Operation not supported */
101         MNT3ERR_SERVERFAULT     = 10006,        /* A failure on the server */
102 };
103
104 static struct {
105         u32 status;
106         int errno;
107 } mnt3_errtbl[] = {
108         { .status = MNT3_OK,                    .errno = 0,             },
109         { .status = MNT3ERR_PERM,               .errno = -EPERM,        },
110         { .status = MNT3ERR_NOENT,              .errno = -ENOENT,       },
111         { .status = MNT3ERR_IO,                 .errno = -EIO,          },
112         { .status = MNT3ERR_ACCES,              .errno = -EACCES,       },
113         { .status = MNT3ERR_NOTDIR,             .errno = -ENOTDIR,      },
114         { .status = MNT3ERR_INVAL,              .errno = -EINVAL,       },
115         { .status = MNT3ERR_NAMETOOLONG,        .errno = -ENAMETOOLONG, },
116         { .status = MNT3ERR_NOTSUPP,            .errno = -ENOTSUPP,     },
117         { .status = MNT3ERR_SERVERFAULT,        .errno = -ESERVERFAULT, },
118 };
119
120 struct mountres {
121         int errno;
122         struct nfs_fh *fh;
123 };
124
125 struct mnt_fhstatus {
126         u32 status;
127         struct nfs_fh *fh;
128 };
129
130 /**
131  * nfs_mount - Obtain an NFS file handle for the given host and path
132  * @info: pointer to mount request arguments
133  *
134  * Uses default timeout parameters specified by underlying transport.
135  */
136 int nfs_mount(struct nfs_mount_request *info)
137 {
138         struct mnt_fhstatus     result = {
139                 .fh             = info->fh
140         };
141         struct rpc_message msg  = {
142                 .rpc_argp       = info->dirpath,
143                 .rpc_resp       = &result,
144         };
145         struct rpc_create_args args = {
146                 .protocol       = info->protocol,
147                 .address        = info->sap,
148                 .addrsize       = info->salen,
149                 .servername     = info->hostname,
150                 .program        = &mnt_program,
151                 .version        = info->version,
152                 .authflavor     = RPC_AUTH_UNIX,
153         };
154         struct rpc_clnt         *mnt_clnt;
155         int                     status;
156
157         dprintk("NFS: sending MNT request for %s:%s\n",
158                 (info->hostname ? info->hostname : "server"),
159                         info->dirpath);
160
161         if (info->noresvport)
162                 args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
163
164         mnt_clnt = rpc_create(&args);
165         if (IS_ERR(mnt_clnt))
166                 goto out_clnt_err;
167
168         if (info->version == NFS_MNT3_VERSION)
169                 msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT];
170         else
171                 msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC_MNT];
172
173         status = rpc_call_sync(mnt_clnt, &msg, 0);
174         rpc_shutdown_client(mnt_clnt);
175
176         if (status < 0)
177                 goto out_call_err;
178         if (result.status != 0)
179                 goto out_mnt_err;
180
181         dprintk("NFS: MNT request succeeded\n");
182         status = 0;
183
184 out:
185         return status;
186
187 out_clnt_err:
188         status = PTR_ERR(mnt_clnt);
189         dprintk("NFS: failed to create RPC client, status=%d\n", status);
190         goto out;
191
192 out_call_err:
193         dprintk("NFS: failed to start MNT request, status=%d\n", status);
194         goto out;
195
196 out_mnt_err:
197         dprintk("NFS: MNT server returned result %d\n", result.status);
198         status = nfs_stat_to_errno(result.status);
199         goto out;
200 }
201
202 /*
203  * XDR encode/decode functions for MOUNT
204  */
205
206 static int encode_mntdirpath(struct xdr_stream *xdr, const char *pathname)
207 {
208         const u32 pathname_len = strlen(pathname);
209         __be32 *p;
210
211         if (unlikely(pathname_len > MNTPATHLEN))
212                 return -EIO;
213
214         p = xdr_reserve_space(xdr, sizeof(u32) + pathname_len);
215         if (unlikely(p == NULL))
216                 return -EIO;
217         xdr_encode_opaque(p, pathname, pathname_len);
218
219         return 0;
220 }
221
222 static int mnt_enc_dirpath(struct rpc_rqst *req, __be32 *p,
223                            const char *dirpath)
224 {
225         struct xdr_stream xdr;
226
227         xdr_init_encode(&xdr, &req->rq_snd_buf, p);
228         return encode_mntdirpath(&xdr, dirpath);
229 }
230
231 static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p,
232                                struct mnt_fhstatus *res)
233 {
234         struct nfs_fh *fh = res->fh;
235
236         if ((res->status = ntohl(*p++)) == 0) {
237                 fh->size = NFS2_FHSIZE;
238                 memcpy(fh->data, p, NFS2_FHSIZE);
239         }
240         return 0;
241 }
242
243 /*
244  * RFC 1094: "A non-zero status indicates some sort of error.  In this
245  * case, the status is a UNIX error number."  This can be problematic
246  * if the server and client use different errno values for the same
247  * error.
248  *
249  * However, the OpenGroup XNFS spec provides a simple mapping that is
250  * independent of local errno values on the server and the client.
251  */
252 static int decode_status(struct xdr_stream *xdr, struct mountres *res)
253 {
254         unsigned int i;
255         u32 status;
256         __be32 *p;
257
258         p = xdr_inline_decode(xdr, sizeof(status));
259         if (unlikely(p == NULL))
260                 return -EIO;
261         status = ntohl(*p);
262
263         for (i = 0; i <= ARRAY_SIZE(mnt_errtbl); i++) {
264                 if (mnt_errtbl[i].status == status) {
265                         res->errno = mnt_errtbl[i].errno;
266                         return 0;
267                 }
268         }
269
270         dprintk("NFS: unrecognized MNT status code: %u\n", status);
271         res->errno = -EACCES;
272         return 0;
273 }
274
275 static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res)
276 {
277         unsigned int i;
278         u32 status;
279         __be32 *p;
280
281         p = xdr_inline_decode(xdr, sizeof(status));
282         if (unlikely(p == NULL))
283                 return -EIO;
284         status = ntohl(*p);
285
286         for (i = 0; i <= ARRAY_SIZE(mnt3_errtbl); i++) {
287                 if (mnt3_errtbl[i].status == status) {
288                         res->errno = mnt3_errtbl[i].errno;
289                         return 0;
290                 }
291         }
292
293         dprintk("NFS: unrecognized MNT3 status code: %u\n", status);
294         res->errno = -EACCES;
295         return 0;
296 }
297
298 static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p,
299                                 struct mnt_fhstatus *res)
300 {
301         struct nfs_fh *fh = res->fh;
302         unsigned size;
303
304         if ((res->status = ntohl(*p++)) == 0) {
305                 size = ntohl(*p++);
306                 if (size <= NFS3_FHSIZE && size != 0) {
307                         fh->size = size;
308                         memcpy(fh->data, p, size);
309                 } else
310                         res->status = -EBADHANDLE;
311         }
312         return 0;
313 }
314
315 #define MNT_fhstatus_sz         (1 + 8)
316 #define MNT_fhstatus3_sz        (1 + 16)
317
318 static struct rpc_procinfo mnt_procedures[] = {
319         [MOUNTPROC_MNT] = {
320                 .p_proc         = MOUNTPROC_MNT,
321                 .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
322                 .p_decode       = (kxdrproc_t) xdr_decode_fhstatus,
323                 .p_arglen       = MNT_enc_dirpath_sz,
324                 .p_replen       = MNT_fhstatus_sz,
325                 .p_statidx      = MOUNTPROC_MNT,
326                 .p_name         = "MOUNT",
327         },
328 };
329
330 static struct rpc_procinfo mnt3_procedures[] = {
331         [MOUNTPROC3_MNT] = {
332                 .p_proc         = MOUNTPROC3_MNT,
333                 .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
334                 .p_decode       = (kxdrproc_t) xdr_decode_fhstatus3,
335                 .p_arglen       = MNT_enc_dirpath_sz,
336                 .p_replen       = MNT_fhstatus3_sz,
337                 .p_statidx      = MOUNTPROC3_MNT,
338                 .p_name         = "MOUNT",
339         },
340 };
341
342
343 static struct rpc_version mnt_version1 = {
344         .number         = 1,
345         .nrprocs        = 2,
346         .procs          = mnt_procedures,
347 };
348
349 static struct rpc_version mnt_version3 = {
350         .number         = 3,
351         .nrprocs        = 2,
352         .procs          = mnt3_procedures,
353 };
354
355 static struct rpc_version *mnt_version[] = {
356         NULL,
357         &mnt_version1,
358         NULL,
359         &mnt_version3,
360 };
361
362 static struct rpc_stat mnt_stats;
363
364 static struct rpc_program mnt_program = {
365         .name           = "mount",
366         .number         = NFS_MNT_PROGRAM,
367         .nrvers         = ARRAY_SIZE(mnt_version),
368         .version        = mnt_version,
369         .stats          = &mnt_stats,
370 };