Import 2.3.25pre1
[davej-history.git] / fs / nfs / nfs3xdr.c
blobbeed6ec1ed4a756626ac7430d939507eaec45fbf
1 /*
2 * linux/fs/nfs/nfs2xdr.c
4 * XDR functions to encode/decode NFSv3 RPC arguments and results.
5 * Note: this is incomplete!
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/nfs_fs.h>
17 #include <linux/utsname.h>
18 #include <linux/errno.h>
19 #include <linux/string.h>
20 #include <linux/in.h>
21 #include <linux/pagemap.h>
22 #include <linux/proc_fs.h>
23 #include <linux/sunrpc/clnt.h>
25 #ifdef RPC_DEBUG
26 # define RPC_FACILITY RPCDBG_NFS
27 #endif
29 #define QUADLEN(len) (((len) + 3) >> 2)
30 static int nfs_stat_to_errno(int stat);
32 /* Mapping from NFS error code to "errno" error code. */
33 #define errno_NFSERR_IO EIO
36 * Declare the space requirements for NFS arguments and replies as
37 * number of 32bit-words
39 #define NFS_fhandle_sz (1+16)
40 #define NFS_sattr_sz 8
41 #define NFS_filename_sz 1+(NFS_MAXNAMLEN>>2)
42 #define NFS_path_sz 1+(NFS_MAXPATHLEN>>2)
43 #define NFS_fattr_sz 17
44 #define NFS_info_sz 5
45 #define NFS_entry_sz NFS_filename_sz+3
47 #define NFS_enc_void_sz 0
48 #define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz
49 #define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz
50 #define NFS_readargs_sz NFS_fhandle_sz+3
51 #define NFS_writeargs_sz NFS_fhandle_sz+4
52 #define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz
53 #define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz
54 #define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz
55 #define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
56 #define NFS_readdirargs_sz NFS_fhandle_sz+2
58 #define NFS_dec_void_sz 0
59 #define NFS_attrstat_sz 1+NFS_fattr_sz
60 #define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz
61 #define NFS_readlinkres_sz 1+NFS_path_sz
62 #define NFS_readres_sz 1+NFS_fattr_sz+1
63 #define NFS_stat_sz 1
64 #define NFS_readdirres_sz 1
65 #define NFS_statfsres_sz 1+NFS_info_sz
68 * Common NFS XDR functions as inlines
70 static inline u32 *
71 xdr_encode_fhandle(u32 *p, struct nfs3_fh *fh)
73 *p++ = htonl(fh->size);
74 memcpy(p, fh->data, fh->size);
75 return p + QUADLEN(fh->size);
78 static inline u32 *
79 xdr_decode_fhandle(u32 *p, struct nfs3_fh *fh)
81 if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
82 memcpy(fh->data, p, fh->size);
83 return p + QUADLEN(fh->size);
85 return NULL;
88 static inline enum nfs_ftype
89 xdr_decode_ftype(u32 type)
91 return (type == NF3FIFO)? NFFIFO : (enum nfs_ftype) type;
94 static inline u32 *
95 xdr_decode_string2(u32 *p, char **string, unsigned int *len,
96 unsigned int maxlen)
98 *len = ntohl(*p++);
99 if (*len > maxlen)
100 return NULL;
101 *string = (char *) p;
102 return p + QUADLEN(*len);
105 static inline u32 *
106 xdr_decode_fattr(u32 *p, struct nfs3_fattr *fattr)
108 fattr->type = xdr_decode_ftype(ntohl(*p++));
109 fattr->mode = ntohl(*p++);
110 fattr->nlink = ntohl(*p++);
111 fattr->uid = ntohl(*p++);
112 fattr->gid = ntohl(*p++);
113 fattr->size = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
114 fattr->used = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
115 fattr->rdev_maj = ntohl(*p++);
116 fattr->rdev_min = ntohl(*p++);
117 fattr->fsid = ntohl(*p++);
118 fattr->fileid = ntohl(*p++);
119 fattr->atime.seconds = ntohl(*p++);
120 fattr->atime.useconds = ntohl(*p++);
121 fattr->mtime.seconds = ntohl(*p++);
122 fattr->mtime.useconds = ntohl(*p++);
123 fattr->ctime.seconds = ntohl(*p++);
124 fattr->ctime.useconds = ntohl(*p++);
125 return p;
128 static inline u32 *
129 xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
131 *p++ = htonl(sattr->mode);
132 *p++ = htonl(sattr->uid);
133 *p++ = htonl(sattr->gid);
134 *p++ = htonl(sattr->size >> 32);
135 *p++ = htonl(sattr->size & 0xFFFFFFFF);
136 *p++ = htonl(sattr->atime.seconds);
137 *p++ = htonl(sattr->atime.useconds);
138 *p++ = htonl(sattr->mtime.seconds);
139 *p++ = htonl(sattr->mtime.useconds);
140 return p;
144 * NFS encode functions
147 * Encode void argument
149 static int
150 nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
152 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
153 return 0;
157 * Encode file handle argument
158 * GETATTR, READLINK, STATFS
160 static int
161 nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh)
163 p = xdr_encode_fhandle(p, fh);
164 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
165 return 0;
169 * Encode SETATTR arguments
171 static int
172 nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
174 p = xdr_encode_fhandle(p, args->fh);
175 p = xdr_encode_sattr(p, args->sattr);
176 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
177 return 0;
181 * Encode directory ops argument
182 * LOOKUP, REMOVE, RMDIR
184 static int
185 nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
187 p = xdr_encode_fhandle(p, args->fh);
188 p = xdr_encode_string(p, args->name);
189 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
190 return 0;
194 * Arguments to a READ call. Since we read data directly into the page
195 * cache, we also set up the reply iovec here so that iov[1] points
196 * exactly to the page wewant to fetch.
198 static int
199 nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
201 struct rpc_auth *auth = req->rq_task->tk_auth;
202 int replen, buflen;
204 p = xdr_encode_fhandle(p, args->fh);
205 *p++ = htonl(args->offset);
206 *p++ = htonl(args->count);
207 *p++ = htonl(args->count);
208 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
210 #if 1
211 /* set up reply iovec */
212 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
213 buflen = req->rq_rvec[0].iov_len;
214 req->rq_rvec[0].iov_len = replen;
215 req->rq_rvec[1].iov_base = args->buffer;
216 req->rq_rvec[1].iov_len = args->count;
217 req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
218 req->rq_rvec[2].iov_len = buflen - replen;
219 req->rq_rlen = args->count + buflen;
220 req->rq_rnr = 3;
221 #else
222 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
223 req->rq_rvec[0].iov_len = replen;
224 #endif
226 return 0;
230 * Decode READ reply
232 static int
233 nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
235 struct iovec *iov = req->rq_rvec;
236 int status, count, recvd, hdrlen;
238 dprintk("RPC: readres OK status %lx\n", ntohl(*p));
239 if ((status = ntohl(*p++)))
240 return -nfs_stat_to_errno(status);
241 p = xdr_decode_fattr(p, res->fattr);
243 count = ntohl(*p++);
244 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
245 recvd = req->rq_rlen - hdrlen;
246 if (p != iov[2].iov_base) {
247 /* Unexpected reply header size. Punt.
248 * XXX: Move iovec contents to align data on page
249 * boundary and adjust RPC header size guess */
250 printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
251 return -errno_NFSERR_IO;
253 if (count > recvd) {
254 printk("NFS: server cheating in read reply: "
255 "count %d > recvd %d\n", count, recvd);
256 count = recvd;
259 dprintk("RPC: readres OK count %d\n", count);
260 if (count < res->count)
261 memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
263 return count;
268 * Write arguments. Splice the buffer to be written into the iovec.
270 static int
271 nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
273 p = xdr_encode_fhandle(p, args->fh);
274 *p++ = htonl(args->offset);
275 *p++ = htonl(args->offset);
276 *p++ = htonl(args->count);
277 *p++ = htonl(args->count);
278 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
280 req->rq_svec[1].iov_base = (void *) args->buffer;
281 req->rq_svec[1].iov_len = args->count;
282 req->rq_slen += args->count;
283 req->rq_snr = 2;
285 return 0;
289 * Encode create arguments
290 * CREATE, MKDIR
292 static int
293 nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
295 p = xdr_encode_fhandle(p, args->fh);
296 p = xdr_encode_string(p, args->name);
297 p = xdr_encode_sattr(p, args->sattr);
298 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
299 return 0;
303 * Encode RENAME arguments
305 static int
306 nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
308 p = xdr_encode_fhandle(p, args->fromfh);
309 p = xdr_encode_string(p, args->fromname);
310 p = xdr_encode_fhandle(p, args->tofh);
311 p = xdr_encode_string(p, args->toname);
312 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
313 return 0;
317 * Encode LINK arguments
319 static int
320 nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
322 p = xdr_encode_fhandle(p, args->fromfh);
323 p = xdr_encode_fhandle(p, args->tofh);
324 p = xdr_encode_string(p, args->toname);
325 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
326 return 0;
330 * Encode SYMLINK arguments
332 static int
333 nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
335 p = xdr_encode_fhandle(p, args->fromfh);
336 p = xdr_encode_string(p, args->fromname);
337 p = xdr_encode_string(p, args->topath);
338 p = xdr_encode_sattr(p, args->sattr);
339 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
340 return 0;
344 * Encode arguments to readdir call
346 static int
347 nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
349 struct rpc_auth *auth = req->rq_task->tk_auth;
350 int replen;
352 p = xdr_encode_fhandle(p, args->fh);
353 *p++ = htonl(args->cookie);
354 *p++ = htonl(args->bufsiz);
355 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
357 /* set up reply iovec */
358 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
360 dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
361 RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
363 req->rq_rvec[0].iov_len = replen;
364 req->rq_rvec[1].iov_base = args->buffer;
365 req->rq_rvec[1].iov_len = args->bufsiz;
366 req->rq_rlen = replen + args->bufsiz;
367 req->rq_rnr = 2;
370 dprintk("RPC: readdirargs set up reply vec:\n");
371 dprintk(" rvec[0] = %p/%d\n",
372 req->rq_rvec[0].iov_base,
373 req->rq_rvec[0].iov_len);
374 dprintk(" rvec[1] = %p/%d\n",
375 req->rq_rvec[1].iov_base,
376 req->rq_rvec[1].iov_len);
379 return 0;
383 * Decode the result of a readdir call. We decode the result in place
384 * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
385 * After decoding, the layout in memory looks like this:
386 * entry1 entry2 ... entryN <space> stringN ... string2 string1
387 * Each entry consists of three __u32 values, the same space as NFS uses.
388 * Note that the strings are not null-terminated so that the entire number
389 * of entries returned by the server should fit into the buffer.
391 static int
392 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
394 struct iovec *iov = req->rq_rvec;
395 int status, nr, len;
396 char *string, *start;
397 u32 *end;
398 __u32 fileid, cookie, *entry;
400 if ((status = ntohl(*p++)))
401 return -nfs_stat_to_errno(status);
402 if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
403 /* Unexpected reply header size. Punt. */
404 printk("NFS: Odd RPC header size in readdirres reply\n");
405 return -errno_NFSERR_IO;
408 p = (u32 *) iov[1].iov_base;
409 end = (u32 *) ((u8 *) p + iov[1].iov_len);
411 if (p != res->buffer) {
412 printk("NFS: p != res->buffer in %s:%d!!!\n",
413 __FILE__, __LINE__);
414 return -errno_NFSERR_IO;
417 entry = (__u32 *) res->buffer;
418 start = (char *) res->buffer;
419 string = start + res->bufsiz;
420 for (nr = 0; *p++; nr++) {
421 fileid = ntohl(*p++);
423 len = ntohl(*p++);
424 if ((p + QUADLEN(len) + 3) > end) {
425 printk(KERN_NOTICE
426 "NFS: short packet in readdir reply!\n");
427 break;
429 if (len > NFS_MAXNAMLEN) {
430 printk("NFS: giant filename in readdir (len %x)!\n",
431 len);
432 return -errno_NFSERR_IO;
434 string -= len;
435 if ((void *) (entry+3) > (void *) string) {
437 * This error is impossible as long as the temp
438 * buffer is no larger than the user buffer. The
439 * current packing algorithm uses the same amount
440 * of space in the user buffer as in the XDR data,
441 * so it's guaranteed to fit.
443 printk("NFS: incorrect buffer size in %s!\n",
444 __FUNCTION__);
445 break;
448 memmove(string, p, len);
449 p += QUADLEN(len);
450 cookie = ntohl(*p++);
452 * To make everything fit, we encode the length, offset,
453 * and eof flag into 32 bits. This works for filenames
454 * up to 32K and PAGE_SIZE up to 64K.
456 status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
457 *entry++ = fileid;
458 *entry++ = cookie;
459 *entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
461 dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n",
462 len, string, cookie, status);
465 #ifdef NFS_PARANOIA
466 printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
467 nr, ((char *) entry - start), (start + res->bufsiz - string));
468 #endif
469 return nr;
473 * NFS XDR decode functions
476 * Decode void reply
478 static int
479 nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
481 return 0;
485 * Decode simple status reply
487 static int
488 nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
490 int status;
492 if ((status = ntohl(*p++)) != 0)
493 status = -nfs_stat_to_errno(status);
494 return status;
498 * Decode attrstat reply
499 * GETATTR, SETATTR, WRITE
501 static int
502 nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
504 int status;
506 dprintk("RPC: attrstat status %lx\n", ntohl(*p));
507 if ((status = ntohl(*p++)))
508 return -nfs_stat_to_errno(status);
509 xdr_decode_fattr(p, fattr);
510 dprintk("RPC: attrstat OK type %d mode %o dev %x ino %x\n",
511 fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
512 return 0;
516 * Decode diropres reply
517 * LOOKUP, CREATE, MKDIR
519 static int
520 nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
522 int status;
524 dprintk("RPC: diropres status %lx\n", ntohl(*p));
525 if ((status = ntohl(*p++)))
526 return -nfs_stat_to_errno(status);
527 p = xdr_decode_fhandle(p, res->fh);
528 xdr_decode_fattr(p, res->fattr);
529 dprintk("RPC: diropres OK type %x mode %o dev %x ino %x\n",
530 res->fattr->type, res->fattr->mode,
531 res->fattr->fsid, res->fattr->fileid);
532 return 0;
536 * Decode READLINK reply
538 static int
539 nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
541 int status;
543 if ((status = ntohl(*p++)))
544 return -nfs_stat_to_errno(status);
545 xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
547 /* Caller takes over the buffer here to avoid extra copy */
548 res->buffer = req->rq_task->tk_buffer;
549 req->rq_task->tk_buffer = NULL;
550 return 0;
554 * Decode STATFS reply
556 static int
557 nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
559 int status;
561 if ((status = ntohl(*p++)))
562 return -nfs_stat_to_errno(status);
563 res->tsize = ntohl(*p++);
564 res->bsize = ntohl(*p++);
565 res->blocks = ntohl(*p++);
566 res->bfree = ntohl(*p++);
567 res->bavail = ntohl(*p++);
568 return 0;
572 * We need to translate between nfs status return values and
573 * the local errno values which may not be the same.
575 static struct {
576 int stat;
577 int errno;
578 } nfs_errtbl[] = {
579 { NFS_OK, 0 },
580 { NFSERR_PERM, EPERM },
581 { NFSERR_NOENT, ENOENT },
582 { NFSERR_IO, errno_NFSERR_IO },
583 { NFSERR_NXIO, ENXIO },
584 { NFSERR_EAGAIN, EAGAIN },
585 { NFSERR_ACCES, EACCES },
586 { NFSERR_EXIST, EEXIST },
587 { NFSERR_XDEV, EXDEV },
588 { NFSERR_NODEV, ENODEV },
589 { NFSERR_NOTDIR, ENOTDIR },
590 { NFSERR_ISDIR, EISDIR },
591 { NFSERR_INVAL, EINVAL },
592 { NFSERR_FBIG, EFBIG },
593 { NFSERR_NOSPC, ENOSPC },
594 { NFSERR_ROFS, EROFS },
595 { NFSERR_NAMETOOLONG, ENAMETOOLONG },
596 { NFSERR_NOTEMPTY, ENOTEMPTY },
597 { NFSERR_DQUOT, EDQUOT },
598 { NFSERR_STALE, ESTALE },
599 #ifdef EWFLUSH
600 { NFSERR_WFLUSH, EWFLUSH },
601 #endif
602 { -1, EIO }
605 static int
606 nfs_stat_to_errno(int stat)
608 int i;
610 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
611 if (nfs_errtbl[i].stat == stat)
612 return nfs_errtbl[i].errno;
614 printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
615 return nfs_errtbl[i].errno;
618 #ifndef MAX
619 # define MAX(a, b) (((a) > (b))? (a) : (b))
620 #endif
622 #define PROC(proc, argtype, restype) \
623 { "nfs_" #proc, \
624 (kxdrproc_t) nfs_xdr_##argtype, \
625 (kxdrproc_t) nfs_xdr_##restype, \
626 MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2 \
629 static struct rpc_procinfo nfs_procedures[18] = {
630 PROC(null, enc_void, dec_void),
631 PROC(getattr, fhandle, attrstat),
632 PROC(setattr, sattrargs, attrstat),
633 PROC(root, enc_void, dec_void),
634 PROC(lookup, diropargs, diropres),
635 PROC(readlink, fhandle, readlinkres),
636 PROC(read, readargs, readres),
637 PROC(writecache, enc_void, dec_void),
638 PROC(write, writeargs, attrstat),
639 PROC(create, createargs, diropres),
640 PROC(remove, diropargs, stat),
641 PROC(rename, renameargs, stat),
642 PROC(link, linkargs, stat),
643 PROC(symlink, symlinkargs, stat),
644 PROC(mkdir, createargs, diropres),
645 PROC(rmdir, diropargs, stat),
646 PROC(readdir, readdirargs, readdirres),
647 PROC(statfs, fhandle, statfsres),
650 static struct rpc_version nfs_version2 = {
652 sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
653 nfs_procedures
656 static struct rpc_version * nfs_version[] = {
657 NULL,
658 NULL,
659 &nfs_version2
662 struct rpc_program nfs_program = {
663 "nfs",
664 NFS_PROGRAM,
665 sizeof(nfs_version) / sizeof(nfs_version[0]),
666 nfs_version,
667 &nfs_rpcstat,
671 * RPC stats support
673 static int
674 nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
676 return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
679 static struct proc_dir_entry proc_nfsclnt = {
680 0, 3, "nfs",
681 S_IFREG | S_IRUGO, 1, 0, 0,
682 6, NULL,
683 nfs_get_info
686 struct rpc_stat nfs_rpcstat = {
687 NULL, /* next */
688 &proc_nfsclnt, /* /proc/net directory entry */
689 &nfs_program, /* RPC program */