]> git.saurik.com Git - apple/network_cmds.git/blob - tcpdump.tproj/print-nfs.c
9f7b5b2acfb65290039befe380f99a5a633f4c39
[apple/network_cmds.git] / tcpdump.tproj / print-nfs.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996
26 * The Regents of the University of California. All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that: (1) source code distributions
30 * retain the above copyright notice and this paragraph in its entirety, (2)
31 * distributions including binary code include the above copyright notice and
32 * this paragraph in its entirety in the documentation or other materials
33 * provided with the distribution, and (3) all advertising materials mentioning
34 * features or use of this software display the following acknowledgement:
35 * ``This product includes software developed by the University of California,
36 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
37 * the University nor the names of its contributors may be used to endorse
38 * or promote products derived from this software without specific prior
39 * written permission.
40 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
41 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
42 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
43 */
44
45 #ifndef lint
46 static const char rcsid[] =
47 "@(#) $Header: /cvs/Darwin/Commands/NeXT/network_cmds/tcpdump.tproj/print-nfs.c,v 1.1.1.1 1999/05/02 03:58:34 wsanchez Exp $ (LBL)";
48 #endif
49
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <sys/socket.h>
53
54 #if __STDC__
55 struct mbuf;
56 struct rtentry;
57 #endif
58 #include <net/if.h>
59
60 #include <netinet/in.h>
61 #include <netinet/if_ether.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/ip.h>
64 #include <netinet/ip_var.h>
65
66 #include <rpc/rpc.h>
67
68 #include <ctype.h>
69 #include <pcap.h>
70 #include <stdio.h>
71 #include <string.h>
72
73 #include "interface.h"
74 #include "addrtoname.h"
75
76 #include "nfsv2.h"
77 #include "nfsfh.h"
78
79 static void nfs_printfh(const u_int32_t *);
80 static void xid_map_enter(const struct rpc_msg *, const struct ip *);
81 static int32_t xid_map_find(const struct rpc_msg *, const struct ip *);
82 static void interp_reply(const struct rpc_msg *, u_int32_t, u_int);
83
84 static int nfserr; /* true if we error rather than trunc */
85
86 void
87 nfsreply_print(register const u_char *bp, u_int length,
88 register const u_char *bp2)
89 {
90 register const struct rpc_msg *rp;
91 register const struct ip *ip;
92 int32_t proc;
93
94 nfserr = 0; /* assume no error */
95 rp = (const struct rpc_msg *)bp;
96 ip = (const struct ip *)bp2;
97
98 if (!nflag)
99 (void)printf("%s.nfs > %s.%x: reply %s %d",
100 ipaddr_string(&ip->ip_src),
101 ipaddr_string(&ip->ip_dst),
102 (u_int32_t)ntohl(rp->rm_xid),
103 ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
104 "ok":"ERR",
105 length);
106 else
107 (void)printf("%s.%x > %s.%x: reply %s %d",
108 ipaddr_string(&ip->ip_src),
109 NFS_PORT,
110 ipaddr_string(&ip->ip_dst),
111 (u_int32_t)ntohl(rp->rm_xid),
112 ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
113 "ok":"ERR",
114 length);
115
116 proc = xid_map_find(rp, ip);
117 if (proc >= 0)
118 interp_reply(rp, (u_int32_t)proc, length);
119 }
120
121 /*
122 * Return a pointer to the first file handle in the packet.
123 * If the packet was truncated, return 0.
124 */
125 static const u_int32_t *
126 parsereq(register const struct rpc_msg *rp, register u_int length)
127 {
128 register const u_int32_t *dp;
129 register u_int len;
130
131 /*
132 * find the start of the req data (if we captured it)
133 */
134 dp = (u_int32_t *)&rp->rm_call.cb_cred;
135 TCHECK(dp[1]);
136 len = ntohl(dp[1]);
137 if (len < length) {
138 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
139 TCHECK(dp[1]);
140 len = ntohl(dp[1]);
141 if (len < length) {
142 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
143 TCHECK2(dp[0], 0);
144 return (dp);
145 }
146 }
147 trunc:
148 return (NULL);
149 }
150
151 /*
152 * Print out an NFS file handle and return a pointer to following word.
153 * If packet was truncated, return 0.
154 */
155 static const u_int32_t *
156 parsefh(register const u_int32_t *dp)
157 {
158 if (dp + 8 <= (u_int32_t *)snapend) {
159 nfs_printfh(dp);
160 return (dp + 8);
161 }
162 return (NULL);
163 }
164
165 /*
166 * Print out a file name and return pointer to 32-bit word past it.
167 * If packet was truncated, return 0.
168 */
169 static const u_int32_t *
170 parsefn(register const u_int32_t *dp)
171 {
172 register u_int32_t len;
173 register const u_char *cp;
174
175 /* Bail if we don't have the string length */
176 if ((u_char *)dp > snapend - sizeof(*dp))
177 return (NULL);
178
179 /* Fetch string length; convert to host order */
180 len = *dp++;
181 NTOHL(len);
182
183 cp = (u_char *)dp;
184 /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
185 dp += ((len + 3) & ~3) / sizeof(*dp);
186 if ((u_char *)dp > snapend)
187 return (NULL);
188 /* XXX seems like we should be checking the length */
189 putchar('"');
190 (void) fn_printn(cp, len, NULL);
191 putchar('"');
192
193 return (dp);
194 }
195
196 /*
197 * Print out file handle and file name.
198 * Return pointer to 32-bit word past file name.
199 * If packet was truncated (or there was some other error), return 0.
200 */
201 static const u_int32_t *
202 parsefhn(register const u_int32_t *dp)
203 {
204 dp = parsefh(dp);
205 if (dp == NULL)
206 return (NULL);
207 putchar(' ');
208 return (parsefn(dp));
209 }
210
211 void
212 nfsreq_print(register const u_char *bp, u_int length,
213 register const u_char *bp2)
214 {
215 register const struct rpc_msg *rp;
216 register const struct ip *ip;
217 register const u_int32_t *dp;
218
219 nfserr = 0; /* assume no error */
220 rp = (const struct rpc_msg *)bp;
221 ip = (const struct ip *)bp2;
222 if (!nflag)
223 (void)printf("%s.%x > %s.nfs: %d",
224 ipaddr_string(&ip->ip_src),
225 (u_int32_t)ntohl(rp->rm_xid),
226 ipaddr_string(&ip->ip_dst),
227 length);
228 else
229 (void)printf("%s.%x > %s.%x: %d",
230 ipaddr_string(&ip->ip_src),
231 (u_int32_t)ntohl(rp->rm_xid),
232 ipaddr_string(&ip->ip_dst),
233 NFS_PORT,
234 length);
235
236 xid_map_enter(rp, ip); /* record proc number for later on */
237
238 switch (ntohl(rp->rm_call.cb_proc)) {
239 #ifdef NFSPROC_NOOP
240 case NFSPROC_NOOP:
241 printf(" nop");
242 return;
243 #else
244 #define NFSPROC_NOOP -1
245 #endif
246 case NFSPROC_NULL:
247 printf(" null");
248 return;
249
250 case NFSPROC_GETATTR:
251 printf(" getattr");
252 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL)
253 return;
254 break;
255
256 case NFSPROC_SETATTR:
257 printf(" setattr");
258 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL)
259 return;
260 break;
261
262 #if NFSPROC_ROOT != NFSPROC_NOOP
263 case NFSPROC_ROOT:
264 printf(" root");
265 break;
266 #endif
267 case NFSPROC_LOOKUP:
268 printf(" lookup");
269 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL)
270 return;
271 break;
272
273 case NFSPROC_READLINK:
274 printf(" readlink");
275 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL)
276 return;
277 break;
278
279 case NFSPROC_READ:
280 printf(" read");
281 if ((dp = parsereq(rp, length)) != NULL &&
282 (dp = parsefh(dp)) != NULL) {
283 TCHECK2(dp[0], 3 * sizeof(*dp));
284 printf(" %u bytes @ %u",
285 (u_int32_t)ntohl(dp[1]),
286 (u_int32_t)ntohl(dp[0]));
287 return;
288 }
289 break;
290
291 #if NFSPROC_WRITECACHE != NFSPROC_NOOP
292 case NFSPROC_WRITECACHE:
293 printf(" writecache");
294 if ((dp = parsereq(rp, length)) != NULL &&
295 (dp = parsefh(dp)) != NULL) {
296 TCHECK2(dp[0], 4 * sizeof(*dp));
297 printf(" %u (%u) bytes @ %u (%u)",
298 (u_int32_t)ntohl(dp[3]),
299 (u_int32_t)ntohl(dp[2]),
300 (u_int32_t)ntohl(dp[1]),
301 (u_int32_t)ntohl(dp[0]));
302 return;
303 }
304 break;
305 #endif
306 case NFSPROC_WRITE:
307 printf(" write");
308 if ((dp = parsereq(rp, length)) != NULL &&
309 (dp = parsefh(dp)) != NULL) {
310 TCHECK2(dp[0], 4 * sizeof(*dp));
311 printf(" %u (%u) bytes @ %u (%u)",
312 (u_int32_t)ntohl(dp[3]),
313 (u_int32_t)ntohl(dp[2]),
314 (u_int32_t)ntohl(dp[1]),
315 (u_int32_t)ntohl(dp[0]));
316 return;
317 }
318 break;
319
320 case NFSPROC_CREATE:
321 printf(" create");
322 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL)
323 return;
324 break;
325
326 case NFSPROC_REMOVE:
327 printf(" remove");
328 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL)
329 return;
330 break;
331
332 case NFSPROC_RENAME:
333 printf(" rename");
334 if ((dp = parsereq(rp, length)) != NULL &&
335 (dp = parsefhn(dp)) != NULL) {
336 fputs(" ->", stdout);
337 if (parsefhn(dp) != NULL)
338 return;
339 }
340 break;
341
342 case NFSPROC_LINK:
343 printf(" link");
344 if ((dp = parsereq(rp, length)) != NULL &&
345 (dp = parsefh(dp)) != NULL) {
346 fputs(" ->", stdout);
347 if (parsefhn(dp) != NULL)
348 return;
349 }
350 break;
351
352 case NFSPROC_SYMLINK:
353 printf(" symlink");
354 if ((dp = parsereq(rp, length)) != NULL &&
355 (dp = parsefhn(dp)) != NULL) {
356 fputs(" -> ", stdout);
357 if (parsefn(dp) != NULL)
358 return;
359 }
360 break;
361
362 case NFSPROC_MKDIR:
363 printf(" mkdir");
364 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL)
365 return;
366 break;
367
368 case NFSPROC_RMDIR:
369 printf(" rmdir");
370 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL)
371 return;
372 break;
373
374 case NFSPROC_READDIR:
375 printf(" readdir");
376 if ((dp = parsereq(rp, length)) != NULL &&
377 (dp = parsefh(dp)) != NULL) {
378 TCHECK2(dp[0], 2 * sizeof(*dp));
379 /*
380 * Print the offset as signed, since -1 is common,
381 * but offsets > 2^31 aren't.
382 */
383 printf(" %u bytes @ %d",
384 (u_int32_t)ntohl(dp[1]),
385 (u_int32_t)ntohl(dp[0]));
386 return;
387 }
388 break;
389
390 case NFSPROC_STATFS:
391 printf(" statfs");
392 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL)
393 return;
394 break;
395
396 default:
397 printf(" proc-%u", (u_int32_t)ntohl(rp->rm_call.cb_proc));
398 return;
399 }
400 trunc:
401 if (!nfserr)
402 fputs(" [|nfs]", stdout);
403 }
404
405 /*
406 * Print out an NFS file handle.
407 * We assume packet was not truncated before the end of the
408 * file handle pointed to by dp.
409 *
410 * Note: new version (using portable file-handle parser) doesn't produce
411 * generation number. It probably could be made to do that, with some
412 * additional hacking on the parser code.
413 */
414 static void
415 nfs_printfh(register const u_int32_t *dp)
416 {
417 my_fsid fsid;
418 ino_t ino;
419 char *sfsname = NULL;
420
421 Parse_fh((caddr_t *)dp, &fsid, &ino, NULL, &sfsname, 0);
422
423 if (sfsname) {
424 /* file system ID is ASCII, not numeric, for this server OS */
425 static char temp[NFS_FHSIZE+1];
426
427 /* Make sure string is null-terminated */
428 strncpy(temp, sfsname, NFS_FHSIZE);
429 /* Remove trailing spaces */
430 sfsname = strchr(temp, ' ');
431 if (sfsname)
432 *sfsname = 0;
433
434 (void)printf(" fh %s/%u", temp, (u_int32_t)ino);
435 } else {
436 (void)printf(" fh %u,%u/%u",
437 fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor, (u_int32_t)ino);
438 }
439 }
440
441 /*
442 * Maintain a small cache of recent client.XID.server/proc pairs, to allow
443 * us to match up replies with requests and thus to know how to parse
444 * the reply.
445 */
446
447 struct xid_map_entry {
448 u_int32_t xid; /* transaction ID (net order) */
449 struct in_addr client; /* client IP address (net order) */
450 struct in_addr server; /* server IP address (net order) */
451 u_int32_t proc; /* call proc number (host order) */
452 };
453
454 /*
455 * Map entries are kept in an array that we manage as a ring;
456 * new entries are always added at the tail of the ring. Initially,
457 * all the entries are zero and hence don't match anything.
458 */
459
460 #define XIDMAPSIZE 64
461
462 struct xid_map_entry xid_map[XIDMAPSIZE];
463
464 int xid_map_next = 0;
465 int xid_map_hint = 0;
466
467 static void
468 xid_map_enter(const struct rpc_msg *rp, const struct ip *ip)
469 {
470 struct xid_map_entry *xmep;
471
472 xmep = &xid_map[xid_map_next];
473
474 if (++xid_map_next >= XIDMAPSIZE)
475 xid_map_next = 0;
476
477 xmep->xid = rp->rm_xid;
478 xmep->client = ip->ip_src;
479 xmep->server = ip->ip_dst;
480 xmep->proc = ntohl(rp->rm_call.cb_proc);
481 }
482
483 /* Returns NFSPROC_xxx or -1 on failure */
484 static int32_t
485 xid_map_find(const struct rpc_msg *rp, const struct ip *ip)
486 {
487 int i;
488 struct xid_map_entry *xmep;
489 u_int32_t xid = rp->rm_xid;
490 u_int32_t clip = ip->ip_dst.s_addr;
491 u_int32_t sip = ip->ip_src.s_addr;
492
493 /* Start searching from where we last left off */
494 i = xid_map_hint;
495 do {
496 xmep = &xid_map[i];
497 if (xmep->xid == xid && xmep->client.s_addr == clip &&
498 xmep->server.s_addr == sip) {
499 /* match */
500 xid_map_hint = i;
501 return ((int32_t)xmep->proc);
502 }
503 if (++i >= XIDMAPSIZE)
504 i = 0;
505 } while (i != xid_map_hint);
506
507 /* search failed */
508 return (-1);
509 }
510
511 /*
512 * Routines for parsing reply packets
513 */
514
515 /*
516 * Return a pointer to the beginning of the actual results.
517 * If the packet was truncated, return 0.
518 */
519 static const u_int32_t *
520 parserep(register const struct rpc_msg *rp, register u_int length)
521 {
522 register const u_int32_t *dp;
523 u_int len;
524 enum accept_stat astat;
525
526 /*
527 * Portability note:
528 * Here we find the address of the ar_verf credentials.
529 * Originally, this calculation was
530 * dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
531 * On the wire, the rp_acpt field starts immediately after
532 * the (32 bit) rp_stat field. However, rp_acpt (which is a
533 * "struct accepted_reply") contains a "struct opaque_auth",
534 * whose internal representation contains a pointer, so on a
535 * 64-bit machine the compiler inserts 32 bits of padding
536 * before rp->rm_reply.rp_acpt.ar_verf. So, we cannot use
537 * the internal representation to parse the on-the-wire
538 * representation. Instead, we skip past the rp_stat field,
539 * which is an "enum" and so occupies one 32-bit word.
540 */
541 dp = ((const u_int32_t *)&rp->rm_reply) + 1;
542 TCHECK2(dp[0], 1);
543 len = ntohl(dp[1]);
544 if (len >= length)
545 return (NULL);
546 /*
547 * skip past the ar_verf credentials.
548 */
549 dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
550 TCHECK2(dp[0], 0);
551
552 /*
553 * now we can check the ar_stat field
554 */
555 astat = ntohl(*(enum accept_stat *)dp);
556 switch (astat) {
557
558 case SUCCESS:
559 break;
560
561 case PROG_UNAVAIL:
562 printf(" PROG_UNAVAIL");
563 nfserr = 1; /* suppress trunc string */
564 return (NULL);
565
566 case PROG_MISMATCH:
567 printf(" PROG_MISMATCH");
568 nfserr = 1; /* suppress trunc string */
569 return (NULL);
570
571 case PROC_UNAVAIL:
572 printf(" PROC_UNAVAIL");
573 nfserr = 1; /* suppress trunc string */
574 return (NULL);
575
576 case GARBAGE_ARGS:
577 printf(" GARBAGE_ARGS");
578 nfserr = 1; /* suppress trunc string */
579 return (NULL);
580
581 case SYSTEM_ERR:
582 printf(" SYSTEM_ERR");
583 nfserr = 1; /* suppress trunc string */
584 return (NULL);
585
586 default:
587 printf(" ar_stat %d", astat);
588 nfserr = 1; /* suppress trunc string */
589 return (NULL);
590 }
591 /* successful return */
592 if ((sizeof(astat) + ((u_char *)dp)) < snapend)
593 return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
594
595 trunc:
596 return (NULL);
597 }
598
599 static const u_int32_t *
600 parsestatus(const u_int32_t *dp)
601 {
602 register int errnum;
603
604 TCHECK(dp[0]);
605 errnum = ntohl(dp[0]);
606 if (errnum != 0) {
607 if (!qflag)
608 printf(" ERROR: %s", pcap_strerror(errnum));
609 nfserr = 1; /* suppress trunc string */
610 return (NULL);
611 }
612 return (dp + 1);
613 trunc:
614 return (NULL);
615 }
616
617 static struct tok type2str[] = {
618 { NFNON, "NON" },
619 { NFREG, "REG" },
620 { NFDIR, "DIR" },
621 { NFBLK, "BLK" },
622 { NFCHR, "CHR" },
623 { NFLNK, "LNK" },
624 { 0, NULL }
625 };
626
627 static const u_int32_t *
628 parsefattr(const u_int32_t *dp, int verbose)
629 {
630 const struct nfsv2_fattr *fap;
631
632 fap = (const struct nfsv2_fattr *)dp;
633 if (verbose) {
634 TCHECK(fap->fa_nfssize);
635 printf(" %s %o ids %u/%u sz %u ",
636 tok2str(type2str, "unk-ft %d ",
637 (u_int32_t)ntohl(fap->fa_type)),
638 (u_int32_t)ntohl(fap->fa_mode),
639 (u_int32_t)ntohl(fap->fa_uid),
640 (u_int32_t)ntohl(fap->fa_gid),
641 (u_int32_t)ntohl(fap->fa_nfssize));
642 }
643 /* print lots more stuff */
644 if (verbose > 1) {
645 TCHECK(fap->fa_nfsfileid);
646 printf("nlink %u rdev %x fsid %x nodeid %x a/m/ctime ",
647 (u_int32_t)ntohl(fap->fa_nlink),
648 (u_int32_t)ntohl(fap->fa_nfsrdev),
649 (u_int32_t)ntohl(fap->fa_nfsfsid),
650 (u_int32_t)ntohl(fap->fa_nfsfileid));
651 TCHECK(fap->fa_nfsatime);
652 printf("%u.%06u ",
653 (u_int32_t)ntohl(fap->fa_nfsatime.nfs_sec),
654 (u_int32_t)ntohl(fap->fa_nfsatime.nfs_usec));
655 TCHECK(fap->fa_nfsmtime);
656 printf("%u.%06u ",
657 (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_sec),
658 (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_usec));
659 TCHECK(fap->fa_nfsctime);
660 printf("%u.%06u ",
661 (u_int32_t)ntohl(fap->fa_nfsctime.nfs_sec),
662 (u_int32_t)ntohl(fap->fa_nfsctime.nfs_usec));
663 }
664 return ((const u_int32_t *)&fap[1]);
665 trunc:
666 return (NULL);
667 }
668
669 static int
670 parseattrstat(const u_int32_t *dp, int verbose)
671 {
672
673 dp = parsestatus(dp);
674 if (dp == NULL)
675 return (0);
676
677 return (parsefattr(dp, verbose) != NULL);
678 }
679
680 static int
681 parsediropres(const u_int32_t *dp)
682 {
683
684 dp = parsestatus(dp);
685 if (dp == NULL)
686 return (0);
687
688 dp = parsefh(dp);
689 if (dp == NULL)
690 return (0);
691
692 return (parsefattr(dp, vflag) != NULL);
693 }
694
695 static int
696 parselinkres(const u_int32_t *dp)
697 {
698 dp = parsestatus(dp);
699 if (dp == NULL)
700 return (0);
701
702 putchar(' ');
703 return (parsefn(dp) != NULL);
704 }
705
706 static int
707 parsestatfs(const u_int32_t *dp)
708 {
709 const struct nfsv2_statfs *sfsp;
710
711 dp = parsestatus(dp);
712 if (dp == NULL)
713 return (0);
714
715 if (!qflag) {
716 sfsp = (const struct nfsv2_statfs *)dp;
717 TCHECK(sfsp->sf_bavail);
718 printf(" tsize %u bsize %u blocks %u bfree %u bavail %u",
719 (u_int32_t)ntohl(sfsp->sf_tsize),
720 (u_int32_t)ntohl(sfsp->sf_bsize),
721 (u_int32_t)ntohl(sfsp->sf_blocks),
722 (u_int32_t)ntohl(sfsp->sf_bfree),
723 (u_int32_t)ntohl(sfsp->sf_bavail));
724 }
725
726 return (1);
727 trunc:
728 return (0);
729 }
730
731 static int
732 parserddires(const u_int32_t *dp)
733 {
734 dp = parsestatus(dp);
735 if (dp == NULL)
736 return (0);
737 if (!qflag) {
738 TCHECK(dp[0]);
739 printf(" offset %x", (u_int32_t)ntohl(dp[0]));
740 TCHECK(dp[1]);
741 printf(" size %u", (u_int32_t)ntohl(dp[1]));
742 TCHECK(dp[2]);
743 if (dp[2] != 0)
744 printf(" eof");
745 }
746
747 return (1);
748 trunc:
749 return (0);
750 }
751
752 static void
753 interp_reply(const struct rpc_msg *rp, u_int32_t proc, u_int length)
754 {
755 register const u_int32_t *dp;
756
757 switch (proc) {
758
759 #ifdef NFSPROC_NOOP
760 case NFSPROC_NOOP:
761 printf(" nop");
762 return;
763 #else
764 #define NFSPROC_NOOP -1
765 #endif
766 case NFSPROC_NULL:
767 printf(" null");
768 return;
769
770 case NFSPROC_GETATTR:
771 printf(" getattr");
772 dp = parserep(rp, length);
773 if (dp != NULL && parseattrstat(dp, !qflag) != 0)
774 return;
775 break;
776
777 case NFSPROC_SETATTR:
778 printf(" setattr");
779 dp = parserep(rp, length);
780 if (dp != NULL && parseattrstat(dp, !qflag) != 0)
781 return;
782 break;
783
784 #if NFSPROC_ROOT != NFSPROC_NOOP
785 case NFSPROC_ROOT:
786 printf(" root");
787 break;
788 #endif
789 case NFSPROC_LOOKUP:
790 printf(" lookup");
791 dp = parserep(rp, length);
792 if (dp != NULL && parsediropres(dp) != 0)
793 return;
794 break;
795
796 case NFSPROC_READLINK:
797 printf(" readlink");
798 dp = parserep(rp, length);
799 if (dp != NULL && parselinkres(dp) != 0)
800 return;
801 break;
802
803 case NFSPROC_READ:
804 printf(" read");
805 dp = parserep(rp, length);
806 if (dp != NULL && parseattrstat(dp, vflag) != 0)
807 return;
808 break;
809
810 #if NFSPROC_WRITECACHE != NFSPROC_NOOP
811 case NFSPROC_WRITECACHE:
812 printf(" writecache");
813 break;
814 #endif
815 case NFSPROC_WRITE:
816 printf(" write");
817 dp = parserep(rp, length);
818 if (dp != NULL && parseattrstat(dp, vflag) != 0)
819 return;
820 break;
821
822 case NFSPROC_CREATE:
823 printf(" create");
824 dp = parserep(rp, length);
825 if (dp != NULL && parsediropres(dp) != 0)
826 return;
827 break;
828
829 case NFSPROC_REMOVE:
830 printf(" remove");
831 dp = parserep(rp, length);
832 if (dp != NULL && parsestatus(dp) != 0)
833 return;
834 break;
835
836 case NFSPROC_RENAME:
837 printf(" rename");
838 dp = parserep(rp, length);
839 if (dp != NULL && parsestatus(dp) != 0)
840 return;
841 break;
842
843 case NFSPROC_LINK:
844 printf(" link");
845 dp = parserep(rp, length);
846 if (dp != NULL && parsestatus(dp) != 0)
847 return;
848 break;
849
850 case NFSPROC_SYMLINK:
851 printf(" symlink");
852 dp = parserep(rp, length);
853 if (dp != NULL && parsestatus(dp) != 0)
854 return;
855 break;
856
857 case NFSPROC_MKDIR:
858 printf(" mkdir");
859 dp = parserep(rp, length);
860 if (dp != NULL && parsediropres(dp) != 0)
861 return;
862 break;
863
864 case NFSPROC_RMDIR:
865 printf(" rmdir");
866 dp = parserep(rp, length);
867 if (dp != NULL && parsestatus(dp) != 0)
868 return;
869 break;
870
871 case NFSPROC_READDIR:
872 printf(" readdir");
873 dp = parserep(rp, length);
874 if (dp != NULL && parserddires(dp) != 0)
875 return;
876 break;
877
878 case NFSPROC_STATFS:
879 printf(" statfs");
880 dp = parserep(rp, length);
881 if (dp != NULL && parsestatfs(dp) != 0)
882 return;
883 break;
884
885 default:
886 printf(" proc-%u", proc);
887 return;
888 }
889 if (!nfserr)
890 fputs(" [|nfs]", stdout);
891 }