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