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