1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27#ident	"%Z%%M%	%I%	%E% SMI"	/* SunOS	*/
28
29#include <sys/types.h>
30#include <sys/errno.h>
31#include <sys/tiuser.h>
32#include <setjmp.h>
33#include <string.h>
34
35#include <rpc/types.h>
36#include <rpc/xdr.h>
37#include <rpc/auth.h>
38#include <rpc/clnt.h>
39#include <rpc/rpc_msg.h>
40#include "snoop.h"
41#include "snoop_nfs.h"
42
43#include <sys/stat.h>
44#include <sys/param.h>
45#include <rpcsvc/nfs_prot.h>
46
47#ifndef MIN
48#define	MIN(a, b)	((a) < (b) ? (a) : (b))
49#endif
50
51extern jmp_buf xdr_err;
52
53static void nfscall3(int);
54static void nfsreply3(int);
55static char *perms(int);
56static char *filetype(int);
57static char *sum_access(void);
58static char *sum_readdirres(void);
59static char *sum_readdirplusres(void);
60static char *sum_createhow(void);
61static char *sum_stablehow(void);
62static void detail_sattr3(void);
63static void detail_diropargs3(void);
64static void detail_readdirres(void);
65static void detail_readdirplusres(void);
66static void detail_fattr3(void);
67static void detail_access(void);
68static void detail_mode(int);
69static void detail_wcc_attr(void);
70static void detail_pre_op_attr(char *);
71static void detail_wcc_data(char *);
72static void skip_postop(void);
73static void skip_wcc_data(void);
74static void skip_sattr3(void);
75
76#define	DONT_CHANGE		0
77#define	SET_TO_SERVER_TIME	1
78#define	SET_TO_CLIENT_TIME	2
79
80#define	UNCHECKED	0
81#define	GUARDED		1
82#define	EXCLUSIVE	2
83
84#define	ACCESS3_READ	0x0001
85#define	ACCESS3_LOOKUP	0x0002
86#define	ACCESS3_MODIFY	0x0004
87#define	ACCESS3_EXTEND	0x0008
88#define	ACCESS3_DELETE	0x0010
89#define	ACCESS3_EXECUTE	0x0020
90
91#define	UNSTABLE	0
92#define	DATA_SYNC	1
93#define	FILE_SYNC	2
94
95#define	NF3REG		1	/* regular file */
96#define	NF3DIR		2	/* directory */
97#define	NF3BLK		3	/* block special */
98#define	NF3CHR		4	/* character special */
99#define	NF3LNK		5	/* symbolic link */
100#define	NF3SOCK		6	/* unix domain socket */
101#define	NF3FIFO		7	/* named pipe */
102
103#define	NFS3_FHSIZE	64
104
105static char *procnames_short[] = {
106	"NULL3",	/*  0 */
107	"GETATTR3",	/*  1 */
108	"SETATTR3",	/*  2 */
109	"LOOKUP3",	/*  3 */
110	"ACCESS3",	/*  4 */
111	"READLINK3",	/*  5 */
112	"READ3",	/*  6 */
113	"WRITE3",	/*  7 */
114	"CREATE3",	/*  8 */
115	"MKDIR3",	/*  9 */
116	"SYMLINK3",	/* 10 */
117	"MKNOD3",	/* 11 */
118	"REMOVE3",	/* 12 */
119	"RMDIR3",	/* 13 */
120	"RENAME3",	/* 14 */
121	"LINK3",	/* 15 */
122	"READDIR3",	/* 16 */
123	"READDIRPLUS3",	/* 17 */
124	"FSSTAT3",	/* 18 */
125	"FSINFO3",	/* 19 */
126	"PATHCONF3",	/* 20 */
127	"COMMIT3",	/* 21 */
128};
129
130static char *procnames_long[] = {
131	"Null procedure",		/*  0 */
132	"Get file attributes",		/*  1 */
133	"Set file attributes",		/*  2 */
134	"Look up file name",		/*  3 */
135	"Check access permission",	/*  4 */
136	"Read from symbolic link",	/*  5 */
137	"Read from file",		/*  6 */
138	"Write to file",		/*  7 */
139	"Create file",			/*  8 */
140	"Make directory",		/*  9 */
141	"Make symbolic link",		/* 10 */
142	"Make special file",		/* 11 */
143	"Remove file",			/* 12 */
144	"Remove directory",		/* 13 */
145	"Rename",			/* 14 */
146	"Link",				/* 15 */
147	"Read from directory",		/* 16 */
148	"Read from directory - plus",	/* 17 */
149	"Get filesystem statistics",	/* 18 */
150	"Get filesystem information",	/* 19 */
151	"Get POSIX information",	/* 20 */
152	"Commit to stable storage",	/* 21 */
153};
154
155#define	MAXPROC	21
156
157void
158interpret_nfs3(flags, type, xid, vers, proc, data, len)
159	int flags, type, xid, vers, proc;
160	char *data;
161	int len;
162{
163	char *line;
164	char buff[NFS_MAXPATHLEN + 1];	/* protocol allows longer */
165	u_longlong_t off;
166	int sz, how;
167	char *fh, *name;
168
169	if (proc < 0 || proc > MAXPROC)
170		return;
171
172	if (flags & F_SUM) {
173		line = get_sum_line();
174
175		if (type == CALL) {
176			(void) sprintf(line, "NFS C %s",
177				procnames_short[proc]);
178			line += strlen(line);
179			switch (proc) {
180			case NFSPROC3_GETATTR:
181			case NFSPROC3_READLINK:
182			case NFSPROC3_FSSTAT:
183			case NFSPROC3_FSINFO:
184			case NFSPROC3_PATHCONF:
185				(void) sprintf(line, sum_nfsfh3());
186				break;
187			case NFSPROC3_SETATTR:
188				(void) sprintf(line, sum_nfsfh3());
189				break;
190			case NFSPROC3_READDIR:
191				fh = sum_nfsfh3();
192				off = getxdr_u_longlong();
193				(void) getxdr_u_longlong();
194				sz = getxdr_u_long();
195				(void) sprintf(line, "%s Cookie=%llu for %lu",
196					fh, off, sz);
197				break;
198			case NFSPROC3_READDIRPLUS:
199				fh = sum_nfsfh3();
200				off = getxdr_u_longlong();
201				(void) getxdr_u_longlong();
202				sz = getxdr_u_long();
203				(void) sprintf(line,
204						"%s Cookie=%llu for %lu/%lu",
205						fh, off, sz, getxdr_u_long());
206				break;
207			case NFSPROC3_ACCESS:
208				fh = sum_nfsfh3();
209				(void) sprintf(line, "%s (%s)",
210					fh, sum_access());
211				break;
212			case NFSPROC3_LOOKUP:
213			case NFSPROC3_REMOVE:
214			case NFSPROC3_RMDIR:
215			case NFSPROC3_MKDIR:
216				fh = sum_nfsfh3();
217				(void) sprintf(line, "%s %s",
218					fh, getxdr_string(buff,
219						NFS_MAXPATHLEN));
220				break;
221			case NFSPROC3_CREATE:
222				fh = sum_nfsfh3();
223				name = getxdr_string(buff, NFS_MAXPATHLEN);
224				(void) sprintf(line, "%s (%s) %s",
225					fh, sum_createhow(), name);
226				break;
227			case NFSPROC3_MKNOD:
228				fh = sum_nfsfh3();
229				name = getxdr_string(buff, NFS_MAXPATHLEN);
230				how = getxdr_long();
231				(void) sprintf(line, "%s (%s) %s",
232					fh, filetype(how), name);
233				break;
234			case NFSPROC3_READ:
235				fh = sum_nfsfh3();
236				off = getxdr_u_longlong();
237				sz = getxdr_u_long();
238				(void) sprintf(line, "%s at %llu for %lu",
239					fh, off, sz);
240				break;
241			case NFSPROC3_WRITE:
242				fh = sum_nfsfh3();
243				off = getxdr_u_longlong();
244				sz = getxdr_u_long();
245				(void) sprintf(line, "%s at %llu for %lu (%s)",
246					fh, off, sz, sum_stablehow());
247				break;
248			case NFSPROC3_SYMLINK:
249				fh = sum_nfsfh3();
250				(void) sprintf(line, "%s %s",
251					fh, getxdr_string(buff,
252						NFS_MAXPATHLEN));
253				skip_sattr3();
254				line += strlen(line);
255				(void) sprintf(line, " to %s",
256					getxdr_string(buff, NFS_MAXPATHLEN));
257				break;
258			case NFSPROC3_RENAME:
259				fh = sum_nfsfh3();
260				(void) sprintf(line, "%s %s",
261					fh, getxdr_string(buff,
262						NFS_MAXPATHLEN));
263				line += strlen(line);
264				fh = sum_nfsfh3();
265				(void) sprintf(line, " to%s %s",
266					fh, getxdr_string(buff,
267						NFS_MAXPATHLEN));
268				break;
269			case NFSPROC3_LINK:
270				fh = sum_nfsfh3();
271				(void) sprintf(line, "%s", fh);
272				line += strlen(line);
273				fh = sum_nfsfh3();
274				(void) sprintf(line, " to%s %s",
275					fh, getxdr_string(buff,
276						NFS_MAXPATHLEN));
277				break;
278			case NFSPROC3_COMMIT:
279				fh = sum_nfsfh3();
280				off = getxdr_u_longlong();
281				sz  = getxdr_u_long();
282				(void) sprintf(line, "%s at %llu for %lu",
283					fh, off, sz);
284				break;
285			default:
286				break;
287			}
288
289			check_retransmit(line, xid);
290		} else {
291			(void) sprintf(line, "NFS R %s ",
292				procnames_short[proc]);
293			line += strlen(line);
294			switch (proc) {
295			case NFSPROC3_LOOKUP:
296				if (sum_nfsstat3(line) == NFS3_OK)
297					(void) strcat(line, sum_nfsfh3());
298				break;
299			case NFSPROC3_CREATE:
300			case NFSPROC3_MKDIR:
301			case NFSPROC3_SYMLINK:
302			case NFSPROC3_MKNOD:
303				if (sum_nfsstat3(line) == NFS3_OK) {
304					if (getxdr_bool())
305						(void) strcat(line,
306							    sum_nfsfh3());
307				}
308				break;
309			case NFSPROC3_READLINK:
310				if (sum_nfsstat3(line) == NFS3_OK) {
311					line += strlen(line);
312					skip_postop();
313					(void) sprintf(line, " (Path=%s)",
314						getxdr_string(buff,
315						    NFS_MAXPATHLEN));
316				}
317				break;
318			case NFSPROC3_GETATTR:
319			case NFSPROC3_SETATTR:
320			case NFSPROC3_REMOVE:
321			case NFSPROC3_RMDIR:
322			case NFSPROC3_RENAME:
323			case NFSPROC3_LINK:
324			case NFSPROC3_FSSTAT:
325			case NFSPROC3_FSINFO:
326			case NFSPROC3_PATHCONF:
327				(void) sum_nfsstat3(line);
328				break;
329			case NFSPROC3_ACCESS:
330				if (sum_nfsstat3(line) == NFS3_OK) {
331					line += strlen(line);
332					skip_postop();
333					(void) sprintf(line, " (%s)",
334						sum_access());
335				}
336				break;
337			case NFSPROC3_WRITE:
338				if (sum_nfsstat3(line) == NFS3_OK) {
339					line += strlen(line);
340					skip_wcc_data();
341					sz = getxdr_u_long();
342					(void) sprintf(line, " %d (%s)",
343						sz, sum_stablehow());
344				}
345				break;
346			case NFSPROC3_READDIR:
347				if (sum_nfsstat3(line) == NFS3_OK)
348					(void) strcat(line, sum_readdirres());
349				break;
350			case NFSPROC3_READ:
351				if (sum_nfsstat3(line) == NFS3_OK) {
352					line += strlen(line);
353					skip_postop();
354					(void) sprintf(line, " (%lu bytes)",
355						getxdr_u_long());
356					if (getxdr_bool())
357						(void) strcat(line, " EOF");
358				}
359				break;
360			case NFSPROC3_READDIRPLUS:
361				if (sum_nfsstat3(line) == NFS3_OK)
362					(void) strcat(line,
363						    sum_readdirplusres());
364				break;
365			case NFSPROC3_COMMIT:
366				(void) sum_nfsstat3(line);
367				break;
368			default:
369				break;
370			}
371		}
372	}
373
374	if (flags & F_DTAIL) {
375		show_header("NFS:  ", "Sun NFS", len);
376		show_space();
377		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
378			proc, procnames_long[proc]);
379		if (type == CALL)
380			nfscall3(proc);
381		else
382			nfsreply3(proc);
383		show_trailer();
384	}
385}
386
387/*
388 *  Print out version 3 NFS call packets
389 */
390static void
391nfscall3(proc)
392	int proc;
393{
394	int h;
395
396	switch (proc) {
397	case NFSPROC3_GETATTR:
398	case NFSPROC3_READLINK:
399	case NFSPROC3_FSINFO:
400	case NFSPROC3_FSSTAT:
401	case NFSPROC3_PATHCONF:
402		detail_nfsfh3();
403		break;
404	case NFSPROC3_SETATTR:
405		detail_nfsfh3();
406		detail_sattr3();
407		if (getxdr_bool())
408			(void) showxdr_date_ns("Guard = %s");
409		break;
410	case NFSPROC3_LOOKUP:
411	case NFSPROC3_REMOVE:
412	case NFSPROC3_RMDIR:
413		detail_diropargs3();
414		break;
415	case NFSPROC3_ACCESS:
416		detail_nfsfh3();
417		detail_access();
418		break;
419	case NFSPROC3_MKDIR:
420		detail_diropargs3();
421		detail_sattr3();
422		break;
423	case NFSPROC3_CREATE:
424		detail_diropargs3();
425		h = getxdr_u_long();
426		if (h == EXCLUSIVE)
427			showxdr_hex(8, "Guard = %s");
428		else {
429			(void) sprintf(get_line(0, 0), "Method = %s",
430			h == UNCHECKED ? "Unchecked" : "Guarded");
431			detail_sattr3();
432		}
433		break;
434	case NFSPROC3_MKNOD:
435		detail_diropargs3();
436		h = getxdr_u_long();
437		(void) sprintf(get_line(0, 0), "File type = %s",
438			filetype(h));
439		switch (h) {
440		case NF3CHR:
441		case NF3BLK:
442			detail_sattr3();
443			showxdr_u_long("Major = %lu");
444			showxdr_u_long("Minor = %lu");
445			break;
446		case NF3SOCK:
447		case NF3FIFO:
448			detail_sattr3();
449			break;
450		}
451		break;
452	case NFSPROC3_WRITE:
453		detail_nfsfh3();
454		(void) showxdr_u_longlong("Offset = %llu");
455		(void) showxdr_u_long("Size   = %lu");
456		(void) sprintf(get_line(0, 0), "Stable = %s",
457				sum_stablehow());
458		break;
459	case NFSPROC3_RENAME:
460		detail_diropargs3();
461		detail_diropargs3();
462		break;
463	case NFSPROC3_LINK:
464		detail_nfsfh3();
465		detail_diropargs3();
466		break;
467	case NFSPROC3_SYMLINK:
468		detail_diropargs3();
469		detail_sattr3();
470		(void) showxdr_string(MAXPATHLEN, "Path = %s");
471		break;
472	case NFSPROC3_READDIR:
473		detail_nfsfh3();
474		(void) showxdr_u_longlong("Cookie   = %llu");
475		(void) showxdr_hex(8, "Verifier = %s");
476		(void) showxdr_u_long("Count = %lu");
477		break;
478	case NFSPROC3_READDIRPLUS:
479		detail_nfsfh3();
480		(void) showxdr_u_longlong("Cookie   = %llu");
481		(void) showxdr_hex(8, "Verifier = %s");
482		(void) showxdr_u_long("Dircount = %lu");
483		(void) showxdr_u_long("Maxcount = %lu");
484		break;
485	case NFSPROC3_READ:
486	case NFSPROC3_COMMIT:
487		detail_nfsfh3();
488		(void) showxdr_u_longlong("Offset = %llu");
489		(void) showxdr_long("Count = %lu");
490		break;
491	default:
492		break;
493	}
494}
495
496/*
497 *  Print out version 3 NFS reply packets
498 */
499static void
500nfsreply3(proc)
501	int proc;
502{
503	int bits;
504
505	switch (proc) {
506	case NFSPROC3_GETATTR:
507		if (detail_nfsstat3() == NFS3_OK) {
508			detail_fattr3();
509		}
510		break;
511	case NFSPROC3_SETATTR:
512		(void) detail_nfsstat3();
513		detail_wcc_data("");
514		break;
515	case NFSPROC3_WRITE:
516		if (detail_nfsstat3() == NFS3_OK) {
517			detail_wcc_data("");
518			(void) showxdr_u_long("Count = %lu bytes written");
519			(void) sprintf(get_line(0, 0), "Stable = %s",
520					sum_stablehow());
521			(void) showxdr_hex(8, "Verifier = %s");
522		} else
523			detail_wcc_data("");
524		break;
525	case NFSPROC3_LOOKUP:
526		if (detail_nfsstat3() == NFS3_OK) {
527			detail_nfsfh3();
528			detail_post_op_attr("(object)");
529		}
530		detail_post_op_attr("(directory)");
531		break;
532	case NFSPROC3_CREATE:
533	case NFSPROC3_MKDIR:
534	case NFSPROC3_SYMLINK:
535	case NFSPROC3_MKNOD:
536		if (detail_nfsstat3() == NFS3_OK) {
537			if (getxdr_bool())
538				detail_nfsfh3();
539			else
540				(void) sprintf(get_line(0, 0),
541						"(No file handle available)");
542			detail_post_op_attr("");
543		}
544		detail_wcc_data("");
545		break;
546	case NFSPROC3_READLINK:
547		if (detail_nfsstat3() == NFS3_OK) {
548			detail_post_op_attr("");
549			(void) showxdr_string(MAXPATHLEN, "Path = %s");
550		} else
551			detail_post_op_attr("");
552		break;
553	case NFSPROC3_READ:
554		if (detail_nfsstat3() == NFS3_OK) {
555			detail_post_op_attr("");
556			(void) showxdr_u_long("Count = %lu bytes read");
557			(void) showxdr_bool("End of file = %s");
558		} else
559			detail_post_op_attr("");
560		break;
561	case NFSPROC3_ACCESS:
562		if (detail_nfsstat3() == NFS3_OK) {
563			detail_post_op_attr("");
564			(void) sprintf(get_line(0, 0), "Access = %s",
565					sum_access());
566		} else
567			detail_post_op_attr("");
568		break;
569	case NFSPROC3_REMOVE:
570	case NFSPROC3_RMDIR:
571		(void) detail_nfsstat3();
572		detail_wcc_data("");
573		break;
574	case NFSPROC3_RENAME:
575		(void) detail_nfsstat3();
576		detail_wcc_data("(from directory)");
577		detail_wcc_data("(to directory)");
578		break;
579	case NFSPROC3_LINK:
580		(void) detail_nfsstat3();
581		detail_post_op_attr("");
582		detail_wcc_data("");
583		break;
584	case NFSPROC3_READDIR:
585		if (detail_nfsstat3() == NFS3_OK) {
586			detail_readdirres();
587		} else
588			detail_post_op_attr("");
589		break;
590	case NFSPROC3_READDIRPLUS:
591		if (detail_nfsstat3() == NFS3_OK) {
592			detail_readdirplusres();
593		} else
594			detail_post_op_attr("");
595		break;
596	case NFSPROC3_FSSTAT:
597		if (detail_nfsstat3() == NFS3_OK) {
598			detail_post_op_attr("");
599			(void) showxdr_u_longlong(
600				"Total space = %llu bytes");
601			(void) showxdr_u_longlong(
602				"Available space = %llu bytes");
603			(void) showxdr_u_longlong(
604				"Available space - this user = %llu bytes");
605			(void) showxdr_u_longlong(
606				"Total file slots = %llu");
607			(void) showxdr_u_longlong(
608				"Available file slots = %llu");
609			(void) showxdr_u_longlong(
610				"Available file slots - this user = %llu");
611			(void) showxdr_u_long("Invariant time = %lu sec");
612		} else
613			detail_post_op_attr("");
614		break;
615	case NFSPROC3_FSINFO:
616		if (detail_nfsstat3() == NFS3_OK) {
617			detail_post_op_attr("");
618			(void) show_line("Read transfer sizes:");
619			(void) showxdr_u_long("   Maximum = %lu bytes");
620			(void) showxdr_u_long("   Preferred = %lu bytes");
621			(void) showxdr_u_long(
622			    "   Suggested multiple = %lu bytes");
623			(void) show_line("Write transfer sizes:");
624			(void) showxdr_u_long("   Maximum = %lu bytes");
625			(void) showxdr_u_long("   Preferred = %lu bytes");
626			(void) showxdr_u_long(
627			    "   Suggested multiple = %lu bytes");
628			(void) show_line("Directory read size:");
629			(void) showxdr_u_long("   Preferred = %lu bytes");
630			(void) show_line("File system limits:");
631			(void) showxdr_u_longlong(
632			    "   Max file size = %llu bytes");
633			(void) showxdr_date_ns(
634			    "   Server minimum time discrimination = %s sec");
635			bits = showxdr_u_long("Properties = 0x%02x");
636			(void) sprintf(get_line(0, 0), "	%s",
637				getflag(bits, FSF3_LINK,
638				"Hard links supported",
639				"(hard links not supported)"));
640			(void) sprintf(get_line(0, 0), "	%s",
641				getflag(bits, FSF3_SYMLINK,
642				"Symbolic links supported",
643				"(symbolic links not supported)"));
644			(void) sprintf(get_line(0, 0), "	%s",
645				getflag(bits, FSF3_HOMOGENEOUS,
646				"Pathconf cannot vary per file",
647				"(pathconf can vary per file)"));
648			(void) sprintf(get_line(0, 0), "	%s",
649				getflag(bits, FSF3_CANSETTIME,
650				"Server can always set file times",
651				"(server cannot always set file times)"));
652		} else
653			detail_post_op_attr("");
654		break;
655	case NFSPROC3_PATHCONF:
656		if (detail_nfsstat3() == NFS3_OK) {
657			detail_post_op_attr("");
658			(void) showxdr_u_long("Link max = %lu");
659			(void) showxdr_u_long("Name max = %lu");
660			(void) showxdr_bool("No trunc         = %s");
661			(void) showxdr_bool("Chown restricted = %s");
662			(void) showxdr_bool("Case insensitive = %s");
663			(void) showxdr_bool("Case preserving  = %s");
664		} else
665			detail_post_op_attr("");
666		break;
667	case NFSPROC3_COMMIT:
668		if (detail_nfsstat3() == NFS3_OK) {
669			detail_wcc_data("");
670			(void) showxdr_hex(8, "Verifier = %s");
671		} else
672			detail_wcc_data("");
673		break;
674	default:
675		break;
676	}
677}
678
679static void
680detail_diropargs3()
681{
682
683	detail_nfsfh3();
684	(void) showxdr_string(MAXPATHLEN, "File name = %s");
685}
686
687int
688sum_nfsstat3(line)
689	char *line;
690{
691	ulong_t status;
692	char *p;
693
694	status = getxdr_long();
695	switch (status) {
696	case NFS3_OK:		p = "OK"; break;
697	case NFS3ERR_PERM:	p = "Not owner"; break;
698	case NFS3ERR_NOENT:	p = "No such file or directory"; break;
699	case NFS3ERR_IO:	p = "I/O error"; break;
700	case NFS3ERR_NXIO:	p = "No such device or address"; break;
701	case NFS3ERR_ACCES:	p = "Permission denied"; break;
702	case NFS3ERR_EXIST:	p = "File exists"; break;
703	case NFS3ERR_XDEV:	p = "Attempted cross-device link"; break;
704	case NFS3ERR_NODEV:	p = "No such device"; break;
705	case NFS3ERR_NOTDIR:	p = "Not a directory"; break;
706	case NFS3ERR_ISDIR:	p = "Is a directory"; break;
707	case NFS3ERR_INVAL:	p = "Invalid argument"; break;
708	case NFS3ERR_FBIG:	p = "File too large"; break;
709	case NFS3ERR_NOSPC:	p = "No space left on device"; break;
710	case NFS3ERR_ROFS:	p = "Read-only file system"; break;
711	case NFS3ERR_MLINK:	p = "Too many links"; break;
712	case NFS3ERR_NAMETOOLONG:p = "File name too long"; break;
713	case NFS3ERR_NOTEMPTY:	p = "Directory not empty"; break;
714	case NFS3ERR_DQUOT:	p = "Disc quota exceeded"; break;
715	case NFS3ERR_STALE:	p = "Stale NFS file handle"; break;
716	case NFS3ERR_REMOTE:	p = "Too many levels of remote in path"; break;
717	case NFS3ERR_BADHANDLE:	p = "Illegal NFS file handle"; break;
718	case NFS3ERR_NOT_SYNC:	p = "Update synch mismatch"; break;
719	case NFS3ERR_BAD_COOKIE:p = "Readdir cookie is stale"; break;
720	case NFS3ERR_NOTSUPP:	p = "Operation not supported"; break;
721	case NFS3ERR_TOOSMALL:	p = "Buffer/request too small"; break;
722	case NFS3ERR_SERVERFAULT:p = "Server fault"; break;
723	case NFS3ERR_BADTYPE:	p = "Bad type"; break;
724	case NFS3ERR_JUKEBOX:	p = "File is temporarily unavailable"; break;
725	default:		p = "(unknown error)"; break;
726	}
727
728	(void) strcpy(line, p);
729	return (status);
730}
731
732int
733detail_nfsstat3()
734{
735	ulong_t status;
736	char buff[64];
737	int pos;
738
739	pos = getxdr_pos();
740	status = sum_nfsstat3(buff);
741
742	(void) sprintf(get_line(pos, getxdr_pos()), "Status = %d (%s)",
743		status, buff);
744
745	return ((int)status);
746}
747
748static void
749skip_postop()
750{
751
752	if (getxdr_bool())
753		xdr_skip(21 * 4);	/* XDR size of fattr3 */
754}
755
756static void
757skip_wcc_data()
758{
759
760	if (getxdr_bool() > 0)
761		xdr_skip(3 * 8);
762	skip_postop();
763}
764
765static void
766skip_sattr3()
767{
768
769	if (getxdr_bool() > 0)
770		xdr_skip(4);		/* mode */
771	if (getxdr_bool() > 0)
772		xdr_skip(4);		/* uid */
773	if (getxdr_bool() > 0)
774		xdr_skip(4);		/* gid */
775	if (getxdr_bool() > 0)
776		xdr_skip(8);		/* size */
777	if (getxdr_bool() > 0)
778		xdr_skip(8);		/* atime */
779	if (getxdr_bool() > 0)
780		xdr_skip(8);		/* mtime */
781}
782
783char *
784sum_nfsfh3()
785{
786	int len;
787	int fh;
788	static char buff[16];
789
790	len = getxdr_long();
791	fh = sum_filehandle(len);
792	(void) sprintf(buff, " FH=%04X", fh & 0xFFFF);
793	return (buff);
794}
795
796void
797detail_nfsfh3()
798{
799	int pos;
800	int i, l, len;
801	int fh;
802
803	len = getxdr_long();
804	pos = getxdr_pos();
805	fh = sum_filehandle(len);
806	setxdr_pos(pos);
807	(void) sprintf(get_line(0, 0), "File handle = [%04X]", fh & 0xFFFF);
808	i = 0;
809	while (i < len) {
810		l = MIN(len - i, 32);
811		(void) showxdr_hex(l, " %s");
812		i += l;
813	}
814}
815
816static char *
817sum_access()
818{
819	int bits;
820	static char buff[64];
821
822	bits = getxdr_u_long();
823	buff[0] = '\0';
824
825	if (bits & ACCESS3_READ)
826		(void) strcat(buff, "read,");
827	if (bits & ACCESS3_LOOKUP)
828		(void) strcat(buff, "lookup,");
829	if (bits & ACCESS3_MODIFY)
830		(void) strcat(buff, "modify,");
831	if (bits & ACCESS3_EXTEND)
832		(void) strcat(buff, "extend,");
833	if (bits & ACCESS3_DELETE)
834		(void) strcat(buff, "delete,");
835	if (bits & ACCESS3_EXECUTE)
836		(void) strcat(buff, "execute,");
837	if (buff[0] != '\0')
838		buff[strlen(buff) - 1] = '\0';
839
840	return (buff);
841}
842
843static void
844detail_access()
845{
846	uint_t bits;
847
848	bits = showxdr_u_long("Access bits = 0x%08x");
849	(void) sprintf(get_line(0, 0), "	%s",
850		getflag(bits, ACCESS3_READ, "Read", "(no read)"));
851	(void) sprintf(get_line(0, 0), "	%s",
852		getflag(bits, ACCESS3_LOOKUP, "Lookup", "(no lookup)"));
853	(void) sprintf(get_line(0, 0), "	%s",
854		getflag(bits, ACCESS3_MODIFY, "Modify", "(no modify)"));
855	(void) sprintf(get_line(0, 0), "	%s",
856		getflag(bits, ACCESS3_EXTEND, "Extend", "(no extend)"));
857	(void) sprintf(get_line(0, 0), "	%s",
858		getflag(bits, ACCESS3_DELETE, "Delete", "(no delete)"));
859	(void) sprintf(get_line(0, 0), "	%s",
860		getflag(bits, ACCESS3_EXECUTE, "Execute", "(no execute)"));
861}
862
863static void
864detail_mode(mode)
865	int mode;
866{
867
868	(void) sprintf(get_line(0, 0), "  Mode = 0%o", mode);
869	(void) sprintf(get_line(0, 0),
870		"   Setuid = %d, Setgid = %d, Sticky = %d",
871		(mode & S_ISUID) != 0,
872		(mode & S_ISGID) != 0,
873		(mode & S_ISVTX) != 0);
874	(void) sprintf(get_line(0, 0), "   Owner's permissions = %s",
875		perms(mode >> 6 & 0x7));
876	(void) sprintf(get_line(0, 0), "   Group's permissions = %s",
877		perms(mode >> 3 & 0x7));
878	(void) sprintf(get_line(0, 0), "   Other's permissions = %s",
879		perms(mode & 0x7));
880}
881
882static void
883detail_fattr3()
884{
885	uint_t fltype, mode, nlinks, uid, gid;
886	uint_t major, minor;
887	u_longlong_t size, used, fsid, fileid;
888
889	fltype  = getxdr_u_long();
890	mode	= getxdr_u_long();
891	nlinks	= getxdr_u_long();
892	uid	= getxdr_u_long();
893	gid	= getxdr_u_long();
894	size	= getxdr_u_longlong();
895	used 	= getxdr_u_longlong();
896	major	= getxdr_u_long();
897	minor	= getxdr_u_long();
898	fsid	= getxdr_u_longlong();
899	fileid	= getxdr_u_longlong();
900
901	(void) sprintf(get_line(0, 0),
902		"  File type = %d (%s)",
903		fltype, filetype(fltype));
904	detail_mode(mode);
905	(void) sprintf(get_line(0, 0),
906		"  Link count = %u, User ID = %u, Group ID = %u",
907		nlinks, uid, gid);
908	(void) sprintf(get_line(0, 0),
909		"  File size = %llu, Used = %llu",
910		size, used);
911	(void) sprintf(get_line(0, 0),
912		"  Special: Major = %u, Minor = %u",
913		major, minor);
914	(void) sprintf(get_line(0, 0),
915		"  File system id = %llu, File id = %llu",
916		fsid, fileid);
917	(void) showxdr_date_ns("  Last access time      = %s");
918	(void) showxdr_date_ns("  Modification time     = %s");
919	(void) showxdr_date_ns("  Attribute change time = %s");
920	(void) show_line("");
921}
922
923static void
924detail_sattr3()
925{
926	int t;
927
928	if (getxdr_bool())
929		detail_mode(getxdr_u_long());
930	else
931		(void) sprintf(get_line(0, 0), "Mode = (not set)");
932	if (getxdr_bool())
933		(void) showxdr_long("User ID = %d");
934	else
935		(void) sprintf(get_line(0, 0), "User ID = (not set)");
936	if (getxdr_bool())
937		(void) showxdr_long("Group ID = %d");
938	else
939		(void) sprintf(get_line(0, 0), "Group ID = (not set)");
940	if (getxdr_bool())
941		(void) showxdr_u_longlong("Size = %llu");
942	else
943		(void) sprintf(get_line(0, 0), "Size = (not set)");
944
945	if ((t = getxdr_u_long()) == SET_TO_CLIENT_TIME)
946		(void) showxdr_date("Access time = %s (set to client time)");
947	else if (t == SET_TO_SERVER_TIME)
948		(void) sprintf(get_line(0, 0),
949				"Access time = (set to server time)");
950	else
951		(void) sprintf(get_line(0, 0), "Access time = (do not set)");
952
953	if ((t = getxdr_u_long()) == SET_TO_CLIENT_TIME) {
954		(void) showxdr_date(
955				"Modification time = %s (set to client time)");
956	} else if (t == SET_TO_SERVER_TIME)
957		(void) sprintf(get_line(0, 0),
958				"Modification time = (set to server time)");
959	else
960		(void) sprintf(get_line(0, 0),
961				"Modification time = (do not set)");
962	(void) show_line("");
963}
964
965static char *
966filetype(n)
967	int n;
968{
969
970	switch (n) {
971	case NF3REG:
972		return ("Regular File");
973	case NF3DIR:
974		return ("Directory");
975	case NF3BLK:
976		return ("Block special");
977	case NF3CHR:
978		return ("Character special");
979	case NF3LNK:
980		return ("Symbolic Link");
981	case NF3SOCK:
982		return ("Unix domain socket");
983	case NF3FIFO:
984		return ("Named pipe");
985	default:
986		return ("?");
987	}
988	/* NOTREACHED */
989}
990
991static char *
992perms(n)
993	int n;
994{
995	static char buff[4];
996
997	buff[0] = n & 4 ? 'r' : '-';
998	buff[1] = n & 2 ? 'w' : '-';
999	buff[2] = n & 1 ? 'x' : '-';
1000	buff[3] = '\0';
1001	return (buff);
1002}
1003
1004static void
1005detail_wcc_attr()
1006{
1007
1008	(void) showxdr_u_longlong("  Size = %llu bytes");
1009	(void) showxdr_date_ns("  Modification time      = %s");
1010	(void) showxdr_date_ns("  Attribute change time  = %s");
1011	(void) show_line("");
1012}
1013
1014static void
1015detail_pre_op_attr(str)
1016	char *str;
1017{
1018
1019	if (getxdr_bool()) {
1020		(void) sprintf(get_line(0, 0),
1021			"Pre-operation attributes: %s", str);
1022		detail_wcc_attr();
1023	} else
1024		(void) sprintf(get_line(0, 0),
1025			"Pre-operation attributes: %s (not available)", str);
1026}
1027
1028void
1029detail_post_op_attr(str)
1030	char *str;
1031{
1032
1033	if (getxdr_bool()) {
1034		(void) sprintf(get_line(0, 0),
1035			"Post-operation attributes: %s", str);
1036		detail_fattr3();
1037	} else
1038		(void) sprintf(get_line(0, 0),
1039			"Post-operation attributes: %s (not available)", str);
1040}
1041
1042static void
1043detail_wcc_data(str)
1044	char *str;
1045{
1046
1047	detail_pre_op_attr(str);
1048	detail_post_op_attr(str);
1049}
1050
1051static char *
1052sum_readdirres()
1053{
1054	static char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer names */
1055	static int entries;
1056
1057	entries = 0;
1058	if (setjmp(xdr_err)) {
1059		(void) sprintf(buff, " %d+ entries (incomplete)", entries);
1060		return (buff);
1061	}
1062	skip_postop();
1063	xdr_skip(8);	/* cookieverf */
1064	while (getxdr_bool()) {
1065		entries++;
1066		xdr_skip(8);				/* fileid */
1067		(void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
1068		xdr_skip(8);				/* cookie */
1069	}
1070
1071	(void) sprintf(buff, " %d entries (%s)",
1072		entries, getxdr_bool() ? "No more" : "More");
1073	return (buff);
1074}
1075
1076static char *
1077sum_readdirplusres()
1078{
1079	static char buff[NFS_MAXNAMLEN + 1]; /* protocol allows longer */
1080	static int entries;
1081	int skip;
1082
1083	entries = 0;
1084	if (setjmp(xdr_err)) {
1085		(void) sprintf(buff, " %d+ entries (incomplete)", entries);
1086		return (buff);
1087	}
1088	skip_postop();
1089	xdr_skip(8);	/* cookieverf */
1090	while (getxdr_bool()) {
1091		entries++;
1092		xdr_skip(8);				/* fileid */
1093		(void) getxdr_string(buff, NFS_MAXNAMLEN); /* name */
1094		xdr_skip(8);				/* cookie */
1095		skip_postop();				/* post-op */
1096		if (getxdr_bool()) {
1097			skip = getxdr_long();
1098			xdr_skip(RNDUP(skip));		/* fhandle */
1099		}
1100	}
1101
1102	(void) sprintf(buff, " %d entries (%s)",
1103		entries, getxdr_bool() ? "No more" : "More");
1104	return (buff);
1105}
1106
1107static void
1108detail_readdirres()
1109{
1110	static int entries;
1111	u_longlong_t fileid, cookie;
1112	char *name;
1113	char buff[NFS_MAXNAMLEN + 1];	/* protocol allows longer names */
1114
1115	entries = 0;
1116	detail_post_op_attr("");
1117	(void) showxdr_hex(8, "Cookie verifier = %s");
1118	(void) show_line("");
1119	(void) sprintf(get_line(0, 0), "   File id    Cookie   Name");
1120
1121	if (setjmp(xdr_err)) {
1122		(void) sprintf(get_line(0, 0),
1123			"  %d+ entries. (Frame is incomplete)",
1124			entries);
1125		return;
1126	}
1127	while (getxdr_bool()) {
1128		entries++;
1129		fileid = getxdr_u_longlong();
1130		name = (char *)getxdr_string(buff, NFS_MAXNAMLEN);
1131		cookie = getxdr_u_longlong();
1132		(void) sprintf(get_line(0, 0),
1133			" %10llu %10llu %s",
1134			fileid, cookie, name);
1135	}
1136
1137	(void) sprintf(get_line(0, 0), "  %d entries", entries);
1138	(void) showxdr_bool("EOF = %s");
1139}
1140
1141static void
1142detail_readdirplusres()
1143{
1144	static int entries;
1145
1146	entries = 0;
1147	detail_post_op_attr("");
1148	(void) showxdr_hex(8, "Cookie verifier = %s");
1149	(void) show_line("");
1150
1151	if (setjmp(xdr_err)) {
1152		(void) sprintf(get_line(0, 0),
1153			"  %d+ entries. (Frame is incomplete)",
1154			entries);
1155		return;
1156	}
1157	while (getxdr_bool()) {
1158		entries++;
1159		(void) sprintf(get_line(0, 0),
1160			"------------------ entry #%d",
1161			entries);
1162		(void) showxdr_u_longlong("File ID = %llu");
1163		(void) showxdr_string(NFS_MAXNAMLEN, "Name = %s");
1164		(void) showxdr_u_longlong("Cookie = %llu");
1165		detail_post_op_attr("");
1166		if (getxdr_bool())
1167			detail_nfsfh3();
1168		else
1169			(void) sprintf(get_line(0, 0),
1170					"(No file handle available)");
1171	}
1172
1173	(void) show_line("");
1174	(void) sprintf(get_line(0, 0), "  %d entries", entries);
1175	(void) showxdr_bool("EOF = %s");
1176}
1177
1178static char *
1179sum_createhow()
1180{
1181	long how;
1182
1183	how = getxdr_long();
1184	switch (how) {
1185	case UNCHECKED:
1186		return ("UNCHECKED");
1187	case GUARDED:
1188		return ("GUARDED");
1189	case EXCLUSIVE:
1190		return ("EXCLUSIVE");
1191	default:
1192		return ("?");
1193	}
1194	/* NOTREACHED */
1195}
1196
1197static char *
1198sum_stablehow()
1199{
1200	long stable;
1201
1202	stable = getxdr_long();
1203	switch (stable) {
1204	case UNSTABLE:
1205		return ("ASYNC");
1206	case DATA_SYNC:
1207		return ("DSYNC");
1208	case FILE_SYNC:
1209		return ("FSYNC");
1210	default:
1211		return ("?");
1212	}
1213	/* NOTREACHED */
1214}
1215