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