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