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