xref: /freebsd/contrib/tcpdump/print-nfs.c (revision 6780ab54325a71e7e70112b11657973edde8655e)
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 }
1737