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