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