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