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