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