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