1
2
3
4
5
6
7
8
9
10
11
12#include <linux/param.h>
13#include <linux/time.h>
14#include <linux/mm.h>
15#include <linux/slab.h>
16#include <linux/errno.h>
17#include <linux/string.h>
18#include <linux/in.h>
19#include <linux/pagemap.h>
20#include <linux/proc_fs.h>
21#include <linux/sunrpc/clnt.h>
22#include <linux/nfs.h>
23#include <linux/nfs2.h>
24#include <linux/nfs_fs.h>
25#include "internal.h"
26
27#define NFSDBG_FACILITY NFSDBG_XDR
28
29
30#define errno_NFSERR_IO EIO
31
32
33
34
35
36#define NFS_fhandle_sz (8)
37#define NFS_sattr_sz (8)
38#define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2))
39#define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2))
40#define NFS_fattr_sz (17)
41#define NFS_info_sz (5)
42#define NFS_entry_sz (NFS_filename_sz+3)
43
44#define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz)
45#define NFS_removeargs_sz (NFS_fhandle_sz+NFS_filename_sz)
46#define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz)
47#define NFS_readlinkargs_sz (NFS_fhandle_sz)
48#define NFS_readargs_sz (NFS_fhandle_sz+3)
49#define NFS_writeargs_sz (NFS_fhandle_sz+4)
50#define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz)
51#define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz)
52#define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz)
53#define NFS_symlinkargs_sz (NFS_diropargs_sz+1+NFS_sattr_sz)
54#define NFS_readdirargs_sz (NFS_fhandle_sz+2)
55
56#define NFS_attrstat_sz (1+NFS_fattr_sz)
57#define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz)
58#define NFS_readlinkres_sz (2)
59#define NFS_readres_sz (1+NFS_fattr_sz+1)
60#define NFS_writeres_sz (NFS_attrstat_sz)
61#define NFS_stat_sz (1)
62#define NFS_readdirres_sz (1)
63#define NFS_statfsres_sz (1+NFS_info_sz)
64
65
66
67
68static inline __be32 *
69xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fhandle)
70{
71 memcpy(p, fhandle->data, NFS2_FHSIZE);
72 return p + XDR_QUADLEN(NFS2_FHSIZE);
73}
74
75static inline __be32 *
76xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle)
77{
78
79 fhandle->size = NFS2_FHSIZE;
80 memcpy(fhandle->data, p, NFS2_FHSIZE);
81 return p + XDR_QUADLEN(NFS2_FHSIZE);
82}
83
84static inline __be32*
85xdr_encode_time(__be32 *p, struct timespec *timep)
86{
87 *p++ = htonl(timep->tv_sec);
88
89 *p++ = htonl(timep->tv_nsec ? timep->tv_nsec / 1000 : 0);
90 return p;
91}
92
93static inline __be32*
94xdr_encode_current_server_time(__be32 *p, struct timespec *timep)
95{
96
97
98
99
100
101
102
103
104
105 *p++ = htonl(timep->tv_sec);
106 *p++ = htonl(1000000);
107 return p;
108}
109
110static inline __be32*
111xdr_decode_time(__be32 *p, struct timespec *timep)
112{
113 timep->tv_sec = ntohl(*p++);
114
115 timep->tv_nsec = ntohl(*p++) * 1000;
116 return p;
117}
118
119static __be32 *
120xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
121{
122 u32 rdev, type;
123 type = ntohl(*p++);
124 fattr->mode = ntohl(*p++);
125 fattr->nlink = ntohl(*p++);
126 fattr->uid = ntohl(*p++);
127 fattr->gid = ntohl(*p++);
128 fattr->size = ntohl(*p++);
129 fattr->du.nfs2.blocksize = ntohl(*p++);
130 rdev = ntohl(*p++);
131 fattr->du.nfs2.blocks = ntohl(*p++);
132 fattr->fsid.major = ntohl(*p++);
133 fattr->fsid.minor = 0;
134 fattr->fileid = ntohl(*p++);
135 p = xdr_decode_time(p, &fattr->atime);
136 p = xdr_decode_time(p, &fattr->mtime);
137 p = xdr_decode_time(p, &fattr->ctime);
138 fattr->valid |= NFS_ATTR_FATTR_V2;
139 fattr->rdev = new_decode_dev(rdev);
140 if (type == NFCHR && rdev == NFS2_FIFO_DEV) {
141 fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
142 fattr->rdev = 0;
143 }
144 return p;
145}
146
147static inline __be32 *
148xdr_encode_sattr(__be32 *p, struct iattr *attr)
149{
150 const __be32 not_set = __constant_htonl(0xFFFFFFFF);
151
152 *p++ = (attr->ia_valid & ATTR_MODE) ? htonl(attr->ia_mode) : not_set;
153 *p++ = (attr->ia_valid & ATTR_UID) ? htonl(attr->ia_uid) : not_set;
154 *p++ = (attr->ia_valid & ATTR_GID) ? htonl(attr->ia_gid) : not_set;
155 *p++ = (attr->ia_valid & ATTR_SIZE) ? htonl(attr->ia_size) : not_set;
156
157 if (attr->ia_valid & ATTR_ATIME_SET) {
158 p = xdr_encode_time(p, &attr->ia_atime);
159 } else if (attr->ia_valid & ATTR_ATIME) {
160 p = xdr_encode_current_server_time(p, &attr->ia_atime);
161 } else {
162 *p++ = not_set;
163 *p++ = not_set;
164 }
165
166 if (attr->ia_valid & ATTR_MTIME_SET) {
167 p = xdr_encode_time(p, &attr->ia_mtime);
168 } else if (attr->ia_valid & ATTR_MTIME) {
169 p = xdr_encode_current_server_time(p, &attr->ia_mtime);
170 } else {
171 *p++ = not_set;
172 *p++ = not_set;
173 }
174 return p;
175}
176
177
178
179
180
181
182
183
184static int
185nfs_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
186{
187 p = xdr_encode_fhandle(p, fh);
188 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
189 return 0;
190}
191
192
193
194
195static int
196nfs_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs_sattrargs *args)
197{
198 p = xdr_encode_fhandle(p, args->fh);
199 p = xdr_encode_sattr(p, args->sattr);
200 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
201 return 0;
202}
203
204
205
206
207
208static int
209nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args)
210{
211 p = xdr_encode_fhandle(p, args->fh);
212 p = xdr_encode_array(p, args->name, args->len);
213 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
214 return 0;
215}
216
217
218
219
220static int
221nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
222{
223 p = xdr_encode_fhandle(p, args->fh);
224 p = xdr_encode_array(p, args->name.name, args->name.len);
225 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
226 return 0;
227}
228
229
230
231
232
233
234static int
235nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
236{
237 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
238 unsigned int replen;
239 u32 offset = (u32)args->offset;
240 u32 count = args->count;
241
242 p = xdr_encode_fhandle(p, args->fh);
243 *p++ = htonl(offset);
244 *p++ = htonl(count);
245 *p++ = htonl(count);
246 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
247
248
249 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
250 xdr_inline_pages(&req->rq_rcv_buf, replen,
251 args->pages, args->pgbase, count);
252 req->rq_rcv_buf.flags |= XDRBUF_READ;
253 return 0;
254}
255
256
257
258
259static int
260nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
261{
262 struct kvec *iov = req->rq_rcv_buf.head;
263 size_t hdrlen;
264 u32 count, recvd;
265 int status;
266
267 if ((status = ntohl(*p++)))
268 return nfs_stat_to_errno(status);
269 p = xdr_decode_fattr(p, res->fattr);
270
271 count = ntohl(*p++);
272 res->eof = 0;
273 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
274 if (iov->iov_len < hdrlen) {
275 dprintk("NFS: READ reply header overflowed:"
276 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
277 return -errno_NFSERR_IO;
278 } else if (iov->iov_len != hdrlen) {
279 dprintk("NFS: READ header is short. iovec will be shifted.\n");
280 xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
281 }
282
283 recvd = req->rq_rcv_buf.len - hdrlen;
284 if (count > recvd) {
285 dprintk("NFS: server cheating in read reply: "
286 "count %u > recvd %u\n", count, recvd);
287 count = recvd;
288 }
289
290 dprintk("RPC: readres OK count %u\n", count);
291 if (count < res->count)
292 res->count = count;
293
294 return count;
295}
296
297
298
299
300
301static int
302nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
303{
304 struct xdr_buf *sndbuf = &req->rq_snd_buf;
305 u32 offset = (u32)args->offset;
306 u32 count = args->count;
307
308 p = xdr_encode_fhandle(p, args->fh);
309 *p++ = htonl(offset);
310 *p++ = htonl(offset);
311 *p++ = htonl(count);
312 *p++ = htonl(count);
313 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
314
315
316 xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
317 sndbuf->flags |= XDRBUF_WRITE;
318 return 0;
319}
320
321
322
323
324
325static int
326nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args)
327{
328 p = xdr_encode_fhandle(p, args->fh);
329 p = xdr_encode_array(p, args->name, args->len);
330 p = xdr_encode_sattr(p, args->sattr);
331 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
332 return 0;
333}
334
335
336
337
338static int
339nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
340{
341 p = xdr_encode_fhandle(p, args->fromfh);
342 p = xdr_encode_array(p, args->fromname, args->fromlen);
343 p = xdr_encode_fhandle(p, args->tofh);
344 p = xdr_encode_array(p, args->toname, args->tolen);
345 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
346 return 0;
347}
348
349
350
351
352static int
353nfs_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs_linkargs *args)
354{
355 p = xdr_encode_fhandle(p, args->fromfh);
356 p = xdr_encode_fhandle(p, args->tofh);
357 p = xdr_encode_array(p, args->toname, args->tolen);
358 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
359 return 0;
360}
361
362
363
364
365static int
366nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *args)
367{
368 struct xdr_buf *sndbuf = &req->rq_snd_buf;
369 size_t pad;
370
371 p = xdr_encode_fhandle(p, args->fromfh);
372 p = xdr_encode_array(p, args->fromname, args->fromlen);
373 *p++ = htonl(args->pathlen);
374 sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
375
376 xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen);
377
378
379
380
381
382
383 pad = sndbuf->tail->iov_len;
384 if (pad > 0)
385 p++;
386 p = xdr_encode_sattr(p, args->sattr);
387 sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
388 return 0;
389}
390
391
392
393
394static int
395nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args)
396{
397 struct rpc_task *task = req->rq_task;
398 struct rpc_auth *auth = task->tk_msg.rpc_cred->cr_auth;
399 unsigned int replen;
400 u32 count = args->count;
401
402 p = xdr_encode_fhandle(p, args->fh);
403 *p++ = htonl(args->cookie);
404 *p++ = htonl(count);
405 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
406
407
408 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
409 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
410 return 0;
411}
412
413
414
415
416
417
418
419
420static int
421nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
422{
423 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
424 struct kvec *iov = rcvbuf->head;
425 struct page **page;
426 size_t hdrlen;
427 unsigned int pglen, recvd;
428 u32 len;
429 int status, nr = 0;
430 __be32 *end, *entry, *kaddr;
431
432 if ((status = ntohl(*p++)))
433 return nfs_stat_to_errno(status);
434
435 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
436 if (iov->iov_len < hdrlen) {
437 dprintk("NFS: READDIR reply header overflowed:"
438 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
439 return -errno_NFSERR_IO;
440 } else if (iov->iov_len != hdrlen) {
441 dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
442 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
443 }
444
445 pglen = rcvbuf->page_len;
446 recvd = rcvbuf->len - hdrlen;
447 if (pglen > recvd)
448 pglen = recvd;
449 page = rcvbuf->pages;
450 kaddr = p = kmap_atomic(*page, KM_USER0);
451 end = (__be32 *)((char *)p + pglen);
452 entry = p;
453
454
455 if ((entry + 1) > end)
456 goto short_pkt;
457
458 for (; *p++; nr++) {
459 if (p + 2 > end)
460 goto short_pkt;
461 p++;
462 len = ntohl(*p++);
463 p += XDR_QUADLEN(len) + 1;
464 if (len > NFS2_MAXNAMLEN) {
465 dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
466 len);
467 goto err_unmap;
468 }
469 if (p + 2 > end)
470 goto short_pkt;
471 entry = p;
472 }
473
474
475
476
477
478
479 if (!nr && entry[1] == 0) {
480 dprintk("NFS: readdir reply truncated!\n");
481 entry[1] = 1;
482 }
483 out:
484 kunmap_atomic(kaddr, KM_USER0);
485 return nr;
486 short_pkt:
487
488
489
490
491
492
493
494
495
496 entry[0] = entry[1] = 0;
497 if (!nr)
498 nr = -errno_NFSERR_IO;
499 goto out;
500err_unmap:
501 nr = -errno_NFSERR_IO;
502 goto out;
503}
504
505__be32 *
506nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
507{
508 if (!*p++) {
509 if (!*p)
510 return ERR_PTR(-EAGAIN);
511 entry->eof = 1;
512 return ERR_PTR(-EBADCOOKIE);
513 }
514
515 entry->ino = ntohl(*p++);
516 entry->len = ntohl(*p++);
517 entry->name = (const char *) p;
518 p += XDR_QUADLEN(entry->len);
519 entry->prev_cookie = entry->cookie;
520 entry->cookie = ntohl(*p++);
521 entry->eof = !p[0] && p[1];
522
523 return p;
524}
525
526
527
528
529
530
531
532static int
533nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
534{
535 int status;
536
537 if ((status = ntohl(*p++)) != 0)
538 status = nfs_stat_to_errno(status);
539 return status;
540}
541
542
543
544
545
546static int
547nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
548{
549 int status;
550
551 if ((status = ntohl(*p++)))
552 return nfs_stat_to_errno(status);
553 xdr_decode_fattr(p, fattr);
554 return 0;
555}
556
557
558
559
560
561static int
562nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
563{
564 int status;
565
566 if ((status = ntohl(*p++)))
567 return nfs_stat_to_errno(status);
568 p = xdr_decode_fhandle(p, res->fh);
569 xdr_decode_fattr(p, res->fattr);
570 return 0;
571}
572
573
574
575
576static int
577nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args)
578{
579 struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
580 unsigned int replen;
581
582 p = xdr_encode_fhandle(p, args->fh);
583 req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
584
585
586 replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
587 xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
588 return 0;
589}
590
591
592
593
594static int
595nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
596{
597 struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
598 struct kvec *iov = rcvbuf->head;
599 size_t hdrlen;
600 u32 len, recvd;
601 char *kaddr;
602 int status;
603
604 if ((status = ntohl(*p++)))
605 return nfs_stat_to_errno(status);
606
607 len = ntohl(*p++);
608 if (len >= rcvbuf->page_len) {
609 dprintk("nfs: server returned giant symlink!\n");
610 return -ENAMETOOLONG;
611 }
612 hdrlen = (u8 *) p - (u8 *) iov->iov_base;
613 if (iov->iov_len < hdrlen) {
614 dprintk("NFS: READLINK reply header overflowed:"
615 "length %Zu > %Zu\n", hdrlen, iov->iov_len);
616 return -errno_NFSERR_IO;
617 } else if (iov->iov_len != hdrlen) {
618 dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
619 xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
620 }
621 recvd = req->rq_rcv_buf.len - hdrlen;
622 if (recvd < len) {
623 dprintk("NFS: server cheating in readlink reply: "
624 "count %u > recvd %u\n", len, recvd);
625 return -EIO;
626 }
627
628
629 kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0);
630 kaddr[len+rcvbuf->page_base] = '\0';
631 kunmap_atomic(kaddr, KM_USER0);
632 return 0;
633}
634
635
636
637
638static int
639nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
640{
641 res->verf->committed = NFS_FILE_SYNC;
642 return nfs_xdr_attrstat(req, p, res->fattr);
643}
644
645
646
647
648static int
649nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
650{
651 int status;
652
653 if ((status = ntohl(*p++)))
654 return nfs_stat_to_errno(status);
655
656 res->tsize = ntohl(*p++);
657 res->bsize = ntohl(*p++);
658 res->blocks = ntohl(*p++);
659 res->bfree = ntohl(*p++);
660 res->bavail = ntohl(*p++);
661 return 0;
662}
663
664
665
666
667
668static struct {
669 int stat;
670 int errno;
671} nfs_errtbl[] = {
672 { NFS_OK, 0 },
673 { NFSERR_PERM, -EPERM },
674 { NFSERR_NOENT, -ENOENT },
675 { NFSERR_IO, -errno_NFSERR_IO},
676 { NFSERR_NXIO, -ENXIO },
677
678 { NFSERR_ACCES, -EACCES },
679 { NFSERR_EXIST, -EEXIST },
680 { NFSERR_XDEV, -EXDEV },
681 { NFSERR_NODEV, -ENODEV },
682 { NFSERR_NOTDIR, -ENOTDIR },
683 { NFSERR_ISDIR, -EISDIR },
684 { NFSERR_INVAL, -EINVAL },
685 { NFSERR_FBIG, -EFBIG },
686 { NFSERR_NOSPC, -ENOSPC },
687 { NFSERR_ROFS, -EROFS },
688 { NFSERR_MLINK, -EMLINK },
689 { NFSERR_NAMETOOLONG, -ENAMETOOLONG },
690 { NFSERR_NOTEMPTY, -ENOTEMPTY },
691 { NFSERR_DQUOT, -EDQUOT },
692 { NFSERR_STALE, -ESTALE },
693 { NFSERR_REMOTE, -EREMOTE },
694#ifdef EWFLUSH
695 { NFSERR_WFLUSH, -EWFLUSH },
696#endif
697 { NFSERR_BADHANDLE, -EBADHANDLE },
698 { NFSERR_NOT_SYNC, -ENOTSYNC },
699 { NFSERR_BAD_COOKIE, -EBADCOOKIE },
700 { NFSERR_NOTSUPP, -ENOTSUPP },
701 { NFSERR_TOOSMALL, -ETOOSMALL },
702 { NFSERR_SERVERFAULT, -ESERVERFAULT },
703 { NFSERR_BADTYPE, -EBADTYPE },
704 { NFSERR_JUKEBOX, -EJUKEBOX },
705 { -1, -EIO }
706};
707
708
709
710
711
712int
713nfs_stat_to_errno(int stat)
714{
715 int i;
716
717 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
718 if (nfs_errtbl[i].stat == stat)
719 return nfs_errtbl[i].errno;
720 }
721 dprintk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
722 return nfs_errtbl[i].errno;
723}
724
725#define PROC(proc, argtype, restype, timer) \
726[NFSPROC_##proc] = { \
727 .p_proc = NFSPROC_##proc, \
728 .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \
729 .p_decode = (kxdrproc_t) nfs_xdr_##restype, \
730 .p_arglen = NFS_##argtype##_sz, \
731 .p_replen = NFS_##restype##_sz, \
732 .p_timer = timer, \
733 .p_statidx = NFSPROC_##proc, \
734 .p_name = #proc, \
735 }
736struct rpc_procinfo nfs_procedures[] = {
737 PROC(GETATTR, fhandle, attrstat, 1),
738 PROC(SETATTR, sattrargs, attrstat, 0),
739 PROC(LOOKUP, diropargs, diropres, 2),
740 PROC(READLINK, readlinkargs, readlinkres, 3),
741 PROC(READ, readargs, readres, 3),
742 PROC(WRITE, writeargs, writeres, 4),
743 PROC(CREATE, createargs, diropres, 0),
744 PROC(REMOVE, removeargs, stat, 0),
745 PROC(RENAME, renameargs, stat, 0),
746 PROC(LINK, linkargs, stat, 0),
747 PROC(SYMLINK, symlinkargs, stat, 0),
748 PROC(MKDIR, createargs, diropres, 0),
749 PROC(RMDIR, diropargs, stat, 0),
750 PROC(READDIR, readdirargs, readdirres, 3),
751 PROC(STATFS, fhandle, statfsres, 0),
752};
753
754struct rpc_version nfs_version2 = {
755 .number = 2,
756 .nrprocs = ARRAY_SIZE(nfs_procedures),
757 .procs = nfs_procedures
758};
759