pre-2.3.4..
[davej-history.git] / fs / nfs / nfs2xdr.c
bloba4c4e86d5db453fe343751d1bcba8f84daef4ca5
1 /*
2 * linux/fs/nfs/xdr.c
4 * XDR functions to encode/decode NFS RPC arguments and results.
6 * Copyright (C) 1992, 1993, 1994 Rick Sladkey
7 * Copyright (C) 1996 Olaf Kirch
8 */
10 #define NFS_NEED_XDR_TYPES
12 #include <linux/param.h>
13 #include <linux/sched.h>
14 #include <linux/mm.h>
15 #include <linux/malloc.h>
16 #include <linux/utsname.h>
17 #include <linux/errno.h>
18 #include <linux/string.h>
19 #include <linux/in.h>
20 #include <linux/pagemap.h>
21 #include <linux/proc_fs.h>
22 #include <linux/sunrpc/clnt.h>
23 #include <linux/nfs_fs.h>
25 /* Uncomment this to support servers requiring longword lengths */
26 #define NFS_PAD_WRITES 1
28 #define NFSDBG_FACILITY NFSDBG_XDR
29 /* #define NFS_PARANOIA 1 */
31 #define QUADLEN(len) (((len) + 3) >> 2)
32 static int nfs_stat_to_errno(int stat);
34 /* Mapping from NFS error code to "errno" error code. */
35 #define errno_NFSERR_IO EIO
38 * Declare the space requirements for NFS arguments and replies as
39 * number of 32bit-words
41 #define NFS_fhandle_sz 8
42 #define NFS_sattr_sz 8
43 #define NFS_filename_sz 1+(NFS_MAXNAMLEN>>2)
44 #define NFS_path_sz 1+(NFS_MAXPATHLEN>>2)
45 #define NFS_fattr_sz 17
46 #define NFS_info_sz 5
47 #define NFS_entry_sz NFS_filename_sz+3
49 #define NFS_enc_void_sz 0
50 #define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz
51 #define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz
52 #define NFS_readargs_sz NFS_fhandle_sz+3
53 #define NFS_writeargs_sz NFS_fhandle_sz+4
54 #define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz
55 #define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz
56 #define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz
57 #define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
58 #define NFS_readdirargs_sz NFS_fhandle_sz+2
60 #define NFS_dec_void_sz 0
61 #define NFS_attrstat_sz 1+NFS_fattr_sz
62 #define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz
63 #define NFS_readlinkres_sz 1+NFS_path_sz
64 #define NFS_readres_sz 1+NFS_fattr_sz+1
65 #define NFS_stat_sz 1
66 #define NFS_readdirres_sz 1
67 #define NFS_statfsres_sz 1+NFS_info_sz
70 * Common NFS XDR functions as inlines
72 static inline u32 *
73 xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
75 *((struct nfs_fh *) p) = *fhandle;
76 return p + QUADLEN(sizeof(*fhandle));
79 static inline u32 *
80 xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
82 *fhandle = *((struct nfs_fh *) p);
83 return p + QUADLEN(sizeof(*fhandle));
86 static inline u32 *
87 xdr_decode_string2(u32 *p, char **string, unsigned int *len,
88 unsigned int maxlen)
90 *len = ntohl(*p++);
91 if (*len > maxlen)
92 return NULL;
93 *string = (char *) p;
94 return p + QUADLEN(*len);
97 static inline u32 *
98 xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
100 fattr->type = (enum nfs_ftype) ntohl(*p++);
101 fattr->mode = ntohl(*p++);
102 fattr->nlink = ntohl(*p++);
103 fattr->uid = ntohl(*p++);
104 fattr->gid = ntohl(*p++);
105 fattr->size = ntohl(*p++);
106 fattr->blocksize = ntohl(*p++);
107 fattr->rdev = ntohl(*p++);
108 fattr->blocks = ntohl(*p++);
109 fattr->fsid = ntohl(*p++);
110 fattr->fileid = ntohl(*p++);
111 fattr->atime.seconds = ntohl(*p++);
112 fattr->atime.useconds = ntohl(*p++);
113 fattr->mtime.seconds = ntohl(*p++);
114 fattr->mtime.useconds = ntohl(*p++);
115 fattr->ctime.seconds = ntohl(*p++);
116 fattr->ctime.useconds = ntohl(*p++);
117 return p;
120 static inline u32 *
121 xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
123 *p++ = htonl(sattr->mode);
124 *p++ = htonl(sattr->uid);
125 *p++ = htonl(sattr->gid);
126 *p++ = htonl(sattr->size);
127 *p++ = htonl(sattr->atime.seconds);
128 *p++ = htonl(sattr->atime.useconds);
129 *p++ = htonl(sattr->mtime.seconds);
130 *p++ = htonl(sattr->mtime.useconds);
131 return p;
135 * NFS encode functions
138 * Encode void argument
140 static int
141 nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
143 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
144 return 0;
148 * Encode file handle argument
149 * GETATTR, READLINK, STATFS
151 static int
152 nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
154 p = xdr_encode_fhandle(p, fh);
155 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
156 return 0;
160 * Encode SETATTR arguments
162 static int
163 nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
165 p = xdr_encode_fhandle(p, args->fh);
166 p = xdr_encode_sattr(p, args->sattr);
167 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
168 return 0;
172 * Encode directory ops argument
173 * LOOKUP, REMOVE, RMDIR
175 static int
176 nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
178 p = xdr_encode_fhandle(p, args->fh);
179 p = xdr_encode_string(p, args->name);
180 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
181 return 0;
185 * Arguments to a READ call. Since we read data directly into the page
186 * cache, we also set up the reply iovec here so that iov[1] points
187 * exactly to the page we want to fetch.
189 static int
190 nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
192 struct rpc_auth *auth = req->rq_task->tk_auth;
193 int replen, buflen;
195 p = xdr_encode_fhandle(p, args->fh);
196 *p++ = htonl(args->offset);
197 *p++ = htonl(args->count);
198 *p++ = htonl(args->count);
199 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
201 #if 1
202 /* set up reply iovec */
203 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
204 buflen = req->rq_rvec[0].iov_len;
205 req->rq_rvec[0].iov_len = replen;
206 req->rq_rvec[1].iov_base = args->buffer;
207 req->rq_rvec[1].iov_len = args->count;
208 req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
209 req->rq_rvec[2].iov_len = buflen - replen;
210 req->rq_rlen = args->count + buflen;
211 req->rq_rnr = 3;
212 #else
213 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
214 req->rq_rvec[0].iov_len = replen;
215 #endif
217 return 0;
221 * Decode READ reply
223 static int
224 nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
226 struct iovec *iov = req->rq_rvec;
227 int status, count, recvd, hdrlen;
229 dprintk("RPC: readres OK status %lx\n", (long)ntohl(*p));
230 if ((status = ntohl(*p++)))
231 return -nfs_stat_to_errno(status);
232 p = xdr_decode_fattr(p, res->fattr);
234 count = ntohl(*p++);
235 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
236 recvd = req->rq_rlen - hdrlen;
237 if (p != iov[2].iov_base) {
238 /* Unexpected reply header size. Punt.
239 * XXX: Move iovec contents to align data on page
240 * boundary and adjust RPC header size guess */
241 printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
242 return -errno_NFSERR_IO;
244 if (count > recvd) {
245 printk("NFS: server cheating in read reply: "
246 "count %d > recvd %d\n", count, recvd);
247 count = recvd;
250 dprintk("RPC: readres OK count %d\n", count);
251 if (count < res->count)
252 memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
254 return count;
259 * Write arguments. Splice the buffer to be written into the iovec.
261 static int
262 nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
264 u32 count = args->count;
266 p = xdr_encode_fhandle(p, args->fh);
267 *p++ = htonl(args->offset);
268 *p++ = htonl(args->offset);
269 *p++ = htonl(count);
270 *p++ = htonl(count);
271 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
273 req->rq_svec[1].iov_base = (void *) args->buffer;
274 req->rq_svec[1].iov_len = count;
275 req->rq_slen += count;
276 req->rq_snr = 2;
278 #ifdef NFS_PAD_WRITES
280 * Some old servers require that the message length
281 * be a multiple of 4, so we pad it here if needed.
283 count = ((count + 3) & ~3) - count;
284 if (count) {
285 #if 0
286 printk("nfs_writeargs: padding write, len=%d, slen=%d, pad=%d\n",
287 req->rq_svec[1].iov_len, req->rq_slen, count);
288 #endif
289 req->rq_svec[2].iov_base = (void *) "\0\0\0";
290 req->rq_svec[2].iov_len = count;
291 req->rq_slen += count;
292 req->rq_snr = 3;
294 #endif
296 return 0;
300 * Encode create arguments
301 * CREATE, MKDIR
303 static int
304 nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
306 p = xdr_encode_fhandle(p, args->fh);
307 p = xdr_encode_string(p, args->name);
308 p = xdr_encode_sattr(p, args->sattr);
309 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
310 return 0;
314 * Encode RENAME arguments
316 static int
317 nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
319 p = xdr_encode_fhandle(p, args->fromfh);
320 p = xdr_encode_string(p, args->fromname);
321 p = xdr_encode_fhandle(p, args->tofh);
322 p = xdr_encode_string(p, args->toname);
323 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
324 return 0;
328 * Encode LINK arguments
330 static int
331 nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
333 p = xdr_encode_fhandle(p, args->fromfh);
334 p = xdr_encode_fhandle(p, args->tofh);
335 p = xdr_encode_string(p, args->toname);
336 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
337 return 0;
341 * Encode SYMLINK arguments
343 static int
344 nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
346 p = xdr_encode_fhandle(p, args->fromfh);
347 p = xdr_encode_string(p, args->fromname);
348 p = xdr_encode_string(p, args->topath);
349 p = xdr_encode_sattr(p, args->sattr);
350 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
351 return 0;
355 * Encode arguments to readdir call
357 static int
358 nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
360 struct rpc_task *task = req->rq_task;
361 struct rpc_auth *auth = task->tk_auth;
362 u32 bufsiz = args->bufsiz;
363 int replen;
366 * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
367 * to be in longwords ... check whether to convert the size.
369 if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
370 bufsiz = bufsiz >> 2;
372 p = xdr_encode_fhandle(p, args->fh);
373 *p++ = htonl(args->cookie);
374 *p++ = htonl(bufsiz); /* see above */
375 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
377 /* set up reply iovec */
378 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
380 dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
381 RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
383 req->rq_rvec[0].iov_len = replen;
384 req->rq_rvec[1].iov_base = args->buffer;
385 req->rq_rvec[1].iov_len = args->bufsiz;
386 req->rq_rlen = replen + args->bufsiz;
387 req->rq_rnr = 2;
390 dprintk("RPC: readdirargs set up reply vec:\n");
391 dprintk(" rvec[0] = %p/%d\n",
392 req->rq_rvec[0].iov_base,
393 req->rq_rvec[0].iov_len);
394 dprintk(" rvec[1] = %p/%d\n",
395 req->rq_rvec[1].iov_base,
396 req->rq_rvec[1].iov_len);
399 return 0;
403 * Decode the result of a readdir call. We decode the result in place
404 * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
405 * After decoding, the layout in memory looks like this:
406 * entry1 entry2 ... entryN <space> stringN ... string2 string1
407 * Each entry consists of three __u32 values, the same space as NFS uses.
408 * Note that the strings are not null-terminated so that the entire number
409 * of entries returned by the server should fit into the buffer.
411 static int
412 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
414 struct iovec *iov = req->rq_rvec;
415 int status, nr;
416 char *string, *start;
417 u32 *end, *entry, len, fileid, cookie;
419 if ((status = ntohl(*p++)))
420 return -nfs_stat_to_errno(status);
421 if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
422 /* Unexpected reply header size. Punt. */
423 printk("NFS: Odd RPC header size in readdirres reply\n");
424 return -errno_NFSERR_IO;
427 /* Get start and end address of XDR data */
428 p = (u32 *) iov[1].iov_base;
429 end = (u32 *) ((u8 *) p + iov[1].iov_len);
431 /* Get start and end of dirent buffer */
432 entry = (u32 *) res->buffer;
433 start = (char *) res->buffer;
434 string = (char *) res->buffer + res->bufsiz;
435 for (nr = 0; *p++; nr++) {
436 fileid = ntohl(*p++);
438 len = ntohl(*p++);
440 * Check whether the server has exceeded our reply buffer,
441 * and set a flag to convert the size to longwords.
443 if ((p + QUADLEN(len) + 3) > end) {
444 struct rpc_clnt *clnt = req->rq_task->tk_client;
445 printk(KERN_WARNING
446 "NFS: server %s, readdir reply truncated\n",
447 clnt->cl_server);
448 printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n",
449 nr, (end - p), len);
450 clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
451 break;
453 if (len > NFS_MAXNAMLEN) {
454 printk("NFS: giant filename in readdir (len %x)!\n",
455 len);
456 return -errno_NFSERR_IO;
458 string -= len;
459 if ((void *) (entry+3) > (void *) string) {
461 * This error is impossible as long as the temp
462 * buffer is no larger than the user buffer. The
463 * current packing algorithm uses the same amount
464 * of space in the user buffer as in the XDR data,
465 * so it's guaranteed to fit.
467 printk("NFS: incorrect buffer size in %s!\n",
468 __FUNCTION__);
469 break;
472 memmove(string, p, len);
473 p += QUADLEN(len);
474 cookie = ntohl(*p++);
476 * To make everything fit, we encode the length, offset,
477 * and eof flag into 32 bits. This works for filenames
478 * up to 32K and PAGE_SIZE up to 64K.
480 status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
481 *entry++ = fileid;
482 *entry++ = cookie;
483 *entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
485 #ifdef NFS_PARANOIA
486 printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
487 nr, ((char *) entry - start), (start + res->bufsiz - string));
488 #endif
489 return nr;
493 * NFS XDR decode functions
496 * Decode void reply
498 static int
499 nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
501 return 0;
505 * Decode simple status reply
507 static int
508 nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
510 int status;
512 if ((status = ntohl(*p++)) != 0)
513 status = -nfs_stat_to_errno(status);
514 return status;
518 * Decode attrstat reply
519 * GETATTR, SETATTR, WRITE
521 static int
522 nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
524 int status;
526 dprintk("RPC: attrstat status %lx\n", (long)ntohl(*p));
527 if ((status = ntohl(*p++)))
528 return -nfs_stat_to_errno(status);
529 xdr_decode_fattr(p, fattr);
530 dprintk("RPC: attrstat OK type %d mode %o dev %x ino %x\n",
531 fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
532 return 0;
536 * Decode diropres reply
537 * LOOKUP, CREATE, MKDIR
539 static int
540 nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
542 int status;
544 dprintk("RPC: diropres status %lx\n", (long)ntohl(*p));
545 if ((status = ntohl(*p++)))
546 return -nfs_stat_to_errno(status);
547 p = xdr_decode_fhandle(p, res->fh);
548 xdr_decode_fattr(p, res->fattr);
549 dprintk("RPC: diropres OK type %x mode %o dev %x ino %x\n",
550 res->fattr->type, res->fattr->mode,
551 res->fattr->fsid, res->fattr->fileid);
552 return 0;
556 * Decode READLINK reply
558 static int
559 nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
561 int status;
563 if ((status = ntohl(*p++)))
564 return -nfs_stat_to_errno(status);
565 xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
567 /* Caller takes over the buffer here to avoid extra copy */
568 res->buffer = req->rq_task->tk_buffer;
569 req->rq_task->tk_buffer = NULL;
570 return 0;
574 * Decode STATFS reply
576 static int
577 nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
579 int status;
581 if ((status = ntohl(*p++)))
582 return -nfs_stat_to_errno(status);
583 res->tsize = ntohl(*p++);
584 res->bsize = ntohl(*p++);
585 res->blocks = ntohl(*p++);
586 res->bfree = ntohl(*p++);
587 res->bavail = ntohl(*p++);
588 return 0;
592 * We need to translate between nfs status return values and
593 * the local errno values which may not be the same.
595 static struct {
596 int stat;
597 int errno;
598 } nfs_errtbl[] = {
599 { NFS_OK, 0 },
600 { NFSERR_PERM, EPERM },
601 { NFSERR_NOENT, ENOENT },
602 { NFSERR_IO, errno_NFSERR_IO },
603 { NFSERR_NXIO, ENXIO },
604 { NFSERR_EAGAIN, EAGAIN },
605 { NFSERR_ACCES, EACCES },
606 { NFSERR_EXIST, EEXIST },
607 { NFSERR_XDEV, EXDEV },
608 { NFSERR_NODEV, ENODEV },
609 { NFSERR_NOTDIR, ENOTDIR },
610 { NFSERR_ISDIR, EISDIR },
611 { NFSERR_INVAL, EINVAL },
612 { NFSERR_FBIG, EFBIG },
613 { NFSERR_NOSPC, ENOSPC },
614 { NFSERR_ROFS, EROFS },
615 { NFSERR_OPNOTSUPP, EOPNOTSUPP },
616 { NFSERR_NAMETOOLONG, ENAMETOOLONG },
617 { NFSERR_NOTEMPTY, ENOTEMPTY },
618 { NFSERR_DQUOT, EDQUOT },
619 { NFSERR_STALE, ESTALE },
620 #ifdef EWFLUSH
621 { NFSERR_WFLUSH, EWFLUSH },
622 #endif
623 { -1, EIO }
626 static int
627 nfs_stat_to_errno(int stat)
629 int i;
631 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
632 if (nfs_errtbl[i].stat == stat)
633 return nfs_errtbl[i].errno;
635 printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
636 return nfs_errtbl[i].errno;
639 #ifndef MAX
640 # define MAX(a, b) (((a) > (b))? (a) : (b))
641 #endif
643 #define PROC(proc, argtype, restype) \
644 { "nfs_" #proc, \
645 (kxdrproc_t) nfs_xdr_##argtype, \
646 (kxdrproc_t) nfs_xdr_##restype, \
647 MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2 \
650 static struct rpc_procinfo nfs_procedures[18] = {
651 PROC(null, enc_void, dec_void),
652 PROC(getattr, fhandle, attrstat),
653 PROC(setattr, sattrargs, attrstat),
654 PROC(root, enc_void, dec_void),
655 PROC(lookup, diropargs, diropres),
656 PROC(readlink, fhandle, readlinkres),
657 PROC(read, readargs, readres),
658 PROC(writecache, enc_void, dec_void),
659 PROC(write, writeargs, attrstat),
660 PROC(create, createargs, diropres),
661 PROC(remove, diropargs, stat),
662 PROC(rename, renameargs, stat),
663 PROC(link, linkargs, stat),
664 PROC(symlink, symlinkargs, stat),
665 PROC(mkdir, createargs, diropres),
666 PROC(rmdir, diropargs, stat),
667 PROC(readdir, readdirargs, readdirres),
668 PROC(statfs, fhandle, statfsres),
671 static struct rpc_version nfs_version2 = {
673 sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
674 nfs_procedures
677 static struct rpc_version * nfs_version[] = {
678 NULL,
679 NULL,
680 &nfs_version2
683 struct rpc_program nfs_program = {
684 "nfs",
685 NFS_PROGRAM,
686 sizeof(nfs_version) / sizeof(nfs_version[0]),
687 nfs_version,
688 &nfs_rpcstat,