OSDN Git Service

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