Merge from vendor branch BSDTAR:
[dragonfly.git] / contrib / tcpdump-3.8.3 / print-nfs.c
1 /*
2  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code distributions
7  * retain the above copyright notice and this paragraph in its entirety, (2)
8  * distributions including binary code include the above copyright notice and
9  * this paragraph in its entirety in the documentation or other materials
10  * provided with the distribution, and (3) all advertising materials mentioning
11  * features or use of this software display the following acknowledgement:
12  * ``This product includes software developed by the University of California,
13  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14  * the University nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior
16  * written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21
22 #ifndef lint
23 static const char rcsid[] _U_ =
24     "@(#) $Header: /tcpdump/master/tcpdump/print-nfs.c,v 1.99.2.2 2003/11/16 08:51:35 guy Exp $ (LBL)";
25 #endif
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <tcpdump-stdinc.h>
32
33 #include <rpc/rpc.h>
34
35 #include <pcap.h>
36 #include <stdio.h>
37 #include <string.h>
38
39 #include "interface.h"
40 #include "addrtoname.h"
41 #include "extract.h"
42
43 #include "nfs.h"
44 #include "nfsfh.h"
45
46 #include "ip.h"
47 #ifdef INET6
48 #include "ip6.h"
49 #endif
50
51 static void nfs_printfh(const u_int32_t *, const u_int);
52 static void xid_map_enter(const struct rpc_msg *, const u_char *);
53 static int32_t xid_map_find(const struct rpc_msg *, const u_char *,
54                             u_int32_t *, u_int32_t *);
55 static void interp_reply(const struct rpc_msg *, u_int32_t, u_int32_t, int);
56 static const u_int32_t *parse_post_op_attr(const u_int32_t *, int);
57 static void print_sattr3(const struct nfsv3_sattr *sa3, int verbose);
58 static int print_int64(const u_int32_t *dp, int how);
59 static void print_nfsaddr(const u_char *, const char *, const char *);
60
61 /*
62  * Mapping of old NFS Version 2 RPC numbers to generic numbers.
63  */
64 u_int32_t nfsv3_procid[NFS_NPROCS] = {
65         NFSPROC_NULL,
66         NFSPROC_GETATTR,
67         NFSPROC_SETATTR,
68         NFSPROC_NOOP,
69         NFSPROC_LOOKUP,
70         NFSPROC_READLINK,
71         NFSPROC_READ,
72         NFSPROC_NOOP,
73         NFSPROC_WRITE,
74         NFSPROC_CREATE,
75         NFSPROC_REMOVE,
76         NFSPROC_RENAME,
77         NFSPROC_LINK,
78         NFSPROC_SYMLINK,
79         NFSPROC_MKDIR,
80         NFSPROC_RMDIR,
81         NFSPROC_READDIR,
82         NFSPROC_FSSTAT,
83         NFSPROC_NOOP,
84         NFSPROC_NOOP,
85         NFSPROC_NOOP,
86         NFSPROC_NOOP,
87         NFSPROC_NOOP,
88         NFSPROC_NOOP,
89         NFSPROC_NOOP,
90         NFSPROC_NOOP
91 };
92
93 /*
94  * NFS V2 and V3 status values.
95  *
96  * Some of these come from the RFCs for NFS V2 and V3, with the message
97  * strings taken from the FreeBSD C library "errlst.c".
98  *
99  * Others are errors that are not in the RFC but that I suspect some
100  * NFS servers could return; the values are FreeBSD errno values, as
101  * the first NFS server was the SunOS 2.0 one, and until 5.0 SunOS
102  * was primarily BSD-derived.
103  */
104 static struct tok status2str[] = {
105         { 1,     "Operation not permitted" },   /* EPERM */
106         { 2,     "No such file or directory" }, /* ENOENT */
107         { 5,     "Input/output error" },        /* EIO */
108         { 6,     "Device not configured" },     /* ENXIO */
109         { 11,    "Resource deadlock avoided" }, /* EDEADLK */
110         { 12,    "Cannot allocate memory" },    /* ENOMEM */
111         { 13,    "Permission denied" },         /* EACCES */
112         { 17,    "File exists" },               /* EEXIST */
113         { 18,    "Cross-device link" },         /* EXDEV */
114         { 19,    "Operation not supported by device" }, /* ENODEV */
115         { 20,    "Not a directory" },           /* ENOTDIR */
116         { 21,    "Is a directory" },            /* EISDIR */
117         { 22,    "Invalid argument" },          /* EINVAL */
118         { 26,    "Text file busy" },            /* ETXTBSY */
119         { 27,    "File too large" },            /* EFBIG */
120         { 28,    "No space left on device" },   /* ENOSPC */
121         { 30,    "Read-only file system" },     /* EROFS */
122         { 31,    "Too many links" },            /* EMLINK */
123         { 45,    "Operation not supported" },   /* EOPNOTSUPP */
124         { 62,    "Too many levels of symbolic links" }, /* ELOOP */
125         { 63,    "File name too long" },        /* ENAMETOOLONG */
126         { 66,    "Directory not empty" },       /* ENOTEMPTY */
127         { 69,    "Disc quota exceeded" },       /* EDQUOT */
128         { 70,    "Stale NFS file handle" },     /* ESTALE */
129         { 71,    "Too many levels of remote in path" }, /* EREMOTE */
130         { 99,    "Write cache flushed to disk" }, /* NFSERR_WFLUSH (not used) */
131         { 10001, "Illegal NFS file handle" },   /* NFS3ERR_BADHANDLE */
132         { 10002, "Update synchronization mismatch" }, /* NFS3ERR_NOT_SYNC */
133         { 10003, "READDIR/READDIRPLUS cookie is stale" }, /* NFS3ERR_BAD_COOKIE */
134         { 10004, "Operation not supported" },   /* NFS3ERR_NOTSUPP */
135         { 10005, "Buffer or request is too small" }, /* NFS3ERR_TOOSMALL */
136         { 10006, "Unspecified error on server" }, /* NFS3ERR_SERVERFAULT */
137         { 10007, "Object of that type not supported" }, /* NFS3ERR_BADTYPE */
138         { 10008, "Request couldn't be completed in time" }, /* NFS3ERR_JUKEBOX */
139         { 0,     NULL }
140 };
141
142 static struct tok nfsv3_writemodes[] = {
143         { 0,            "unstable" },
144         { 1,            "datasync" },
145         { 2,            "filesync" },
146         { 0,            NULL }
147 };
148
149 static struct tok type2str[] = {
150         { NFNON,        "NON" },
151         { NFREG,        "REG" },
152         { NFDIR,        "DIR" },
153         { NFBLK,        "BLK" },
154         { NFCHR,        "CHR" },
155         { NFLNK,        "LNK" },
156         { NFFIFO,       "FIFO" },
157         { 0,            NULL }
158 };
159
160 /*
161  * Print out a 64-bit integer. This appears to be different on each system,
162  * try to make the best of it. The integer stored as 2 consecutive XDR
163  * encoded 32-bit integers, to which a pointer is passed.
164  *
165  * Assume that a system that has INT64_FORMAT defined, has a 64-bit
166  * integer datatype and can print it.
167  */
168
169 #define UNSIGNED 0
170 #define SIGNED   1
171 #define HEX      2
172
173 static int print_int64(const u_int32_t *dp, int how)
174 {
175 #ifdef INT64_FORMAT
176         u_int64_t res;
177
178         res = ((u_int64_t)EXTRACT_32BITS(&dp[0]) << 32) | (u_int64_t)EXTRACT_32BITS(&dp[1]);
179         switch (how) {
180         case SIGNED:
181                 printf(INT64_FORMAT, res);
182                 break;
183         case UNSIGNED:
184                 printf(U_INT64_FORMAT, res);
185                 break;
186         case HEX:
187                 printf(HEX_INT64_FORMAT, res);
188                 break;
189         default:
190                 return (0);
191         }
192 #else
193         u_int32_t high;
194
195         high = EXTRACT_32BITS(&dp[0]);
196
197         switch (how) {
198         case SIGNED:
199         case UNSIGNED:
200         case HEX:
201                 if (high != 0)
202                         printf("0x%x%08x", high, EXTRACT_32BITS(&dp[1]));
203                 else
204                         printf("0x%x", EXTRACT_32BITS(&dp[1]));
205                 break;
206         default:
207                 return (0);
208         }
209 #endif
210         return 1;
211 }
212
213 static void
214 print_nfsaddr(const u_char *bp, const char *s, const char *d)
215 {
216         struct ip *ip;
217 #ifdef INET6
218         struct ip6_hdr *ip6;
219         char srcaddr[INET6_ADDRSTRLEN], dstaddr[INET6_ADDRSTRLEN];
220 #else
221 #ifndef INET_ADDRSTRLEN
222 #define INET_ADDRSTRLEN 16
223 #endif
224         char srcaddr[INET_ADDRSTRLEN], dstaddr[INET_ADDRSTRLEN];
225 #endif
226
227         srcaddr[0] = dstaddr[0] = '\0';
228         switch (IP_V((struct ip *)bp)) {
229         case 4:
230                 ip = (struct ip *)bp;
231                 strlcpy(srcaddr, ipaddr_string(&ip->ip_src), sizeof(srcaddr));
232                 strlcpy(dstaddr, ipaddr_string(&ip->ip_dst), sizeof(dstaddr));
233                 break;
234 #ifdef INET6
235         case 6:
236                 ip6 = (struct ip6_hdr *)bp;
237                 strlcpy(srcaddr, ip6addr_string(&ip6->ip6_src),
238                     sizeof(srcaddr));
239                 strlcpy(dstaddr, ip6addr_string(&ip6->ip6_dst),
240                     sizeof(dstaddr));
241                 break;
242 #endif
243         default:
244                 strlcpy(srcaddr, "?", sizeof(srcaddr));
245                 strlcpy(dstaddr, "?", sizeof(dstaddr));
246                 break;
247         }
248
249         (void)printf("%s.%s > %s.%s: ", srcaddr, s, dstaddr, d);
250 }
251
252 static const u_int32_t *
253 parse_sattr3(const u_int32_t *dp, struct nfsv3_sattr *sa3)
254 {
255         TCHECK(dp[0]);
256         sa3->sa_modeset = EXTRACT_32BITS(dp);
257         dp++;
258         if (sa3->sa_modeset) {
259                 TCHECK(dp[0]);
260                 sa3->sa_mode = EXTRACT_32BITS(dp);
261                 dp++;
262         }
263
264         TCHECK(dp[0]);
265         sa3->sa_uidset = EXTRACT_32BITS(dp);
266         dp++;
267         if (sa3->sa_uidset) {
268                 TCHECK(dp[0]);
269                 sa3->sa_uid = EXTRACT_32BITS(dp);
270                 dp++;
271         }
272
273         TCHECK(dp[0]);
274         sa3->sa_gidset = EXTRACT_32BITS(dp);
275         dp++;
276         if (sa3->sa_gidset) {
277                 TCHECK(dp[0]);
278                 sa3->sa_gid = EXTRACT_32BITS(dp);
279                 dp++;
280         }
281
282         TCHECK(dp[0]);
283         sa3->sa_sizeset = EXTRACT_32BITS(dp);
284         dp++;
285         if (sa3->sa_sizeset) {
286                 TCHECK(dp[0]);
287                 sa3->sa_size = EXTRACT_32BITS(dp);
288                 dp++;
289         }
290
291         TCHECK(dp[0]);
292         sa3->sa_atimetype = EXTRACT_32BITS(dp);
293         dp++;
294         if (sa3->sa_atimetype == NFSV3SATTRTIME_TOCLIENT) {
295                 TCHECK(dp[1]);
296                 sa3->sa_atime.nfsv3_sec = EXTRACT_32BITS(dp);
297                 dp++;
298                 sa3->sa_atime.nfsv3_nsec = EXTRACT_32BITS(dp);
299                 dp++;
300         }
301
302         TCHECK(dp[0]);
303         sa3->sa_mtimetype = EXTRACT_32BITS(dp);
304         dp++;
305         if (sa3->sa_mtimetype == NFSV3SATTRTIME_TOCLIENT) {
306                 TCHECK(dp[1]);
307                 sa3->sa_mtime.nfsv3_sec = EXTRACT_32BITS(dp);
308                 dp++;
309                 sa3->sa_mtime.nfsv3_nsec = EXTRACT_32BITS(dp);
310                 dp++;
311         }
312
313         return dp;
314 trunc:
315         return NULL;
316 }
317
318 static int nfserr;              /* true if we error rather than trunc */
319
320 static void
321 print_sattr3(const struct nfsv3_sattr *sa3, int verbose)
322 {
323         if (sa3->sa_modeset)
324                 printf(" mode %o", sa3->sa_mode);
325         if (sa3->sa_uidset)
326                 printf(" uid %u", sa3->sa_uid);
327         if (sa3->sa_gidset)
328                 printf(" gid %u", sa3->sa_gid);
329         if (verbose > 1) {
330                 if (sa3->sa_atimetype == NFSV3SATTRTIME_TOCLIENT)
331                         printf(" atime %u.%06u", sa3->sa_atime.nfsv3_sec,
332                                sa3->sa_atime.nfsv3_nsec);
333                 if (sa3->sa_mtimetype == NFSV3SATTRTIME_TOCLIENT)
334                         printf(" mtime %u.%06u", sa3->sa_mtime.nfsv3_sec,
335                                sa3->sa_mtime.nfsv3_nsec);
336         }
337 }
338
339 void
340 nfsreply_print(register const u_char *bp, u_int length,
341                register const u_char *bp2)
342 {
343         register const struct rpc_msg *rp;
344         u_int32_t proc, vers;
345         char srcid[20], dstid[20];      /*fits 32bit*/
346
347         nfserr = 0;             /* assume no error */
348         rp = (const struct rpc_msg *)bp;
349
350         if (!nflag) {
351                 strlcpy(srcid, "nfs", sizeof(srcid));
352                 snprintf(dstid, sizeof(dstid), "%u",
353                     EXTRACT_32BITS(&rp->rm_xid));
354         } else {
355                 snprintf(srcid, sizeof(srcid), "%u", NFS_PORT);
356                 snprintf(dstid, sizeof(dstid), "%u",
357                     EXTRACT_32BITS(&rp->rm_xid));
358         }
359         print_nfsaddr(bp2, srcid, dstid);
360         (void)printf("reply %s %d",
361                      EXTRACT_32BITS(&rp->rm_reply.rp_stat) == MSG_ACCEPTED?
362                              "ok":"ERR",
363                              length);
364
365         if (xid_map_find(rp, bp2, &proc, &vers) >= 0)
366                 interp_reply(rp, proc, vers, length);
367 }
368
369 /*
370  * Return a pointer to the first file handle in the packet.
371  * If the packet was truncated, return 0.
372  */
373 static const u_int32_t *
374 parsereq(register const struct rpc_msg *rp, register u_int length)
375 {
376         register const u_int32_t *dp;
377         register u_int len;
378
379         /*
380          * find the start of the req data (if we captured it)
381          */
382         dp = (u_int32_t *)&rp->rm_call.cb_cred;
383         TCHECK(dp[1]);
384         len = EXTRACT_32BITS(&dp[1]);
385         if (len < length) {
386                 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
387                 TCHECK(dp[1]);
388                 len = EXTRACT_32BITS(&dp[1]);
389                 if (len < length) {
390                         dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
391                         TCHECK2(dp[0], 0);
392                         return (dp);
393                 }
394         }
395 trunc:
396         return (NULL);
397 }
398
399 /*
400  * Print out an NFS file handle and return a pointer to following word.
401  * If packet was truncated, return 0.
402  */
403 static const u_int32_t *
404 parsefh(register const u_int32_t *dp, int v3)
405 {
406         u_int len;
407
408         if (v3) {
409                 TCHECK(dp[0]);
410                 len = EXTRACT_32BITS(dp) / 4;
411                 dp++;
412         } else
413                 len = NFSX_V2FH / 4;
414
415         if (TTEST2(*dp, len * sizeof(*dp))) {
416                 nfs_printfh(dp, len);
417                 return (dp + len);
418         }
419 trunc:
420         return (NULL);
421 }
422
423 /*
424  * Print out a file name and return pointer to 32-bit word past it.
425  * If packet was truncated, return 0.
426  */
427 static const u_int32_t *
428 parsefn(register const u_int32_t *dp)
429 {
430         register u_int32_t len;
431         register const u_char *cp;
432
433         /* Bail if we don't have the string length */
434         TCHECK(*dp);
435
436         /* Fetch string length; convert to host order */
437         len = *dp++;
438         NTOHL(len);
439
440         TCHECK2(*dp, ((len + 3) & ~3));
441
442         cp = (u_char *)dp;
443         /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
444         dp += ((len + 3) & ~3) / sizeof(*dp);
445         /* XXX seems like we should be checking the length */
446         putchar('"');
447         (void) fn_printn(cp, len, NULL);
448         putchar('"');
449
450         return (dp);
451 trunc:
452         return NULL;
453 }
454
455 /*
456  * Print out file handle and file name.
457  * Return pointer to 32-bit word past file name.
458  * If packet was truncated (or there was some other error), return 0.
459  */
460 static const u_int32_t *
461 parsefhn(register const u_int32_t *dp, int v3)
462 {
463         dp = parsefh(dp, v3);
464         if (dp == NULL)
465                 return (NULL);
466         putchar(' ');
467         return (parsefn(dp));
468 }
469
470 void
471 nfsreq_print(register const u_char *bp, u_int length,
472     register const u_char *bp2)
473 {
474         register const struct rpc_msg *rp;
475         register const u_int32_t *dp;
476         nfs_type type;
477         int v3;
478         u_int32_t proc;
479         struct nfsv3_sattr sa3;
480         char srcid[20], dstid[20];      /*fits 32bit*/
481
482         nfserr = 0;             /* assume no error */
483         rp = (const struct rpc_msg *)bp;
484         if (!nflag) {
485                 snprintf(srcid, sizeof(srcid), "%u",
486                     EXTRACT_32BITS(&rp->rm_xid));
487                 strlcpy(dstid, "nfs", sizeof(dstid));
488         } else {
489                 snprintf(srcid, sizeof(srcid), "%u",
490                     EXTRACT_32BITS(&rp->rm_xid));
491                 snprintf(dstid, sizeof(dstid), "%u", NFS_PORT);
492         }
493         print_nfsaddr(bp2, srcid, dstid);
494         (void)printf("%d", length);
495
496         xid_map_enter(rp, bp2); /* record proc number for later on */
497
498         v3 = (EXTRACT_32BITS(&rp->rm_call.cb_vers) == NFS_VER3);
499         proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
500
501         if (!v3 && proc < NFS_NPROCS)
502                 proc =  nfsv3_procid[proc];
503
504         switch (proc) {
505         case NFSPROC_NOOP:
506                 printf(" nop");
507                 return;
508         case NFSPROC_NULL:
509                 printf(" null");
510                 return;
511
512         case NFSPROC_GETATTR:
513                 printf(" getattr");
514                 if ((dp = parsereq(rp, length)) != NULL &&
515                     parsefh(dp, v3) != NULL)
516                         return;
517                 break;
518
519         case NFSPROC_SETATTR:
520                 printf(" setattr");
521                 if ((dp = parsereq(rp, length)) != NULL &&
522                     parsefh(dp, v3) != NULL)
523                         return;
524                 break;
525
526         case NFSPROC_LOOKUP:
527                 printf(" lookup");
528                 if ((dp = parsereq(rp, length)) != NULL &&
529                     parsefhn(dp, v3) != NULL)
530                         return;
531                 break;
532
533         case NFSPROC_ACCESS:
534                 printf(" access");
535                 if ((dp = parsereq(rp, length)) != NULL &&
536                     (dp = parsefh(dp, v3)) != NULL) {
537                         TCHECK(dp[0]);
538                         printf(" %04x", EXTRACT_32BITS(&dp[0]));
539                         return;
540                 }
541                 break;
542
543         case NFSPROC_READLINK:
544                 printf(" readlink");
545                 if ((dp = parsereq(rp, length)) != NULL &&
546                     parsefh(dp, v3) != NULL)
547                         return;
548                 break;
549
550         case NFSPROC_READ:
551                 printf(" read");
552                 if ((dp = parsereq(rp, length)) != NULL &&
553                     (dp = parsefh(dp, v3)) != NULL) {
554                         if (v3) {
555                                 TCHECK(dp[2]);
556                                 printf(" %u bytes @ ",
557                                        EXTRACT_32BITS(&dp[2]));
558                                 print_int64(dp, UNSIGNED);
559                         } else {
560                                 TCHECK(dp[1]);
561                                 printf(" %u bytes @ %u",
562                                     EXTRACT_32BITS(&dp[1]),
563                                     EXTRACT_32BITS(&dp[0]));
564                         }
565                         return;
566                 }
567                 break;
568
569         case NFSPROC_WRITE:
570                 printf(" write");
571                 if ((dp = parsereq(rp, length)) != NULL &&
572                     (dp = parsefh(dp, v3)) != NULL) {
573                         if (v3) {
574                                 TCHECK(dp[4]);
575                                 printf(" %u bytes @ ",
576                                                 EXTRACT_32BITS(&dp[4]));
577                                 print_int64(dp, UNSIGNED);
578                                 if (vflag) {
579                                         dp += 3;
580                                         TCHECK(dp[0]);
581                                         printf(" <%s>",
582                                                 tok2str(nfsv3_writemodes,
583                                                         NULL, EXTRACT_32BITS(dp)));
584                                 }
585                         } else {
586                                 TCHECK(dp[3]);
587                                 printf(" %u (%u) bytes @ %u (%u)",
588                                                 EXTRACT_32BITS(&dp[3]),
589                                                 EXTRACT_32BITS(&dp[2]),
590                                                 EXTRACT_32BITS(&dp[1]),
591                                                 EXTRACT_32BITS(&dp[0]));
592                         }
593                         return;
594                 }
595                 break;
596
597         case NFSPROC_CREATE:
598                 printf(" create");
599                 if ((dp = parsereq(rp, length)) != NULL &&
600                     parsefhn(dp, v3) != NULL)
601                         return;
602                 break;
603
604         case NFSPROC_MKDIR:
605                 printf(" mkdir");
606                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp, v3) != 0)
607                         return;
608                 break;
609
610         case NFSPROC_SYMLINK:
611                 printf(" symlink");
612                 if ((dp = parsereq(rp, length)) != 0 &&
613                     (dp = parsefhn(dp, v3)) != 0) {
614                         fputs(" ->", stdout);
615                         if (v3 && (dp = parse_sattr3(dp, &sa3)) == 0)
616                                 break;
617                         if (parsefn(dp) == 0)
618                                 break;
619                         if (v3 && vflag)
620                                 print_sattr3(&sa3, vflag);
621                         return;
622                 }
623                 break;
624
625         case NFSPROC_MKNOD:
626                 printf(" mknod");
627                 if ((dp = parsereq(rp, length)) != 0 &&
628                     (dp = parsefhn(dp, v3)) != 0) {
629                         TCHECK(*dp);
630                         type = (nfs_type)EXTRACT_32BITS(dp);
631                         dp++;
632                         if ((dp = parse_sattr3(dp, &sa3)) == 0)
633                                 break;
634                         printf(" %s", tok2str(type2str, "unk-ft %d", type));
635                         if (vflag && (type == NFCHR || type == NFBLK)) {
636                                 TCHECK(dp[1]);
637                                 printf(" %u/%u",
638                                        EXTRACT_32BITS(&dp[0]),
639                                        EXTRACT_32BITS(&dp[1]));
640                                 dp += 2;
641                         }
642                         if (vflag)
643                                 print_sattr3(&sa3, vflag);
644                         return;
645                 }
646                 break;
647
648         case NFSPROC_REMOVE:
649                 printf(" remove");
650                 if ((dp = parsereq(rp, length)) != NULL &&
651                     parsefhn(dp, v3) != NULL)
652                         return;
653                 break;
654
655         case NFSPROC_RMDIR:
656                 printf(" rmdir");
657                 if ((dp = parsereq(rp, length)) != NULL &&
658                     parsefhn(dp, v3) != NULL)
659                         return;
660                 break;
661
662         case NFSPROC_RENAME:
663                 printf(" rename");
664                 if ((dp = parsereq(rp, length)) != NULL &&
665                     (dp = parsefhn(dp, v3)) != NULL) {
666                         fputs(" ->", stdout);
667                         if (parsefhn(dp, v3) != NULL)
668                                 return;
669                 }
670                 break;
671
672         case NFSPROC_LINK:
673                 printf(" link");
674                 if ((dp = parsereq(rp, length)) != NULL &&
675                     (dp = parsefh(dp, v3)) != NULL) {
676                         fputs(" ->", stdout);
677                         if (parsefhn(dp, v3) != NULL)
678                                 return;
679                 }
680                 break;
681
682         case NFSPROC_READDIR:
683                 printf(" readdir");
684                 if ((dp = parsereq(rp, length)) != NULL &&
685                     (dp = parsefh(dp, v3)) != NULL) {
686                         if (v3) {
687                                 TCHECK(dp[4]);
688                                 /*
689                                  * We shouldn't really try to interpret the
690                                  * offset cookie here.
691                                  */
692                                 printf(" %u bytes @ ",
693                                     EXTRACT_32BITS(&dp[4]));
694                                 print_int64(dp, SIGNED);
695                                 if (vflag)
696                                         printf(" verf %08x%08x", dp[2],
697                                                dp[3]);
698                         } else {
699                                 TCHECK(dp[1]);
700                                 /*
701                                  * Print the offset as signed, since -1 is
702                                  * common, but offsets > 2^31 aren't.
703                                  */
704                                 printf(" %u bytes @ %d",
705                                     EXTRACT_32BITS(&dp[1]),
706                                     EXTRACT_32BITS(&dp[0]));
707                         }
708                         return;
709                 }
710                 break;
711
712         case NFSPROC_READDIRPLUS:
713                 printf(" readdirplus");
714                 if ((dp = parsereq(rp, length)) != NULL &&
715                     (dp = parsefh(dp, v3)) != NULL) {
716                         TCHECK(dp[4]);
717                         /*
718                          * We don't try to interpret the offset
719                          * cookie here.
720                          */
721                         printf(" %u bytes @ ", EXTRACT_32BITS(&dp[4]));
722                         print_int64(dp, SIGNED);
723                         if (vflag)
724                                 printf(" max %u verf %08x%08x",
725                                        EXTRACT_32BITS(&dp[5]), dp[2], dp[3]);
726                         return;
727                 }
728                 break;
729
730         case NFSPROC_FSSTAT:
731                 printf(" fsstat");
732                 if ((dp = parsereq(rp, length)) != NULL &&
733                     parsefh(dp, v3) != NULL)
734                         return;
735                 break;
736
737         case NFSPROC_FSINFO:
738                 printf(" fsinfo");
739                 if ((dp = parsereq(rp, length)) != NULL &&
740                     parsefh(dp, v3) != NULL)
741                         return;
742                 break;
743
744         case NFSPROC_PATHCONF:
745                 printf(" pathconf");
746                 if ((dp = parsereq(rp, length)) != NULL &&
747                     parsefh(dp, v3) != NULL)
748                         return;
749                 break;
750
751         case NFSPROC_COMMIT:
752                 printf(" commit");
753                 if ((dp = parsereq(rp, length)) != NULL &&
754                     (dp = parsefh(dp, v3)) != NULL) {
755                         printf(" %u bytes @ ", EXTRACT_32BITS(&dp[2]));
756                         print_int64(dp, UNSIGNED);
757                         return;
758                 }
759                 break;
760
761         default:
762                 printf(" proc-%u", EXTRACT_32BITS(&rp->rm_call.cb_proc));
763                 return;
764         }
765
766 trunc:
767         if (!nfserr)
768                 fputs(" [|nfs]", stdout);
769 }
770
771 /*
772  * Print out an NFS file handle.
773  * We assume packet was not truncated before the end of the
774  * file handle pointed to by dp.
775  *
776  * Note: new version (using portable file-handle parser) doesn't produce
777  * generation number.  It probably could be made to do that, with some
778  * additional hacking on the parser code.
779  */
780 static void
781 nfs_printfh(register const u_int32_t *dp, const u_int len)
782 {
783         my_fsid fsid;
784         ino_t ino;
785         const char *sfsname = NULL;
786         char *spacep;
787
788         if (uflag) {
789                 u_int i;
790                 char const *sep = "";
791
792                 printf(" fh[");
793                 for (i=0; i<len; i++) {
794                         (void)printf("%s%x", sep, dp[i]);
795                         sep = ":";
796                 }
797                 printf("]");
798                 return;
799         }
800
801         Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
802
803         if (sfsname) {
804                 /* file system ID is ASCII, not numeric, for this server OS */
805                 static char temp[NFSX_V3FHMAX+1];
806
807                 /* Make sure string is null-terminated */
808                 strncpy(temp, sfsname, NFSX_V3FHMAX);
809                 temp[sizeof(temp) - 1] = '\0';
810                 /* Remove trailing spaces */
811                 spacep = strchr(temp, ' ');
812                 if (spacep)
813                         *spacep = '\0';
814
815                 (void)printf(" fh %s/", temp);
816         } else {
817                 (void)printf(" fh %d,%d/",
818                              fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor);
819         }
820
821         if(fsid.Fsid_dev.Minor == 257)
822                 /* Print the undecoded handle */
823                 (void)printf("%s", fsid.Opaque_Handle);
824         else
825                 (void)printf("%ld", (long) ino);
826 }
827
828 /*
829  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
830  * us to match up replies with requests and thus to know how to parse
831  * the reply.
832  */
833
834 struct xid_map_entry {
835         u_int32_t       xid;            /* transaction ID (net order) */
836         int ipver;                      /* IP version (4 or 6) */
837 #ifdef INET6
838         struct in6_addr client;         /* client IP address (net order) */
839         struct in6_addr server;         /* server IP address (net order) */
840 #else
841         struct in_addr  client;         /* client IP address (net order) */
842         struct in_addr  server;         /* server IP address (net order) */
843 #endif
844         u_int32_t       proc;           /* call proc number (host order) */
845         u_int32_t       vers;           /* program version (host order) */
846 };
847
848 /*
849  * Map entries are kept in an array that we manage as a ring;
850  * new entries are always added at the tail of the ring.  Initially,
851  * all the entries are zero and hence don't match anything.
852  */
853
854 #define XIDMAPSIZE      64
855
856 struct xid_map_entry xid_map[XIDMAPSIZE];
857
858 int     xid_map_next = 0;
859 int     xid_map_hint = 0;
860
861 static void
862 xid_map_enter(const struct rpc_msg *rp, const u_char *bp)
863 {
864         struct ip *ip = NULL;
865 #ifdef INET6
866         struct ip6_hdr *ip6 = NULL;
867 #endif
868         struct xid_map_entry *xmep;
869
870         switch (IP_V((struct ip *)bp)) {
871         case 4:
872                 ip = (struct ip *)bp;
873                 break;
874 #ifdef INET6
875         case 6:
876                 ip6 = (struct ip6_hdr *)bp;
877                 break;
878 #endif
879         default:
880                 return;
881         }
882
883         xmep = &xid_map[xid_map_next];
884
885         if (++xid_map_next >= XIDMAPSIZE)
886                 xid_map_next = 0;
887
888         xmep->xid = rp->rm_xid;
889         if (ip) {
890                 xmep->ipver = 4;
891                 memcpy(&xmep->client, &ip->ip_src, sizeof(ip->ip_src));
892                 memcpy(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst));
893         }
894 #ifdef INET6
895         else if (ip6) {
896                 xmep->ipver = 6;
897                 memcpy(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src));
898                 memcpy(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
899         }
900 #endif
901         xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
902         xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers);
903 }
904
905 /*
906  * Returns 0 and puts NFSPROC_xxx in proc return and
907  * version in vers return, or returns -1 on failure
908  */
909 static int
910 xid_map_find(const struct rpc_msg *rp, const u_char *bp, u_int32_t *proc,
911              u_int32_t *vers)
912 {
913         int i;
914         struct xid_map_entry *xmep;
915         u_int32_t xid = rp->rm_xid;
916         struct ip *ip = (struct ip *)bp;
917 #ifdef INET6
918         struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
919 #endif
920         int cmp;
921
922         /* Start searching from where we last left off */
923         i = xid_map_hint;
924         do {
925                 xmep = &xid_map[i];
926                 cmp = 1;
927                 if (xmep->ipver != IP_V(ip) || xmep->xid != xid)
928                         goto nextitem;
929                 switch (xmep->ipver) {
930                 case 4:
931                         if (memcmp(&ip->ip_src, &xmep->server,
932                                    sizeof(ip->ip_src)) != 0 ||
933                             memcmp(&ip->ip_dst, &xmep->client,
934                                    sizeof(ip->ip_dst)) != 0) {
935                                 cmp = 0;
936                         }
937                         break;
938 #ifdef INET6
939                 case 6:
940                         if (memcmp(&ip6->ip6_src, &xmep->server,
941                                    sizeof(ip6->ip6_src)) != 0 ||
942                             memcmp(&ip6->ip6_dst, &xmep->client,
943                                    sizeof(ip6->ip6_dst)) != 0) {
944                                 cmp = 0;
945                         }
946                         break;
947 #endif
948                 default:
949                         cmp = 0;
950                         break;
951                 }
952                 if (cmp) {
953                         /* match */
954                         xid_map_hint = i;
955                         *proc = xmep->proc;
956                         *vers = xmep->vers;
957                         return 0;
958                 }
959         nextitem:
960                 if (++i >= XIDMAPSIZE)
961                         i = 0;
962         } while (i != xid_map_hint);
963
964         /* search failed */
965         return (-1);
966 }
967
968 /*
969  * Routines for parsing reply packets
970  */
971
972 /*
973  * Return a pointer to the beginning of the actual results.
974  * If the packet was truncated, return 0.
975  */
976 static const u_int32_t *
977 parserep(register const struct rpc_msg *rp, register u_int length)
978 {
979         register const u_int32_t *dp;
980         u_int len;
981         enum accept_stat astat;
982
983         /*
984          * Portability note:
985          * Here we find the address of the ar_verf credentials.
986          * Originally, this calculation was
987          *      dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
988          * On the wire, the rp_acpt field starts immediately after
989          * the (32 bit) rp_stat field.  However, rp_acpt (which is a
990          * "struct accepted_reply") contains a "struct opaque_auth",
991          * whose internal representation contains a pointer, so on a
992          * 64-bit machine the compiler inserts 32 bits of padding
993          * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
994          * the internal representation to parse the on-the-wire
995          * representation.  Instead, we skip past the rp_stat field,
996          * which is an "enum" and so occupies one 32-bit word.
997          */
998         dp = ((const u_int32_t *)&rp->rm_reply) + 1;
999         TCHECK(dp[1]);
1000         len = EXTRACT_32BITS(&dp[1]);
1001         if (len >= length)
1002                 return (NULL);
1003         /*
1004          * skip past the ar_verf credentials.
1005          */
1006         dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
1007         TCHECK2(dp[0], 0);
1008
1009         /*
1010          * now we can check the ar_stat field
1011          */
1012         astat = EXTRACT_32BITS(dp);
1013         switch (astat) {
1014
1015         case SUCCESS:
1016                 break;
1017
1018         case PROG_UNAVAIL:
1019                 printf(" PROG_UNAVAIL");
1020                 nfserr = 1;             /* suppress trunc string */
1021                 return (NULL);
1022
1023         case PROG_MISMATCH:
1024                 printf(" PROG_MISMATCH");
1025                 nfserr = 1;             /* suppress trunc string */
1026                 return (NULL);
1027
1028         case PROC_UNAVAIL:
1029                 printf(" PROC_UNAVAIL");
1030                 nfserr = 1;             /* suppress trunc string */
1031                 return (NULL);
1032
1033         case GARBAGE_ARGS:
1034                 printf(" GARBAGE_ARGS");
1035                 nfserr = 1;             /* suppress trunc string */
1036                 return (NULL);
1037
1038         case SYSTEM_ERR:
1039                 printf(" SYSTEM_ERR");
1040                 nfserr = 1;             /* suppress trunc string */
1041                 return (NULL);
1042
1043         default:
1044                 printf(" ar_stat %d", astat);
1045                 nfserr = 1;             /* suppress trunc string */
1046                 return (NULL);
1047         }
1048         /* successful return */
1049         TCHECK2(*dp, sizeof(astat));
1050         return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
1051 trunc:
1052         return (0);
1053 }
1054
1055 static const u_int32_t *
1056 parsestatus(const u_int32_t *dp, int *er)
1057 {
1058         int errnum;
1059
1060         TCHECK(dp[0]);
1061
1062         errnum = EXTRACT_32BITS(&dp[0]);
1063         if (er)
1064                 *er = errnum;
1065         if (errnum != 0) {
1066                 if (!qflag)
1067                         printf(" ERROR: %s",
1068                             tok2str(status2str, "unk %d", errnum));
1069                 nfserr = 1;
1070         }
1071         return (dp + 1);
1072 trunc:
1073         return NULL;
1074 }
1075
1076 static const u_int32_t *
1077 parsefattr(const u_int32_t *dp, int verbose, int v3)
1078 {
1079         const struct nfs_fattr *fap;
1080
1081         fap = (const struct nfs_fattr *)dp;
1082         TCHECK(fap->fa_gid);
1083         if (verbose) {
1084                 printf(" %s %o ids %d/%d",
1085                     tok2str(type2str, "unk-ft %d ",
1086                     EXTRACT_32BITS(&fap->fa_type)),
1087                     EXTRACT_32BITS(&fap->fa_mode),
1088                     EXTRACT_32BITS(&fap->fa_uid),
1089                     EXTRACT_32BITS(&fap->fa_gid));
1090                 if (v3) {
1091                         TCHECK(fap->fa3_size);
1092                         printf(" sz ");
1093                         print_int64((u_int32_t *)&fap->fa3_size, UNSIGNED);
1094                 } else {
1095                         TCHECK(fap->fa2_size);
1096                         printf(" sz %d", EXTRACT_32BITS(&fap->fa2_size));
1097                 }
1098         }
1099         /* print lots more stuff */
1100         if (verbose > 1) {
1101                 if (v3) {
1102                         TCHECK(fap->fa3_ctime);
1103                         printf(" nlink %d rdev %d/%d",
1104                                EXTRACT_32BITS(&fap->fa_nlink),
1105                                EXTRACT_32BITS(&fap->fa3_rdev.specdata1),
1106                                EXTRACT_32BITS(&fap->fa3_rdev.specdata2));
1107                         printf(" fsid ");
1108                         print_int64((u_int32_t *)&fap->fa3_fsid, HEX);
1109                         printf(" fileid ");
1110                         print_int64((u_int32_t *)&fap->fa3_fileid, HEX);
1111                         printf(" a/m/ctime %u.%06u",
1112                                EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec),
1113                                EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec));
1114                         printf(" %u.%06u",
1115                                EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec),
1116                                EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec));
1117                         printf(" %u.%06u",
1118                                EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec),
1119                                EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec));
1120                 } else {
1121                         TCHECK(fap->fa2_ctime);
1122                         printf(" nlink %d rdev %x fsid %x nodeid %x a/m/ctime",
1123                                EXTRACT_32BITS(&fap->fa_nlink),
1124                                EXTRACT_32BITS(&fap->fa2_rdev),
1125                                EXTRACT_32BITS(&fap->fa2_fsid),
1126                                EXTRACT_32BITS(&fap->fa2_fileid));
1127                         printf(" %u.%06u",
1128                                EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec),
1129                                EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec));
1130                         printf(" %u.%06u",
1131                                EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec),
1132                                EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec));
1133                         printf(" %u.%06u",
1134                                EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec),
1135                                EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec));
1136                 }
1137         }
1138         return ((const u_int32_t *)((unsigned char *)dp +
1139                 (v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
1140 trunc:
1141         return (NULL);
1142 }
1143
1144 static int
1145 parseattrstat(const u_int32_t *dp, int verbose, int v3)
1146 {
1147         int er;
1148
1149         dp = parsestatus(dp, &er);
1150         if (dp == NULL)
1151                 return (0);
1152         if (er)
1153                 return (1);
1154
1155         return (parsefattr(dp, verbose, v3) != NULL);
1156 }
1157
1158 static int
1159 parsediropres(const u_int32_t *dp)
1160 {
1161         int er;
1162
1163         if (!(dp = parsestatus(dp, &er)))
1164                 return (0);
1165         if (er)
1166                 return (1);
1167
1168         dp = parsefh(dp, 0);
1169         if (dp == NULL)
1170                 return (0);
1171
1172         return (parsefattr(dp, vflag, 0) != NULL);
1173 }
1174
1175 static int
1176 parselinkres(const u_int32_t *dp, int v3)
1177 {
1178         int er;
1179
1180         dp = parsestatus(dp, &er);
1181         if (dp == NULL)
1182                 return(0);
1183         if (er)
1184                 return(1);
1185         if (v3 && !(dp = parse_post_op_attr(dp, vflag)))
1186                 return (0);
1187         putchar(' ');
1188         return (parsefn(dp) != NULL);
1189 }
1190
1191 static int
1192 parsestatfs(const u_int32_t *dp, int v3)
1193 {
1194         const struct nfs_statfs *sfsp;
1195         int er;
1196
1197         dp = parsestatus(dp, &er);
1198         if (dp == NULL)
1199                 return (0);
1200         if (!v3 && er)
1201                 return (1);
1202
1203         if (qflag)
1204                 return(1);
1205
1206         if (v3) {
1207                 if (vflag)
1208                         printf(" POST:");
1209                 if (!(dp = parse_post_op_attr(dp, vflag)))
1210                         return (0);
1211         }
1212
1213         TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
1214
1215         sfsp = (const struct nfs_statfs *)dp;
1216
1217         if (v3) {
1218                 printf(" tbytes ");
1219                 print_int64((u_int32_t *)&sfsp->sf_tbytes, UNSIGNED);
1220                 printf(" fbytes ");
1221                 print_int64((u_int32_t *)&sfsp->sf_fbytes, UNSIGNED);
1222                 printf(" abytes ");
1223                 print_int64((u_int32_t *)&sfsp->sf_abytes, UNSIGNED);
1224                 if (vflag) {
1225                         printf(" tfiles ");
1226                         print_int64((u_int32_t *)&sfsp->sf_tfiles, UNSIGNED);
1227                         printf(" ffiles ");
1228                         print_int64((u_int32_t *)&sfsp->sf_ffiles, UNSIGNED);
1229                         printf(" afiles ");
1230                         print_int64((u_int32_t *)&sfsp->sf_afiles, UNSIGNED);
1231                         printf(" invar %u",
1232                                EXTRACT_32BITS(&sfsp->sf_invarsec));
1233                 }
1234         } else {
1235                 printf(" tsize %d bsize %d blocks %d bfree %d bavail %d",
1236                         EXTRACT_32BITS(&sfsp->sf_tsize),
1237                         EXTRACT_32BITS(&sfsp->sf_bsize),
1238                         EXTRACT_32BITS(&sfsp->sf_blocks),
1239                         EXTRACT_32BITS(&sfsp->sf_bfree),
1240                         EXTRACT_32BITS(&sfsp->sf_bavail));
1241         }
1242
1243         return (1);
1244 trunc:
1245         return (0);
1246 }
1247
1248 static int
1249 parserddires(const u_int32_t *dp)
1250 {
1251         int er;
1252
1253         dp = parsestatus(dp, &er);
1254         if (dp == NULL)
1255                 return (0);
1256         if (er)
1257                 return (1);
1258         if (qflag)
1259                 return (1);
1260
1261         TCHECK(dp[2]);
1262         printf(" offset %x size %d ",
1263                EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1]));
1264         if (dp[2] != 0)
1265                 printf(" eof");
1266
1267         return (1);
1268 trunc:
1269         return (0);
1270 }
1271
1272 static const u_int32_t *
1273 parse_wcc_attr(const u_int32_t *dp)
1274 {
1275         printf(" sz ");
1276         print_int64(dp, UNSIGNED);
1277         printf(" mtime %u.%06u ctime %u.%06u",
1278                EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]),
1279                EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5]));
1280         return (dp + 6);
1281 }
1282
1283 /*
1284  * Pre operation attributes. Print only if vflag > 1.
1285  */
1286 static const u_int32_t *
1287 parse_pre_op_attr(const u_int32_t *dp, int verbose)
1288 {
1289         TCHECK(dp[0]);
1290         if (!EXTRACT_32BITS(&dp[0]))
1291                 return (dp + 1);
1292         dp++;
1293         TCHECK2(*dp, 24);
1294         if (verbose > 1) {
1295                 return parse_wcc_attr(dp);
1296         } else {
1297                 /* If not verbose enough, just skip over wcc_attr */
1298                 return (dp + 6);
1299         }
1300 trunc:
1301         return (NULL);
1302 }
1303
1304 /*
1305  * Post operation attributes are printed if vflag >= 1
1306  */
1307 static const u_int32_t *
1308 parse_post_op_attr(const u_int32_t *dp, int verbose)
1309 {
1310         TCHECK(dp[0]);
1311         if (!EXTRACT_32BITS(&dp[0]))
1312                 return (dp + 1);
1313         dp++;
1314         if (verbose) {
1315                 return parsefattr(dp, verbose, 1);
1316         } else
1317                 return (dp + (NFSX_V3FATTR / sizeof (u_int32_t)));
1318 trunc:
1319         return (NULL);
1320 }
1321
1322 static const u_int32_t *
1323 parse_wcc_data(const u_int32_t *dp, int verbose)
1324 {
1325         if (verbose > 1)
1326                 printf(" PRE:");
1327         if (!(dp = parse_pre_op_attr(dp, verbose)))
1328                 return (0);
1329
1330         if (verbose)
1331                 printf(" POST:");
1332         return parse_post_op_attr(dp, verbose);
1333 }
1334
1335 static const u_int32_t *
1336 parsecreateopres(const u_int32_t *dp, int verbose)
1337 {
1338         int er;
1339
1340         if (!(dp = parsestatus(dp, &er)))
1341                 return (0);
1342         if (er)
1343                 dp = parse_wcc_data(dp, verbose);
1344         else {
1345                 TCHECK(dp[0]);
1346                 if (!EXTRACT_32BITS(&dp[0]))
1347                         return (dp + 1);
1348                 dp++;
1349                 if (!(dp = parsefh(dp, 1)))
1350                         return (0);
1351                 if (verbose) {
1352                         if (!(dp = parse_post_op_attr(dp, verbose)))
1353                                 return (0);
1354                         if (vflag > 1) {
1355                                 printf(" dir attr:");
1356                                 dp = parse_wcc_data(dp, verbose);
1357                         }
1358                 }
1359         }
1360         return (dp);
1361 trunc:
1362         return (NULL);
1363 }
1364
1365 static int
1366 parsewccres(const u_int32_t *dp, int verbose)
1367 {
1368         int er;
1369
1370         if (!(dp = parsestatus(dp, &er)))
1371                 return (0);
1372         return parse_wcc_data(dp, verbose) != 0;
1373 }
1374
1375 static const u_int32_t *
1376 parsev3rddirres(const u_int32_t *dp, int verbose)
1377 {
1378         int er;
1379
1380         if (!(dp = parsestatus(dp, &er)))
1381                 return (0);
1382         if (vflag)
1383                 printf(" POST:");
1384         if (!(dp = parse_post_op_attr(dp, verbose)))
1385                 return (0);
1386         if (er)
1387                 return dp;
1388         if (vflag) {
1389                 TCHECK(dp[1]);
1390                 printf(" verf %08x%08x", dp[0], dp[1]);
1391                 dp += 2;
1392         }
1393         return dp;
1394 trunc:
1395         return (NULL);
1396 }
1397
1398 static int
1399 parsefsinfo(const u_int32_t *dp)
1400 {
1401         struct nfsv3_fsinfo *sfp;
1402         int er;
1403
1404         if (!(dp = parsestatus(dp, &er)))
1405                 return (0);
1406         if (vflag)
1407                 printf(" POST:");
1408         if (!(dp = parse_post_op_attr(dp, vflag)))
1409                 return (0);
1410         if (er)
1411                 return (1);
1412
1413         sfp = (struct nfsv3_fsinfo *)dp;
1414         TCHECK(*sfp);
1415         printf(" rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u",
1416                EXTRACT_32BITS(&sfp->fs_rtmax),
1417                EXTRACT_32BITS(&sfp->fs_rtpref),
1418                EXTRACT_32BITS(&sfp->fs_wtmax),
1419                EXTRACT_32BITS(&sfp->fs_wtpref),
1420                EXTRACT_32BITS(&sfp->fs_dtpref));
1421         if (vflag) {
1422                 printf(" rtmult %u wtmult %u maxfsz ",
1423                        EXTRACT_32BITS(&sfp->fs_rtmult),
1424                        EXTRACT_32BITS(&sfp->fs_wtmult));
1425                 print_int64((u_int32_t *)&sfp->fs_maxfilesize, UNSIGNED);
1426                 printf(" delta %u.%06u ",
1427                        EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec),
1428                        EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec));
1429         }
1430         return (1);
1431 trunc:
1432         return (0);
1433 }
1434
1435 static int
1436 parsepathconf(const u_int32_t *dp)
1437 {
1438         int er;
1439         struct nfsv3_pathconf *spp;
1440
1441         if (!(dp = parsestatus(dp, &er)))
1442                 return (0);
1443         if (vflag)
1444                 printf(" POST:");
1445         if (!(dp = parse_post_op_attr(dp, vflag)))
1446                 return (0);
1447         if (er)
1448                 return (1);
1449
1450         spp = (struct nfsv3_pathconf *)dp;
1451         TCHECK(*spp);
1452
1453         printf(" linkmax %u namemax %u %s %s %s %s",
1454                EXTRACT_32BITS(&spp->pc_linkmax),
1455                EXTRACT_32BITS(&spp->pc_namemax),
1456                EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "",
1457                EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "",
1458                EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "",
1459                EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : "");
1460         return (1);
1461 trunc:
1462         return (0);
1463 }
1464
1465 static void
1466 interp_reply(const struct rpc_msg *rp, u_int32_t proc, u_int32_t vers, int length)
1467 {
1468         register const u_int32_t *dp;
1469         register int v3;
1470         int er;
1471
1472         v3 = (vers == NFS_VER3);
1473
1474         if (!v3 && proc < NFS_NPROCS)
1475                 proc = nfsv3_procid[proc];
1476
1477         switch (proc) {
1478
1479         case NFSPROC_NOOP:
1480                 printf(" nop");
1481                 return;
1482
1483         case NFSPROC_NULL:
1484                 printf(" null");
1485                 return;
1486
1487         case NFSPROC_GETATTR:
1488                 printf(" getattr");
1489                 dp = parserep(rp, length);
1490                 if (dp != NULL && parseattrstat(dp, !qflag, v3) != 0)
1491                         return;
1492                 break;
1493
1494         case NFSPROC_SETATTR:
1495                 printf(" setattr");
1496                 if (!(dp = parserep(rp, length)))
1497                         return;
1498                 if (v3) {
1499                         if (parsewccres(dp, vflag))
1500                                 return;
1501                 } else {
1502                         if (parseattrstat(dp, !qflag, 0) != 0)
1503                                 return;
1504                 }
1505                 break;
1506
1507         case NFSPROC_LOOKUP:
1508                 printf(" lookup");
1509                 if (!(dp = parserep(rp, length)))
1510                         break;
1511                 if (v3) {
1512                         if (!(dp = parsestatus(dp, &er)))
1513                                 break;
1514                         if (er) {
1515                                 if (vflag > 1) {
1516                                         printf(" post dattr:");
1517                                         dp = parse_post_op_attr(dp, vflag);
1518                                 }
1519                         } else {
1520                                 if (!(dp = parsefh(dp, v3)))
1521                                         break;
1522                                 if ((dp = parse_post_op_attr(dp, vflag)) &&
1523                                     vflag > 1) {
1524                                         printf(" post dattr:");
1525                                         dp = parse_post_op_attr(dp, vflag);
1526                                 }
1527                         }
1528                         if (dp)
1529                                 return;
1530                 } else {
1531                         if (parsediropres(dp) != 0)
1532                                 return;
1533                 }
1534                 break;
1535
1536         case NFSPROC_ACCESS:
1537                 printf(" access");
1538                 if (!(dp = parserep(rp, length)))
1539                         break;
1540                 if (!(dp = parsestatus(dp, &er)))
1541                         break;
1542                 if (vflag)
1543                         printf(" attr:");
1544                 if (!(dp = parse_post_op_attr(dp, vflag)))
1545                         break;
1546                 if (!er)
1547                         printf(" c %04x", EXTRACT_32BITS(&dp[0]));
1548                 return;
1549
1550         case NFSPROC_READLINK:
1551                 printf(" readlink");
1552                 dp = parserep(rp, length);
1553                 if (dp != NULL && parselinkres(dp, v3) != 0)
1554                         return;
1555                 break;
1556
1557         case NFSPROC_READ:
1558                 printf(" read");
1559                 if (!(dp = parserep(rp, length)))
1560                         break;
1561                 if (v3) {
1562                         if (!(dp = parsestatus(dp, &er)))
1563                                 break;
1564                         if (!(dp = parse_post_op_attr(dp, vflag)))
1565                                 break;
1566                         if (er)
1567                                 return;
1568                         if (vflag) {
1569                                 TCHECK(dp[1]);
1570                                 printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1571                                 if (EXTRACT_32BITS(&dp[1]))
1572                                         printf(" EOF");
1573                         }
1574                         return;
1575                 } else {
1576                         if (parseattrstat(dp, vflag, 0) != 0)
1577                                 return;
1578                 }
1579                 break;
1580
1581         case NFSPROC_WRITE:
1582                 printf(" write");
1583                 if (!(dp = parserep(rp, length)))
1584                         break;
1585                 if (v3) {
1586                         if (!(dp = parsestatus(dp, &er)))
1587                                 break;
1588                         if (!(dp = parse_wcc_data(dp, vflag)))
1589                                 break;
1590                         if (er)
1591                                 return;
1592                         if (vflag) {
1593                                 TCHECK(dp[0]);
1594                                 printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1595                                 if (vflag > 1) {
1596                                         TCHECK(dp[1]);
1597                                         printf(" <%s>",
1598                                                 tok2str(nfsv3_writemodes,
1599                                                         NULL, EXTRACT_32BITS(&dp[1])));
1600                                 }
1601                                 return;
1602                         }
1603                 } else {
1604                         if (parseattrstat(dp, vflag, v3) != 0)
1605                                 return;
1606                 }
1607                 break;
1608
1609         case NFSPROC_CREATE:
1610                 printf(" create");
1611                 if (!(dp = parserep(rp, length)))
1612                         break;
1613                 if (v3) {
1614                         if (parsecreateopres(dp, vflag) != 0)
1615                                 return;
1616                 } else {
1617                         if (parsediropres(dp) != 0)
1618                                 return;
1619                 }
1620                 break;
1621
1622         case NFSPROC_MKDIR:
1623                 printf(" mkdir");
1624                 if (!(dp = parserep(rp, length)))
1625                         break;
1626                 if (v3) {
1627                         if (parsecreateopres(dp, vflag) != 0)
1628                                 return;
1629                 } else {
1630                         if (parsediropres(dp) != 0)
1631                                 return;
1632                 }
1633                 break;
1634
1635         case NFSPROC_SYMLINK:
1636                 printf(" symlink");
1637                 if (!(dp = parserep(rp, length)))
1638                         break;
1639                 if (v3) {
1640                         if (parsecreateopres(dp, vflag) != 0)
1641                                 return;
1642                 } else {
1643                         if (parsestatus(dp, &er) != 0)
1644                                 return;
1645                 }
1646                 break;
1647
1648         case NFSPROC_MKNOD:
1649                 printf(" mknod");
1650                 if (!(dp = parserep(rp, length)))
1651                         break;
1652                 if (parsecreateopres(dp, vflag) != 0)
1653                         return;
1654                 break;
1655
1656         case NFSPROC_REMOVE:
1657                 printf(" remove");
1658                 if (!(dp = parserep(rp, length)))
1659                         break;
1660                 if (v3) {
1661                         if (parsewccres(dp, vflag))
1662                                 return;
1663                 } else {
1664                         if (parsestatus(dp, &er) != 0)
1665                                 return;
1666                 }
1667                 break;
1668
1669         case NFSPROC_RMDIR:
1670                 printf(" rmdir");
1671                 if (!(dp = parserep(rp, length)))
1672                         break;
1673                 if (v3) {
1674                         if (parsewccres(dp, vflag))
1675                                 return;
1676                 } else {
1677                         if (parsestatus(dp, &er) != 0)
1678                                 return;
1679                 }
1680                 break;
1681
1682         case NFSPROC_RENAME:
1683                 printf(" rename");
1684                 if (!(dp = parserep(rp, length)))
1685                         break;
1686                 if (v3) {
1687                         if (!(dp = parsestatus(dp, &er)))
1688                                 break;
1689                         if (vflag) {
1690                                 printf(" from:");
1691                                 if (!(dp = parse_wcc_data(dp, vflag)))
1692                                         break;
1693                                 printf(" to:");
1694                                 if (!(dp = parse_wcc_data(dp, vflag)))
1695                                         break;
1696                         }
1697                         return;
1698                 } else {
1699                         if (parsestatus(dp, &er) != 0)
1700                                 return;
1701                 }
1702                 break;
1703
1704         case NFSPROC_LINK:
1705                 printf(" link");
1706                 if (!(dp = parserep(rp, length)))
1707                         break;
1708                 if (v3) {
1709                         if (!(dp = parsestatus(dp, &er)))
1710                                 break;
1711                         if (vflag) {
1712                                 printf(" file POST:");
1713                                 if (!(dp = parse_post_op_attr(dp, vflag)))
1714                                         break;
1715                                 printf(" dir:");
1716                                 if (!(dp = parse_wcc_data(dp, vflag)))
1717                                         break;
1718                                 return;
1719                         }
1720                 } else {
1721                         if (parsestatus(dp, &er) != 0)
1722                                 return;
1723                 }
1724                 break;
1725
1726         case NFSPROC_READDIR:
1727                 printf(" readdir");
1728                 if (!(dp = parserep(rp, length)))
1729                         break;
1730                 if (v3) {
1731                         if (parsev3rddirres(dp, vflag))
1732                                 return;
1733                 } else {
1734                         if (parserddires(dp) != 0)
1735                                 return;
1736                 }
1737                 break;
1738
1739         case NFSPROC_READDIRPLUS:
1740                 printf(" readdirplus");
1741                 if (!(dp = parserep(rp, length)))
1742                         break;
1743                 if (parsev3rddirres(dp, vflag))
1744                         return;
1745                 break;
1746
1747         case NFSPROC_FSSTAT:
1748                 printf(" fsstat");
1749                 dp = parserep(rp, length);
1750                 if (dp != NULL && parsestatfs(dp, v3) != 0)
1751                         return;
1752                 break;
1753
1754         case NFSPROC_FSINFO:
1755                 printf(" fsinfo");
1756                 dp = parserep(rp, length);
1757                 if (dp != NULL && parsefsinfo(dp) != 0)
1758                         return;
1759                 break;
1760
1761         case NFSPROC_PATHCONF:
1762                 printf(" pathconf");
1763                 dp = parserep(rp, length);
1764                 if (dp != NULL && parsepathconf(dp) != 0)
1765                         return;
1766                 break;
1767
1768         case NFSPROC_COMMIT:
1769                 printf(" commit");
1770                 dp = parserep(rp, length);
1771                 if (dp != NULL && parsewccres(dp, vflag) != 0)
1772                         return;
1773                 break;
1774
1775         default:
1776                 printf(" proc-%u", proc);
1777                 return;
1778         }
1779 trunc:
1780         if (!nfserr)
1781                 fputs(" [|nfs]", stdout);
1782 }