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