xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #include <ctype.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/errno.h>
37 #include <sys/tiuser.h>
38 #include <setjmp.h>
39 
40 #include <rpc/types.h>
41 #include <rpc/xdr.h>
42 #include <rpc/auth.h>
43 #include <rpc/clnt.h>
44 #include <rpc/rpc_msg.h>
45 #include "snoop.h"
46 
47 #include <sys/stat.h>
48 #include <sys/param.h>
49 #include <rpcsvc/nfs_prot.h>
50 /* use the same nfs4_prot.h as the xdr code */
51 #include "rpcsvc/nfs4_prot.h"
52 
53 /*
54  * XXX With NFS v2 and v3, we only need to xdr the pieces that we care
55  * about.  Anything else we can ignore and just skip to the next packet.
56  * So all the stuff that deals directly with XDR lives in snoop_display.c
57  * With v4, we need to XDR entire structures so that we can skip over
58  * uninteresting bits in a compound array, so we call XDR directly from
59  * here.  We need to rethink how we're going to structure XDR access.  Do
60  * we continue to hide it all in snoop_display.c, or do we expose it to all
61  * the protocol modules?
62  */
63 extern XDR xdrm;
64 
65 #ifndef MIN
66 #define	MIN(a, b)	((a) < (b) ? (a) : (b))
67 #endif
68 
69 /*
70  * Maximum number of characters to display in compound4 summary line.
71  */
72 #define	SUM_COMPND_MAX	100
73 
74 /*
75  * Maximum number of recognized attributes.
76  */
77 #define	MAX_ATTRIBUTES	56
78 
79 /*
80  * This data structure provides a more convenient way to access an
81  * attribute bitmask.  map[N] = value of bit N in a bitmap4.
82  * It's defined as a struct so as to step around all the weird rules in C
83  * about arrays, pointers, passing them as arguments, etc.
84  */
85 
86 typedef struct {
87 	char map[MAX_ATTRIBUTES];
88 } unpkd_attrmap_t;
89 
90 
91 static void sumarg_cb_getattr(char *buf, size_t buflen, void *obj);
92 static void dtlarg_cb_getattr(void *obj);
93 static void sumarg_cb_recall(char *buf, size_t buflen, void *obj);
94 static void dtlarg_cb_recall(void *obj);
95 
96 
97 static void sumarg_access(char *buf, size_t buflen, void *obj);
98 static void dtlarg_access(void *obj);
99 static void sumarg_close(char *buf, size_t buflen, void *obj);
100 static void dtlarg_close(void *obj);
101 static void sumarg_commit(char *buf, size_t buflen, void *obj);
102 static void dtlarg_commit(void *obj);
103 static void sumarg_compnt(char *buf, size_t buflen, void *obj);
104 static void dtlarg_compnt(void *obj);
105 static void sumarg_create(char *buf, size_t buflen, void *obj);
106 static void dtlarg_create(void *obj);
107 static void sumarg_delprge(char *buf, size_t buflen, void *obj);
108 static void dtlarg_delprge(void *obj);
109 static void sumarg_delret(char *buf, size_t buflen, void *obj);
110 static void dtlarg_delret(void *obj);
111 static void sumarg_getattr(char *buf, size_t buflen, void *obj);
112 static void dtlarg_getattr(void *obj);
113 static void sumarg_link(char *buf, size_t buflen, void *obj);
114 static void dtlarg_link(void *obj);
115 static void sum_open_to_lock_owner(char *buf, int buflen,
116 					open_to_lock_owner4 *own);
117 static void sum_exist_lock_owner(char *buf, int buflen,
118 					exist_lock_owner4 *own);
119 static void sum_locker(char *buf, size_t buflen, locker4 *lk);
120 static void sumarg_lock(char *buf, size_t buflen, void *obj);
121 static void detail_open_to_lock_owner(open_to_lock_owner4 *own);
122 static void detail_exist_lock_owner(exist_lock_owner4 *own);
123 static void detail_locker(locker4 *lk);
124 static void dtlarg_lock(void *obj);
125 static void sumarg_lockt(char *buf, size_t buflen, void *obj);
126 static void dtlarg_lockt(void *obj);
127 static void sumarg_locku(char *buf, size_t buflen, void *obj);
128 static void dtlarg_locku(void *obj);
129 static void sumarg_lookup(char *buf, size_t buflen, void *obj);
130 static void dtlarg_lookup(void *obj);
131 static void sumarg_open(char *buf, size_t buflen, void *obj);
132 static void dtlarg_open(void *obj);
133 static void sumarg_openattr(char *buf, size_t buflen, void *obj);
134 static void dtlarg_openattr(void *obj);
135 static void sumarg_open_confirm(char *buf, size_t buflen, void *obj);
136 static void dtlarg_open_confirm(void *obj);
137 static void sumarg_open_downgrd(char *buf, size_t buflen, void *obj);
138 static void dtlarg_open_downgrd(void *obj);
139 static void sumarg_putfh(char *buf, size_t buflen, void *obj);
140 static void dtlarg_putfh(void *obj);
141 static void sumarg_read(char *buf, size_t buflen, void *obj);
142 static void dtlarg_read(void *obj);
143 static void sumarg_readdir(char *buf, size_t buflen, void *obj);
144 static void dtlarg_readdir(void *obj);
145 static void sumarg_release_lkown(char *buf, size_t buflen, void *obj);
146 static void dtlarg_release_lkown(void *obj);
147 static void sumarg_rename(char *buf, size_t buflen, void *obj);
148 static void dtlarg_rename(void *obj);
149 static void sumarg_renew(char *buf, size_t buflen, void *obj);
150 static void dtlarg_renew(void *buf);
151 static void sumarg_secinfo(char *buf, size_t buflen, void *obj);
152 static void dtlarg_secinfo(void *obj);
153 static void sumarg_setattr(char *buf, size_t buflen, void *obj);
154 static void dtlarg_setattr(void *obj);
155 static void sumarg_setclid(char *buf, size_t buflen, void *obj);
156 static void dtlarg_setclid(void *obj);
157 static void sumarg_setclid_cfm(char *buf, size_t buflen, void *obj);
158 static void dtlarg_setclid_cfm(void *obj);
159 static void dtlarg_verify(void *obj);
160 static void sumarg_write(char *buf, size_t buflen, void *obj);
161 static void dtlarg_write(void *obj);
162 
163 static void sumres_cb_getattr(char *buf, size_t buflen, void *obj);
164 static void dtlres_cb_getattr(void *obj);
165 
166 static void sumres_access(char *buf, size_t buflen, void *obj);
167 static void dtlres_access(void *obj);
168 static void sumres_close(char *buf, size_t buflen, void *obj);
169 static void dtlres_close(void *obj);
170 static void sumres_commit(char *buf, size_t buflen, void *obj);
171 static void dtlres_commit(void *obj);
172 static void dtlres_create(void *obj);
173 static void sumres_getattr(char *buf, size_t buflen, void *obj);
174 static void dtlres_getattr(void *obj);
175 static void sumres_getfh(char *buf, size_t buflen, void *obj);
176 static void dtlres_getfh(void *obj);
177 static void dtlres_link(void *obj);
178 static void sumres_lock(char *buf, size_t buflen, void *obj);
179 static void dtlres_lock(void *obj);
180 static void sumres_lockt(char *buf, size_t buflen, void *obj);
181 static void dtlres_lockt(void *obj);
182 static void sumres_locku(char *buf, size_t buflen, void *obj);
183 static void dtlres_locku(void *obj);
184 static void sumres_open(char *buf, size_t buflen, void *obj);
185 static void dtlres_open(void *obj);
186 static void sumres_open_confirm(char *buf, size_t buflen, void *obj);
187 static void dtlres_open_confirm(void *obj);
188 static void sumres_open_downgrd(char *buf, size_t buflen, void *obj);
189 static void dtlres_open_downgrd(void *obj);
190 static void sumres_read(char *buf, size_t buflen, void *obj);
191 static void dtlres_read(void *obj);
192 static void sumres_readdir(char *buf, size_t buflen, void *obj);
193 static void dtlres_readdir(void *obj);
194 static void sumres_readlnk(char *buf, size_t buflen, void *obj);
195 static void dtlres_readlnk(void *obj);
196 static void dtlres_remove(void *obj);
197 static void dtlres_rename(void *obj);
198 static void sumres_secinfo(char *buf, size_t buflen, void *obj);
199 static void dtlres_secinfo(void *obj);
200 static void sumres_setattr(char *buf, size_t buflen, void *obj);
201 static void dtlres_setattr(void *obj);
202 static void sumres_setclid(char *buf, size_t buflen, void *obj);
203 static void dtlres_setclid(void *obj);
204 static void sumres_write(char *buf, size_t buflen, void *obj);
205 static void dtlres_write(void *obj);
206 static void sum_nfsstat4(char *buf, size_t buflen, void *obj);
207 static void dtl_nfsstat4(void *obj);
208 static uint32_t adler16(void *, int);
209 static void nfs4_xdr_skip(int nbytes);
210 static char *sum_lock_type_name(enum nfs_lock_type4 type);
211 
212 int nfs4_pkt_start;
213 int nfs4_pkt_len;
214 int nfs4_skip_bytes;
215 int nfs4_fragged_rpc;
216 char *nfs4err_fragrpc = "<Fragmented RPC>";
217 char *nfs4err_xdrfrag = "<XDR Error or Fragmented RPC>";
218 
219 /*
220  * need a way to enable this if current testcases are parsing snoop
221  * error text. -- maybe an env var would do as temp workaround until
222  * testcases changed to grep for new error text.
223  */
224 int nfs4_use_old_error_text = 0;
225 
226 /*
227  * Information about each operation that can appear in a compound call.
228  * The function pointers are to formatting functions for summary arguments
229  * and results, and detail arguments & results.
230  */
231 
232 typedef struct {
233 	char	*name;
234 	void	(*sumarg)(char *, size_t, void *);
235 	void	(*sumres)(char *, size_t, void *);
236 	void	(*dtlarg)(void *);
237 	void	(*dtlres)(void *);
238 } op_info_t;
239 
240 static op_info_t cb_opcode_info[] = {
241 	{"OP_ZERO",	NULL,	NULL,	NULL,	NULL},	/* 0 */
242 	{"OP_ONE",	NULL,	NULL,	NULL,	NULL},
243 	{"OP_TWO",	NULL,	NULL,	NULL,	NULL},  /* minor vers */
244 	{"CB_GETATTR",
245 		sumarg_cb_getattr,	sumres_cb_getattr,
246 		dtlarg_cb_getattr,	dtlres_cb_getattr},
247 	{"CB_RECALL",
248 		sumarg_cb_recall,	sum_nfsstat4,
249 		dtlarg_cb_recall,	dtl_nfsstat4},
250 };
251 static uint_t cb_num_opcodes = sizeof (cb_opcode_info) / sizeof (op_info_t *);
252 
253 static op_info_t opcode_info[] = {
254 	{"OP_ZERO",	NULL,	NULL,	NULL,	NULL},	/* 0 */
255 	{"OP_ONE",	NULL,	NULL,	NULL,	NULL},
256 	{"OP_TWO",	NULL,	NULL,	NULL,	NULL},  /* minor vers */
257 	{"ACCESS",
258 	sumarg_access,	sumres_access,	dtlarg_access,	dtlres_access},
259 	{"CLOSE",
260 	sumarg_close,	sumres_close,	dtlarg_close,	dtlres_close},
261 	{"COMMIT",
262 	sumarg_commit,	sumres_commit,	dtlarg_commit,	dtlres_commit},
263 	{"CREATE",					/* 5 */
264 	sumarg_create,	sum_nfsstat4,	dtlarg_create,	dtlres_create},
265 	{"DELEGPURGE",
266 	sumarg_delprge,	sum_nfsstat4,	dtlarg_delprge,	dtl_nfsstat4},
267 	{"DELEGRETURN",
268 	sumarg_delret,	sum_nfsstat4,	dtlarg_delret,	dtl_nfsstat4},
269 	{"GETATTR",
270 	sumarg_getattr,	sumres_getattr,	dtlarg_getattr,	dtlres_getattr},
271 	{"GETFH",
272 	NULL,		sumres_getfh,	NULL,	dtlres_getfh},
273 	{"LINK",					/* 10 */
274 	sumarg_link,	sum_nfsstat4,	dtlarg_link,	dtlres_link},
275 	{"LOCK",
276 	sumarg_lock,	sumres_lock,	dtlarg_lock,	dtlres_lock},
277 	{"LOCKT",
278 	sumarg_lockt,	sumres_lockt,	dtlarg_lockt,	dtlres_lockt},
279 	{"LOCKU",
280 	sumarg_locku,	sumres_locku,	dtlarg_locku,	dtlres_locku},
281 	{"LOOKUP",
282 	sumarg_lookup,	sum_nfsstat4,	dtlarg_lookup,	dtl_nfsstat4},
283 	{"LOOKUPP",					/* 15 */
284 	NULL,		sum_nfsstat4,	NULL,		dtl_nfsstat4},
285 	{"NVERIFY",
286 	NULL,		sum_nfsstat4,	dtlarg_verify,	dtl_nfsstat4},
287 	{"OPEN",
288 	sumarg_open,	sumres_open,	dtlarg_open,	dtlres_open},
289 	{"OPENATTR",
290 	sumarg_openattr, sum_nfsstat4, dtlarg_openattr, dtl_nfsstat4},
291 	{"OPEN_CONFIRM",
292 	sumarg_open_confirm,
293 	sumres_open_confirm,
294 	dtlarg_open_confirm,
295 	dtlres_open_confirm},
296 	{"OPEN_DOWNGRADE",
297 	sumarg_open_downgrd,
298 	sumres_open_downgrd,
299 	dtlarg_open_downgrd,
300 	dtlres_open_downgrd},
301 	{"PUTFH",
302 	sumarg_putfh,	sum_nfsstat4,	dtlarg_putfh,	dtl_nfsstat4},
303 	{"PUTPUBFH",					/* 20 */
304 	NULL,		sum_nfsstat4,	NULL,		dtl_nfsstat4},
305 	{"PUTROOTFH",
306 	NULL,		sum_nfsstat4,	NULL,		dtl_nfsstat4},
307 	{"READ",
308 	sumarg_read,	sumres_read,	dtlarg_read,	dtlres_read},
309 	{"READDIR",
310 	sumarg_readdir,	sumres_readdir,	dtlarg_readdir,	dtlres_readdir},
311 	{"READLINK",
312 	NULL,		sumres_readlnk,	NULL,		dtlres_readlnk},
313 	{"REMOVE",					/* 25 */
314 	sumarg_compnt,	sum_nfsstat4,	dtlarg_compnt,	dtlres_remove},
315 	{"RENAME",
316 	sumarg_rename,	sum_nfsstat4,	dtlarg_rename,	dtlres_rename},
317 	{"RENEW",
318 	sumarg_renew,	sum_nfsstat4,	dtlarg_renew,	dtl_nfsstat4},
319 	{"RESTOREFH",
320 	NULL,		sum_nfsstat4,	NULL,		dtl_nfsstat4},
321 	{"SAVEFH",
322 	NULL,		sum_nfsstat4,	NULL,		dtl_nfsstat4},
323 	{"SECINFO",					/* 30 */
324 	sumarg_secinfo,	sumres_secinfo,	dtlarg_secinfo,	dtlres_secinfo},
325 	{"SETATTR",
326 	sumarg_setattr,	sumres_setattr,	dtlarg_setattr,	dtlres_setattr},
327 	{"SETCLIENTID",
328 	sumarg_setclid,	sumres_setclid,	dtlarg_setclid,	dtlres_setclid},
329 	{"SETCLIENTID_CONFIRM",
330 	sumarg_setclid_cfm,
331 	sum_nfsstat4,
332 	dtlarg_setclid_cfm,
333 	dtl_nfsstat4},
334 	{"VERIFY",
335 	NULL,		sum_nfsstat4,	dtlarg_verify,	dtl_nfsstat4},
336 	{"WRITE",
337 	sumarg_write,	sumres_write,	dtlarg_write,	dtlres_write},
338 	{"RELEASE_LOCKOWNER",
339 	sumarg_release_lkown, sum_nfsstat4,
340 	dtlarg_release_lkown, dtl_nfsstat4},
341 };
342 static uint_t num_opcodes = sizeof (opcode_info) / sizeof (op_info_t *);
343 
344 /*
345  * File types.
346  */
347 
348 typedef struct {
349 	char *short_name;		/* for summary output */
350 	char *long_name;		/* for detail output */
351 } ftype_names_t;
352 
353 static ftype_names_t ftype_names[] = {
354 	{"Type 0",	"Type 0"},
355 	{"REG",		"Regular File"},
356 	{"DIR",		"Directory"},
357 	{"BLK",		"Block Device"},
358 	{"CHR",		"Character Device"},
359 	{"LNK",		"Symbolic Link"},	/* 5 */
360 	{"SOCK",	"Socket"},
361 	{"FIFO",	"FIFO"},
362 	{"ATTRDIR",	"Attribute Directory"},
363 	{"NAMEDATTR",	"Named Attribute"},
364 };
365 static uint_t num_ftypes = sizeof (ftype_names) / sizeof (ftype_names_t);
366 
367 static ftype_names_t	open_rflags[] = {
368 	{"?",	"UNKNOWN"},	/* 0 */
369 	{"CF",	"CONFIRM"},	/* 1 */
370 	{"PL",	"POSIX LOCK"},	/* 2 */
371 	{"?",	"UNKNOWN"},
372 };
373 static uint_t num_open_rflags =
374 	sizeof (open_rflags) / sizeof (ftype_names_t) - 1;
375 
376 static char *get_flags(uint_t, ftype_names_t *, uint_t, int, char *);
377 
378 #define	sum_open_rflags(flag) \
379 	get_flags((flag), open_rflags, num_open_rflags, 1, " RF=")
380 
381 #define	detail_open_rflags(flag) \
382 	get_flags((flag), open_rflags, num_open_rflags, 0, NULL)
383 
384 static void prt_supported_attrs(XDR *);
385 static void prt_type(XDR *);
386 static void prt_fh_expire_type(XDR *);
387 static void prt_change(XDR *);
388 static void prt_size(XDR *);
389 static void prt_link_support(XDR *);
390 static void prt_symlink_support(XDR *);
391 static void prt_named_attr(XDR *);
392 static void prt_fsid(XDR *);
393 static void prt_unique_handles(XDR *);
394 static void prt_lease_time(XDR *);
395 static void prt_rdattr_error(XDR *);
396 static void prt_acl(XDR *);
397 static void prt_aclsupport(XDR *);
398 static void prt_archive(XDR *);
399 static void prt_cansettime(XDR *);
400 static void prt_case_insensitive(XDR *);
401 static void prt_case_preserving(XDR *);
402 static void prt_chown_restricted(XDR *);
403 static void prt_filehandle(XDR *);
404 static void prt_fileid(XDR *);
405 static void prt_mounted_on_fileid(XDR *);
406 static void prt_files_avail(XDR *);
407 static void prt_files_free(XDR *);
408 static void prt_files_total(XDR *);
409 static void prt_fs_locations(XDR *);
410 static void prt_hidden(XDR *);
411 static void prt_homogeneous(XDR *);
412 static void prt_maxfilesize(XDR *);
413 static void prt_maxlink(XDR *);
414 static void prt_maxname(XDR *);
415 static void prt_maxread(XDR *);
416 static void prt_maxwrite(XDR *);
417 static void prt_mimetype(XDR *);
418 static void prt_mode(XDR *);
419 static void prt_no_trunc(XDR *);
420 static void prt_numlinks(XDR *);
421 static void prt_owner(XDR *);
422 static void prt_owner_group(XDR *);
423 static void prt_quota_avail_hard(XDR *);
424 static void prt_quota_avail_soft(XDR *);
425 static void prt_quota_used(XDR *);
426 static void prt_rawdev(XDR *);
427 static void prt_space_avail(XDR *);
428 static void prt_space_free(XDR *);
429 static void prt_space_total(XDR *);
430 static void prt_space_used(XDR *);
431 static void prt_system(XDR *);
432 static void prt_time_access(XDR *);
433 static void prt_time_access_set(XDR *);
434 static void prt_time_backup(XDR *);
435 static void prt_time_create(XDR *);
436 static void prt_time_delta(XDR *);
437 static void prt_time_metadata(XDR *);
438 static void prt_time_modify(XDR *);
439 static void prt_time_modify_set(XDR *);
440 
441 
442 
443 /*
444  * Information for attributes.
445  * name		name of the attribute.
446  * prt_details	function to XDR decode the attribute and print it.
447  *
448  * XXX If this table ever gets extensively changed (including
449  * reorganization to track changes to the spec), it would probably be a
450  * good idea to change to a scheme where the table is mechanically
451  * generated.  Look at $SRC/uts/common/rpcsvc for how this is done in the
452  * kernel.
453  */
454 
455 typedef struct {
456 	char	*name;
457 	void	(*prt_details)(XDR *);
458 } attr_info_t;
459 
460 static attr_info_t attr_info[MAX_ATTRIBUTES] = {
461 	{"SUPPORTED_ATTRS",	prt_supported_attrs},
462 	{"TYPE",		prt_type},
463 	{"FH_EXPIRE_TYPE",	prt_fh_expire_type},
464 	{"CHANGE",		prt_change},
465 	{"SIZE",		prt_size},
466 	{"LINK_SUPPORT",	prt_link_support},	/* 5 */
467 	{"SYMLINK_SUPPORT",	prt_symlink_support},
468 	{"NAMED_ATTR",		prt_named_attr},
469 	{"FSID",		prt_fsid},
470 	{"UNIQUE_HANDLES",	prt_unique_handles},
471 	{"LEASE_TIME",		prt_lease_time},	/* 10 */
472 	{"RDATTR_ERROR",	prt_rdattr_error},
473 	{"ACL",			prt_acl},
474 	{"ACLSUPPORT",		prt_aclsupport},
475 	{"ARCHIVE",		prt_archive},
476 	{"CANSETTIME",		prt_cansettime},	/* 15 */
477 	{"CASE_INSENSITIVE",	prt_case_insensitive},
478 	{"CASE_PRESERVING",	prt_case_preserving},
479 	{"CHOWN_RESTRICTED",	prt_chown_restricted},
480 	{"FILEHANDLE",		prt_filehandle},
481 	{"FILEID",		prt_fileid},		/* 20 */
482 	{"FILES_AVAIL",		prt_files_avail},
483 	{"FILES_FREE",		prt_files_free},
484 	{"FILES_TOTAL",		prt_files_total},
485 	{"FS_LOCATIONS",	prt_fs_locations},
486 	{"HIDDEN",		prt_hidden},		/* 25 */
487 	{"HOMOGENEOUS",		prt_homogeneous},
488 	{"MAXFILESIZE",		prt_maxfilesize},
489 	{"MAXLINK",		prt_maxlink},
490 	{"MAXNAME",		prt_maxname},
491 	{"MAXREAD",		prt_maxread},		/* 30 */
492 	{"MAXWRITE",		prt_maxwrite},
493 	{"MIMETYPE",		prt_mimetype},
494 	{"MODE",		prt_mode},
495 	{"NO_TRUNC",		prt_no_trunc},
496 	{"NUMLINKS",		prt_numlinks},		/* 35 */
497 	{"OWNER",		prt_owner},
498 	{"OWNER_GROUP",		prt_owner_group},
499 	{"QUOTA_AVAIL_HARD",	prt_quota_avail_hard},
500 	{"QUOTA_AVAIL_SOFT",	prt_quota_avail_soft},
501 	{"QUOTA_USED",		prt_quota_used},	/* 40 */
502 	{"RAWDEV",		prt_rawdev},
503 	{"SPACE_AVAIL",		prt_space_avail},
504 	{"SPACE_FREE",		prt_space_free},
505 	{"SPACE_TOTAL",		prt_space_total},
506 	{"SPACE_USED",		prt_space_used},	/* 45 */
507 	{"SYSTEM",		prt_system},
508 	{"TIME_ACCESS",		prt_time_access},
509 	{"TIME_ACCESS_SET",	prt_time_access_set},
510 	{"TIME_BACKUP",		prt_time_backup},
511 	{"TIME_CREATE",		prt_time_create},	/* 50 */
512 	{"TIME_DELTA",		prt_time_delta},
513 	{"TIME_METADATA",	prt_time_metadata},
514 	{"TIME_MODIFY",		prt_time_modify},
515 	{"TIME_MODIFY_SET",	prt_time_modify_set},
516 	{"MOUNTED_ON_FILEID",	prt_mounted_on_fileid},
517 };
518 
519 extern char *get_sum_line();
520 
521 extern jmp_buf xdr_err;
522 
523 static void sum_comp4res(char *, char *(*)(void));
524 static char *sum_compound4args(void);
525 static char *sum_compound4res(void);
526 static char *sum_operand(nfs_argop4 *opp);
527 static char *sum_result(nfs_resop4 *resp);
528 
529 static char *sum_cb_compound4args(void);
530 static char *sum_cb_compound4res(void);
531 static char *sum_cb_operand(nfs_cb_argop4 *opp);
532 static char *sum_cb_result(nfs_cb_resop4 *resp);
533 
534 static void detail_acetype4(acetype4);
535 static void detail_uint32_bitmap(uint32_t, char *[], int);
536 static void detail_aceflag4(aceflag4);
537 static void detail_acemask4(acemask4);
538 static void detail_nfs_argop4(void);
539 static void detail_nfs_resop4(void);
540 static void detail_cb_argop4(void);
541 static void detail_cb_resop4(void);
542 
543 static char *attr_name(uint_t);
544 static char *claim_name(enum open_claim_type4 claim_type);
545 static char *delegation_type_name(enum open_delegation_type4 type);
546 static char *flavor_name(uint_t flavor);
547 static char *gss_svc_name(rpc_gss_svc_t svc);
548 static char *limitby_name(enum limit_by4 limitby);
549 static char *lock_type_name(enum nfs_lock_type4);
550 static char *opcode_name(uint_t);
551 static char *cb_opcode_name(uint_t opnum);
552 static char *status_name(int);
553 static char *status_name_compat(int);
554 static char *status_name_pcol(int);
555 static char *sum_type_name(nfs_ftype4);
556 static void sum_access4(char *buf, size_t buflen, uint32_t bits);
557 static void detail_access4(char *, uint32_t);
558 static void sum_claim(char *buf, size_t buflen, open_claim4 *claim);
559 static void detail_claim(open_claim4 *claim);
560 static char *sum_clientid(clientid4 client);
561 static void detail_clientid(clientid4 client);
562 static char *_sum_stateid(stateid4 *, char *prefix);
563 static void sum_delegation(char *buf, size_t buflen, open_delegation4 *delp);
564 static void detail_delegation(open_delegation4 *delp);
565 static void detail_lock_owner(lock_owner4 *owner);
566 static void detail_open_owner(open_owner4 *owner);
567 static void sum_openflag(char *bufp, int buflen, openflag4 *flagp);
568 static char *get_deleg_typestr(open_delegation_type4 dt);
569 static void detail_openflag(openflag4 *flagp);
570 static void sum_name(char *buf, size_t buflen, open_claim4 *claim);
571 static void detail_rpcsec_gss(rpcsec_gss_info *);
572 static void detail_secinfo4(secinfo4 *infop);
573 static char *sum_space_limit(nfs_space_limit4 *limitp);
574 static void detail_space_limit(nfs_space_limit4 *limitp);
575 static char *detail_type_name(nfs_ftype4);
576 static char *createhow4_name(createhow4 *crtp);
577 
578 
579 static void showxdr_utf8string(char *);
580 static char *utf8localize(utf8string *);
581 static void utf8free(void);
582 static void sum_pathname4(char *, size_t, pathname4 *);
583 static void detail_pathname4(pathname4 *pathp, char *);
584 static void sum_compname4(char *buf, size_t buflen, component4 *comp);
585 static void detail_compname4(component4 *comp);
586 
587 static void detail_fattr4(fattr4 *attrp);
588 static void detail_attr_bitmap(char *, bitmap4 *, unpkd_attrmap_t *);
589 static void sum_attr_bitmap(char *buf, size_t buflen, bitmap4 *mapp);
590 static void detail_fattr4_change(char *msg, fattr4_change chg);
591 static char *sum_fh4(nfs_fh4 *fhp);
592 static void detail_fh4(nfs_fh4 *fh);
593 
594 #define	fh4_hash(fh) adler16((fh)->nfs_fh4_val, (fh)->nfs_fh4_len)
595 #define	stateid_hash(st) adler16((st)->other, sizeof ((st)->other))
596 #define	owner_hash(own) adler16((own)->owner_val, (own)->owner_len)
597 
598 #define	sum_deleg_stateid(st)	_sum_stateid((st), "DST=")
599 #define	sum_open_stateid(st)	_sum_stateid((st), "OST=")
600 #define	sum_lock_stateid(st)	_sum_stateid((st), "LST=")
601 #define	sum_stateid(st)		_sum_stateid((st), "ST=")
602 
603 #define	detail_deleg_stateid(st)	_detail_stateid((st), "Delegation ")
604 #define	detail_open_stateid(st)		_detail_stateid((st), "Open ")
605 #define	detail_lock_stateid(st)		_detail_stateid((st), "Lock ")
606 #define	detail_stateid(st)		_detail_stateid((st), "")
607 
608 #define	SPECIAL_STATEID0	"SPC0"
609 #define	SPECIAL_STATEID1	"SPC1"
610 
611 #define	DONT_CHANGE		0
612 #define	SET_TO_SERVER_TIME	1
613 #define	SET_TO_CLIENT_TIME	2
614 
615 static stateid4 spec_stateid_0 =
616 	{0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
617 static stateid4 spec_stateid_1 =
618 	{0xFFFFFFFF, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
619 
620 static char *procnames_short[] = {
621 	"NULL4",	/*  0 */
622 	"COMPOUND4"	/*  1 */
623 };
624 
625 static char *procnames_long[] = {
626 	"Null procedure",		/*  0 */
627 	"Compound",			/*  1 */
628 };
629 
630 static char *cb_procnames_short[] = {
631 	"CB_NULL",	/*  0 */
632 	"CB_COMPOUND"	/*  1 */
633 };
634 
635 static char *cb_procnames_long[] = {
636 	"Null CallBack procedure",	/*  0 */
637 	"CallBack compound",		/*  1 */
638 };
639 
640 static char *acetype4_names[] = {
641 	"ACE4_ACCESS_ALLOWED_ACE_TYPE",
642 	"ACE4_ACCESS_DENIED_ACE_TYPE",
643 	"ACE4_SYSTEM_AUDIT_ACE_TYPE",
644 	"ACE4_SYSTEM_ALARM_ACE_TYPE"
645 };
646 #define	ACETYPE4_NAMES_MAX (sizeof (acetype4_names) / sizeof (char *))
647 
648 static char *aceflag4_names[] = {
649 	"ACE4_FILE_INHERIT_ACE",
650 	"ACE4_DIRECTORY_INHERIT_ACE",
651 	"ACE4_NO_PROPAGATE_INHERIT_ACE",
652 	"ACE4_INHERIT_ONLY_ACE",
653 	"ACE4_SUCCESSFUL_ACCESS_ACE_FLAG",
654 	"ACE4_FAILED_ACCESS_ACE_FLAG",
655 	"ACE4_IDENTIFIER_GROUP"
656 };
657 #define	ACEFLAG4_NAMES_MAX (sizeof (aceflag4_names) / sizeof (char *))
658 
659 static char *acemask4_names[] = {
660 	"ACE4_READ_DATA/ACE4_LIST_DIRECTORY",
661 	"ACE4_WRITE_DATA/ACE4_ADD_FILE",
662 	"ACE4_APPEND_DATA/ACE4_ADD_SUBDIRECTORY",
663 	"ACE4_READ_NAMED_ATTRS",
664 	"ACE4_WRITE_NAMED_ATTRS",
665 	"ACE4_EXECUTE",
666 	"ACE4_DELETE_CHILD",
667 	"ACE4_READ_ATTRIBUTES",
668 	"ACE4_WRITE_ATTRIBUTES",
669 	"UNDEFINED",	/* 0x00000200 */
670 	"UNDEFINED",	/* 0x00000400 */
671 	"UNDEFINED",	/* 0x00000800 */
672 	"UNDEFINED",	/* 0x00001000 */
673 	"UNDEFINED",	/* 0x00002000 */
674 	"UNDEFINED",	/* 0x00004000 */
675 	"UNDEFINED",	/* 0x00008000 */
676 	"ACE4_DELETE",
677 	"ACE4_READ_ACL",
678 	"ACE4_WRITE_ACL",
679 	"ACE4_WRITE_OWNER",
680 	"ACE4_SYNCHRONIZE"
681 };
682 #define	ACEMASK4_NAMES_MAX (sizeof (acemask4_names) / sizeof (char *))
683 
684 #define	MAXPROC	1
685 
686 /*ARGSUSED*/
687 void
688 interpret_nfs4_cb(int flags, int type, int xid, int vers, int proc,
689     char *data, int len)
690 {
691 	char *line = NULL;
692 
693 	if (proc < 0 || proc > MAXPROC)
694 		return;
695 
696 	if (flags & F_SUM) {
697 		line = get_sum_line();
698 
699 		if (type == CALL) {
700 			(void) sprintf(line, "NFS C %s",
701 			    proc == CB_COMPOUND ? "CB4" :
702 			    cb_procnames_short[proc]);
703 			line += strlen(line);
704 
705 			if (proc == CB_COMPOUND) {
706 				static utf8string tag;
707 
708 				if (!xdr_utf8string(&xdrm, &tag))
709 					longjmp(xdr_err, 1);
710 				sprintf(line, " (%.20s) %s",
711 				    utf8localize(&tag),
712 				    sum_cb_compound4args());
713 				xdr_free(xdr_utf8string, (char *)&tag);
714 			}
715 			check_retransmit(line, xid);
716 		} else {
717 			(void) sprintf(line, "NFS R %s ",
718 			    proc == CB_COMPOUND ? "CB4" :
719 			    cb_procnames_short[proc]);
720 			line += strlen(line);
721 			if (proc == CB_COMPOUND)
722 				sum_comp4res(line, sum_cb_compound4res);
723 		}
724 	}
725 
726 	if (flags & F_DTAIL) {
727 		show_header("NFS:  ", "Sun NFS4 CallBack", len);
728 		show_space();
729 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
730 		    proc, cb_procnames_long[proc]);
731 		if (proc == CB_COMPOUND) {
732 			if (type == CALL) {
733 				showxdr_utf8string("Tag = %s");
734 				detail_cb_argop4();
735 			} else {
736 				nfsstat4 status;
737 
738 				status = getxdr_long();
739 				showxdr_utf8string("Tag = %s");
740 				sprintf(get_line(0, 0), "Status = %d (%s)",
741 				    status, status_name(status));
742 				detail_cb_resop4();
743 			}
744 		}
745 		show_trailer();
746 	}
747 
748 	utf8free();			/* cf. utf8localize() */
749 }
750 
751 
752 /*ARGSUSED*/
753 void
754 interpret_nfs4(int flags, int type, int xid, int vers, int proc,
755     char *data, int len)
756 {
757 	char *line = NULL;
758 
759 	if (proc < 0 || proc > MAXPROC)
760 		return;
761 
762 	nfs4_fragged_rpc = 0;
763 	nfs4_pkt_len = len;
764 	nfs4_pkt_start = xdr_getpos(&xdrm);
765 
766 	if (flags & F_SUM) {
767 		line = get_sum_line();
768 
769 		if (type == CALL) {
770 			(void) sprintf(line, "NFS C %s",
771 			    proc == NFSPROC4_COMPOUND ? "4" :
772 			    procnames_short[proc]);
773 			line += strlen(line);
774 
775 			if (proc == NFSPROC4_COMPOUND) {
776 				static utf8string tag;
777 
778 				if (!xdr_utf8string(&xdrm, &tag))
779 					longjmp(xdr_err, 1);
780 				sprintf(line, " (%.20s) %s",
781 				    utf8localize(&tag),
782 				    sum_compound4args());
783 				xdr_free(xdr_utf8string, (char *)&tag);
784 			}
785 			check_retransmit(line, xid);
786 		} else {
787 			(void) sprintf(line, "NFS R %s ",
788 			    proc == NFSPROC4_COMPOUND ? "4" :
789 			    procnames_short[proc]);
790 			line += strlen(line);
791 
792 			if (proc == NFSPROC4_COMPOUND)
793 				sum_comp4res(line, sum_compound4res);
794 		}
795 	}
796 
797 	if (flags & F_DTAIL) {
798 		show_header("NFS:  ", "Sun NFS", len);
799 		show_space();
800 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
801 		    proc, procnames_long[proc]);
802 		if (proc == NFSPROC4_COMPOUND) {
803 			if (type == CALL) {
804 				showxdr_utf8string("Tag = %s");
805 				detail_nfs_argop4();
806 			} else {
807 				nfsstat4 status;
808 
809 				status = getxdr_long();
810 				showxdr_utf8string("Tag = %s");
811 				sprintf(get_line(0, 0), "Status = %d (%s)",
812 				    status, status_name(status));
813 				detail_nfs_resop4();
814 			}
815 		}
816 		show_trailer();
817 	}
818 
819 	utf8free();			/* cf. utf8localize() */
820 }
821 
822 
823 
824 /*
825  * Return the names and arguments of the oplist elements, up to
826  * SUM_COMPND_MAX characters.  If the elements don't fit, include a "..."
827  * at the end of the string.
828  */
829 
830 static char *
831 sum_compound4args(void)
832 {
833 	static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
834 	int numops;
835 	const size_t buflen = sizeof (buf);
836 	char *bp;
837 	nfs_argop4 one_op;
838 	uint32_t minor_version;
839 
840 	buf[0] = '\0';
841 
842 	if (setjmp(xdr_err)) {
843 		bp = buf + strlen(buf);
844 		snprintf(bp, buflen - (bp - buf),
845 		    nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
846 		return (buf);
847 	}
848 
849 	/*
850 	 * might be nice to print minor version, but doesn't
851 	 * seem like very useful info for summary mode
852 	 */
853 	if (!xdr_uint32_t(&xdrm, &minor_version))
854 		longjmp(xdr_err, 1);
855 
856 	numops = getxdr_long();
857 	bp = buf;
858 	while (numops-- > 0) {
859 		char *operand;
860 
861 		bzero(&one_op, sizeof (one_op));
862 
863 		if (!xdr_nfs_argop4(&xdrm, &one_op)) {
864 			xdr_free(xdr_nfs_argop4, (char *)&one_op);
865 			longjmp(xdr_err, 1);
866 		}
867 		snprintf(bp, buflen - (bp - buf), "%s ",
868 		    opcode_name(one_op.argop));
869 		bp += strlen(bp);
870 
871 		operand = sum_operand(&one_op);
872 		if (strlen(operand) > 0) {
873 			snprintf(bp, buflen - (bp - buf), "%s ", operand);
874 			bp += strlen(bp);
875 		}
876 
877 		/* nfs4_skip_bytes set by xdr_nfs4_argop4 */
878 		if (nfs4_skip_bytes != 0)
879 			nfs4_xdr_skip(nfs4_skip_bytes);
880 
881 		xdr_free(xdr_nfs_argop4, (char *)&one_op);
882 
883 		/* add "..." if past the "end" of the buffer */
884 		if (bp - buf > SUM_COMPND_MAX) {
885 			strcpy(buf + SUM_COMPND_MAX - strlen("..."),
886 			    "...");
887 			break;
888 		}
889 	}
890 
891 	return (buf);
892 }
893 
894 static void
895 nfs4_xdr_skip(int nbytes)
896 {
897 	int resid, off, len, cur_pos, new_pos;
898 
899 	len = RNDUP(nbytes);
900 	cur_pos = xdr_getpos(&xdrm);
901 
902 	/*
903 	 * Time to skip over the rd/wr data.  If the
904 	 * rd/wr data is completely contained in the first
905 	 * frag, we must skip over it to process the rest of
906 	 * the packet.
907 	 *
908 	 * nfs4_pkt_start: XDR position of start of NFS4 compound
909 	 * nfs4_pkt_len: number of bytes in pkt relative to
910 	 *		 nfs4_pkt_start
911 	 *
912 	 * cur_pos: current XDR position
913 	 * off: current XDR position relative to nfs4_pkt_start
914 	 * resid: number of unprocessed bytes in current pkt
915 	 *	  (relative to cur_pos/off)
916 	 *
917 	 * If nbytes <= resid, then we must skip over the rd/wr
918 	 * bytes so we can read the next op/compound in this
919 	 * packet.  Otherwise, set the fragged flag so we can
920 	 * display the fragged_rpc message.
921 	 */
922 	off = cur_pos - nfs4_pkt_start;
923 	resid = nfs4_pkt_len - off;
924 
925 	/*
926 	 * set nfs4_fragged_rpc if the requested number of "skip"
927 	 * bytes is larger than the bytes remaining in the XDR
928 	 * stream/current packet.  The global is reset to 0 at
929 	 * start of interpret_nfs4.
930 	 */
931 	new_pos = cur_pos + ((nfs4_fragged_rpc = len > resid) ? resid : len);
932 
933 	/* there's nothing to do for error case (if it fails pkt is doomed) */
934 	xdr_setpos(&xdrm, new_pos);
935 }
936 
937 
938 /*
939  * Return the names and arguments of the oplist elements, up to
940  * SUM_COMPND_MAX characters.  If the elements don't fit, include a "..."
941  * at the end of the string.
942  */
943 static char *
944 sum_cb_compound4args(void)
945 {
946 	static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
947 	int numops;
948 	const size_t buflen = sizeof (buf);
949 	char *bp;
950 	nfs_cb_argop4 one_op;
951 	uint32_t minor_version, callback_ident;
952 
953 	buf[0] = '\0';
954 	if (setjmp(xdr_err)) {
955 		bp = buf + strlen(buf);
956 		snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
957 		    " RPC>");
958 		return (buf);
959 	}
960 
961 	/*
962 	 * might be nice to print minor version, but doesn't
963 	 * seem like very useful info for summary mode
964 	 */
965 	if (!xdr_uint32_t(&xdrm, &minor_version))
966 		longjmp(xdr_err, 1);
967 
968 	/* print callback_ident */
969 	if (!xdr_uint32_t(&xdrm, &callback_ident))
970 		longjmp(xdr_err, 1);
971 	snprintf(buf, buflen, "CBID=%u ", callback_ident);
972 
973 	bp = buf + strlen(buf);
974 	numops = getxdr_long();
975 
976 	while (numops-- > 0) {
977 		char *operand;
978 
979 		bzero(&one_op, sizeof (one_op));
980 		if (!xdr_nfs_cb_argop4(&xdrm, &one_op)) {
981 			xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
982 			longjmp(xdr_err, 1);
983 		}
984 
985 		snprintf(bp, buflen - (bp - buf), "%s ",
986 		    cb_opcode_name(one_op.argop));
987 		bp += strlen(bp);
988 		operand = sum_cb_operand(&one_op);
989 		if (strlen(operand) > 0) {
990 			snprintf(bp, buflen - (bp - buf), "%s ", operand);
991 			bp += strlen(bp);
992 		}
993 
994 		xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
995 
996 		/* add "..." if past the "end" of the buffer */
997 		if (bp - buf > SUM_COMPND_MAX) {
998 			strcpy(buf + SUM_COMPND_MAX - strlen("..."),
999 			    "...");
1000 			break;
1001 		}
1002 	}
1003 
1004 	return (buf);
1005 }
1006 
1007 /*
1008  * Return the summarized argument list for the given nfs_argop4.
1009  */
1010 
1011 static char *
1012 sum_operand(nfs_argop4 *opp)
1013 {
1014 	static char buf[1024];
1015 	void (*fmtproc)(char *, size_t, void *);
1016 
1017 	buf[0] = '\0';
1018 	if (opp->argop < num_opcodes) {
1019 		fmtproc = opcode_info[opp->argop].sumarg;
1020 		if (fmtproc != NULL)
1021 			fmtproc(buf, sizeof (buf), &opp->nfs_argop4_u);
1022 	}
1023 
1024 	return (buf);
1025 }
1026 
1027 /*
1028  * Return the summarized argument list for the given nfs_argop4.
1029  */
1030 
1031 static char *
1032 sum_cb_operand(nfs_cb_argop4 *opp)
1033 {
1034 	static char buf[1024];
1035 	void (*fmtproc)(char *, size_t, void *);
1036 
1037 	buf[0] = '\0';
1038 	if (opp->argop < cb_num_opcodes) {
1039 		fmtproc = cb_opcode_info[opp->argop].sumarg;
1040 		if (fmtproc != NULL)
1041 			fmtproc(buf, sizeof (buf), &opp->nfs_cb_argop4_u);
1042 	}
1043 
1044 	return (buf);
1045 }
1046 
1047 /*
1048  * Print details about the nfs_argop4 that is next in the XDR stream.
1049  */
1050 
1051 static void
1052 detail_nfs_argop4(void)
1053 {
1054 	int numops;
1055 	nfs_argop4 one_op;
1056 	void (*fmtproc)(void *);
1057 	uint32_t minor_version;
1058 
1059 	if (!xdr_uint32_t(&xdrm, &minor_version))
1060 		longjmp(xdr_err, 1);
1061 
1062 	(void) sprintf(get_line(0, 0), "Minor version = %u",
1063 	    minor_version);
1064 
1065 	numops = getxdr_long();
1066 	(void) sprintf(get_line(0, 0), "Number of operations = %d",
1067 	    numops);
1068 
1069 	while (numops-- > 0) {
1070 		bzero(&one_op, sizeof (one_op));
1071 
1072 		if (!xdr_nfs_argop4(&xdrm, &one_op)) {
1073 			xdr_free(xdr_nfs_argop4, (char *)&one_op);
1074 			longjmp(xdr_err, 1);
1075 		}
1076 
1077 		get_line(0, 0);		/* blank line to separate ops */
1078 		sprintf(get_line(0, 0), "Op = %d (%s)",
1079 		    one_op.argop, opcode_name(one_op.argop));
1080 		if (one_op.argop < num_opcodes) {
1081 			fmtproc = opcode_info[one_op.argop].dtlarg;
1082 			if (fmtproc != NULL)
1083 				fmtproc(&one_op.nfs_argop4_u);
1084 		}
1085 
1086 		/* nfs4_skip_bytes set by xdr_nfs_argop4() */
1087 		if (nfs4_skip_bytes)
1088 			nfs4_xdr_skip(nfs4_skip_bytes);
1089 
1090 		xdr_free(xdr_nfs_argop4, (char *)&one_op);
1091 	}
1092 }
1093 
1094 
1095 /*
1096  * Print details about the nfs_argop4 that is next in the XDR stream.
1097  */
1098 static void
1099 detail_cb_argop4(void)
1100 {
1101 	int numops;
1102 	nfs_cb_argop4 one_op;
1103 	void (*fmtproc)(void *);
1104 	uint32_t minor_version, callback_ident;
1105 
1106 	if (!xdr_uint32_t(&xdrm, &minor_version))
1107 		longjmp(xdr_err, 1);
1108 	(void) sprintf(get_line(0, 0), "Minor version = %u",
1109 	    minor_version);
1110 
1111 	if (!xdr_uint32_t(&xdrm, &callback_ident))
1112 		longjmp(xdr_err, 1);
1113 	(void) sprintf(get_line(0, 0), "Callback Ident = %u",
1114 	    callback_ident);
1115 
1116 	numops = getxdr_long();
1117 	(void) sprintf(get_line(0, 0), "Number of operations = %d",
1118 	    numops);
1119 
1120 	while (numops-- > 0) {
1121 		bzero(&one_op, sizeof (one_op));
1122 		if (!xdr_nfs_cb_argop4(&xdrm, &one_op)) {
1123 			xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
1124 			longjmp(xdr_err, 1);
1125 		}
1126 
1127 		get_line(0, 0);		/* blank line to separate ops */
1128 		sprintf(get_line(0, 0), "Op = %d (%s)",
1129 		    one_op.argop, cb_opcode_name(one_op.argop));
1130 		if (one_op.argop < cb_num_opcodes) {
1131 			fmtproc = cb_opcode_info[one_op.argop].dtlarg;
1132 			if (fmtproc != NULL)
1133 				fmtproc(&one_op.nfs_cb_argop4_u);
1134 		}
1135 
1136 		xdr_free(xdr_nfs_cb_argop4, (char *)&one_op);
1137 	}
1138 }
1139 
1140 /*
1141  * component_name: return a printable string for the given component4.  I'm
1142  * leaving this as a separate function (as opposed to having the callers
1143  * call utf8localize() directly) in case the definition of component4
1144  * changes.
1145  */
1146 
1147 static char *
1148 component_name(component4 *cp)
1149 {
1150 	return (utf8localize(cp));
1151 }
1152 
1153 /*
1154  * linktext_name.  cf. component_name().
1155  */
1156 
1157 static char *
1158 linktext_name(linktext4 *lp)
1159 {
1160 	return (utf8localize((utf8string *)lp));
1161 }
1162 
1163 /*
1164  * stable_how4_name: return a string for "how".
1165  */
1166 
1167 static char *
1168 stable_how4_name(stable_how4 how)
1169 {
1170 	char *result;
1171 
1172 	switch (how) {
1173 	case UNSTABLE4:
1174 		result = "ASYNC";
1175 		break;
1176 	case DATA_SYNC4:
1177 		result = "DSYNC";
1178 		break;
1179 	case FILE_SYNC4:
1180 		result = "FSYNC";
1181 		break;
1182 	default:
1183 		result = "?";
1184 		break;
1185 	}
1186 
1187 	return (result);
1188 }
1189 
1190 /*
1191  * sum_open_share_access: return a string corresponding to the
1192  * given OPEN share access bitmask.
1193  */
1194 
1195 static char *
1196 sum_open_share_access(int32_t mask)
1197 {
1198 	char *result;
1199 
1200 	switch (mask) {
1201 	case 0:
1202 		result = "N";
1203 		break;
1204 	case OPEN4_SHARE_ACCESS_READ:
1205 		result = "R";
1206 		break;
1207 	case OPEN4_SHARE_ACCESS_WRITE:
1208 		result = "W";
1209 		break;
1210 	case OPEN4_SHARE_ACCESS_BOTH:
1211 		result = "RW";
1212 		break;
1213 	default:
1214 		result = "?";
1215 		break;
1216 	}
1217 
1218 	return (result);
1219 }
1220 
1221 /*
1222  * sum_open_share_deny: return a string corresponding to the
1223  * given OPEN share deny bitmask.
1224  */
1225 
1226 static char *
1227 sum_open_share_deny(int32_t mask)
1228 {
1229 	char *result;
1230 
1231 	switch (mask) {
1232 	case OPEN4_SHARE_DENY_NONE:
1233 		result = "N";
1234 		break;
1235 	case OPEN4_SHARE_DENY_READ:
1236 		result = "R";
1237 		break;
1238 	case OPEN4_SHARE_DENY_WRITE:
1239 		result = "W";
1240 		break;
1241 	case OPEN4_SHARE_DENY_BOTH:
1242 		result = "RW";
1243 		break;
1244 	default:
1245 		result = "?";
1246 		break;
1247 	}
1248 
1249 	return (result);
1250 }
1251 
1252 static int
1253 special_stateid(stateid4 *stateid)
1254 {
1255 
1256 	if (! memcmp(stateid, &spec_stateid_0, sizeof (*stateid)))
1257 		return (0);
1258 
1259 	if (! memcmp(stateid, &spec_stateid_1, sizeof (*stateid)))
1260 		return (1);
1261 
1262 	return (-1);
1263 }
1264 
1265 static char *
1266 _sum_stateid(stateid4 *stateid, char *prefix)
1267 {
1268 	static char buf[32];
1269 	int spec;
1270 
1271 	if ((spec = special_stateid(stateid)) < 0)
1272 		snprintf(buf, sizeof (buf), "%s%04X:%u", prefix,
1273 		    stateid_hash(stateid), stateid->seqid);
1274 	else
1275 		snprintf(buf, sizeof (buf), "%s%s", prefix,
1276 		    spec == 0 ? "SPC0" : (spec == 1 ? "SPC1" : "SPC?"));
1277 	return (buf);
1278 }
1279 
1280 static void
1281 _detail_stateid(stateid4 *stateid, char *prefix)
1282 {
1283 	int spec;
1284 	char seqstr[32] = {0};
1285 
1286 	spec = special_stateid(stateid);
1287 
1288 	if (spec < 0)
1289 		sprintf(get_line(0, 0), "%sState ID hash = %04X",
1290 		    prefix, stateid_hash(stateid));
1291 	else
1292 		sprintf(get_line(0, 0), "%sState ID hash = %s",	prefix,
1293 		    spec == 0 ? "SPECIAL_0" :
1294 		    (spec == 1 ? "SPECIAL_1" : "SPECIAL_?"));
1295 
1296 	sprintf(get_line(0, 0), "    len = %u    val = %s",
1297 	    sizeof (stateid->other),
1298 	    tohex(stateid->other, sizeof (stateid->other)));
1299 
1300 	/*
1301 	 * If spec 0/1 stateid, print seqid in hex; otherwise,
1302 	 * use decimal.  This makes it more clear how spec stateids
1303 	 * are constructed [obvious that either all bits are 0, or all
1304 	 * bits are 1].
1305 	 */
1306 	if (spec == -1)
1307 		sprintf(seqstr, "%d", stateid->seqid);
1308 	else
1309 		sprintf(seqstr, "%08X", stateid->seqid);
1310 
1311 	sprintf(get_line(0, 0), "    %sState ID Sequence ID = %s",
1312 	    prefix, seqstr);
1313 }
1314 
1315 
1316 static char *
1317 sum_lock_denied(LOCK4denied *denied)
1318 {
1319 	static char buf[64];
1320 
1321 	sprintf(buf, "%s %llu:%llu LO=%04X",
1322 	    sum_lock_type_name(denied->locktype),
1323 	    denied->offset, denied->length,
1324 	    owner_hash(&denied->owner.owner));
1325 
1326 	return (buf);
1327 }
1328 
1329 static void
1330 detail_lock_denied(LOCK4denied *denied)
1331 {
1332 	sprintf(get_line(0, 0), "Type = %s", lock_type_name(denied->locktype));
1333 	detail_lock_owner(&denied->owner);
1334 	sprintf(get_line(0, 0), "Offset = %llu", denied->offset);
1335 	sprintf(get_line(0, 0), "Length = %llu", denied->length);
1336 }
1337 
1338 /*
1339  * sum_createhow4: return the string name of "how".
1340  */
1341 
1342 static char *
1343 createhow4_name(createhow4 *crtp)
1344 {
1345 	char *result;
1346 
1347 	switch (crtp->mode) {
1348 	case UNCHECKED4:
1349 		result = "UNCHECKED";
1350 		break;
1351 	case GUARDED4:
1352 		result = "GUARDED";
1353 		break;
1354 	case EXCLUSIVE4:
1355 		result = "EXCLUSIVE";
1356 		break;
1357 	default:
1358 		result = "?";
1359 		break;
1360 	}
1361 
1362 	return (result);
1363 }
1364 
1365 /*
1366  * detail_createhow4: print detail information about "how".
1367  */
1368 
1369 static void
1370 detail_createhow4(createhow4 *crtp)
1371 {
1372 	sprintf(get_line(0, 0), "Method = %s",
1373 	    createhow4_name(crtp));
1374 
1375 	switch (crtp->mode) {
1376 	case UNCHECKED4:
1377 	case GUARDED4:
1378 		detail_fattr4(&crtp->createhow4_u.createattrs);
1379 		break;
1380 	case EXCLUSIVE4:
1381 		sprintf(get_line(0, 0), "  Verifier = %s",
1382 		    tohex(crtp->createhow4_u.createverf,
1383 		    NFS4_VERIFIER_SIZE));
1384 		break;
1385 	}
1386 }
1387 
1388 static void
1389 detail_createtype4(createtype4 *crtp)
1390 {
1391 	sprintf(get_line(0, 0), "Type = %s",
1392 	    detail_type_name(crtp->type));
1393 	switch (crtp->type) {
1394 	case NF4LNK:
1395 		sprintf(get_line(0, 0), "Linkdata = %s",
1396 		    utf8localize((utf8string *)&crtp->createtype4_u.linkdata));
1397 		break;
1398 	case NF4BLK:
1399 	case NF4CHR:
1400 		sprintf(get_line(0, 0), "Specdata1 = %04x Specdata2 = %04x",
1401 		    crtp->createtype4_u.devdata.specdata1,
1402 		    crtp->createtype4_u.devdata.specdata2);
1403 		break;
1404 	default:
1405 		break;
1406 	}
1407 }
1408 
1409 static void
1410 sumarg_access(char *buf, size_t buflen, void *obj)
1411 {
1412 	ACCESS4args *args = (ACCESS4args *)obj;
1413 
1414 	sum_access4(buf, buflen, args->access);
1415 }
1416 
1417 static void
1418 dtlarg_access(void *obj)
1419 {
1420 	ACCESS4args *args = (ACCESS4args *)obj;
1421 
1422 	detail_access4("Access bits", args->access);
1423 }
1424 
1425 static void
1426 sumarg_close(char *buf, size_t buflen, void *obj)
1427 {
1428 	CLOSE4args *args = (CLOSE4args *)obj;
1429 
1430 	snprintf(buf, buflen, "SQ=%u %s",
1431 	    args->seqid, sum_open_stateid(&args->open_stateid));
1432 }
1433 
1434 static void
1435 dtlarg_close(void *obj)
1436 {
1437 	CLOSE4args *args = (CLOSE4args *)obj;
1438 
1439 	detail_open_stateid(&args->open_stateid);
1440 	sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
1441 }
1442 
1443 static void
1444 sumarg_commit(char *buf, size_t buflen, void *obj)
1445 {
1446 	COMMIT4args *args = (COMMIT4args *)obj;
1447 
1448 	snprintf(buf, buflen, "at %llu for %u ", args->offset,
1449 	    args->count);
1450 }
1451 
1452 static void
1453 dtlarg_commit(void *obj)
1454 {
1455 	COMMIT4args *args = (COMMIT4args *)obj;
1456 
1457 	sprintf(get_line(0, 0), "Offset = %llu", args->offset);
1458 	sprintf(get_line(0, 0), "Count = %u", args->count);
1459 }
1460 
1461 static void
1462 sumarg_compnt(char *buf, size_t buflen, void *obj)
1463 {
1464 	component4 *comp = (component4 *)obj;
1465 
1466 	snprintf(buf, buflen, "%s", component_name(comp));
1467 }
1468 
1469 static void
1470 dtlarg_compnt(void *obj)
1471 {
1472 	component4 *comp = (component4 *)obj;
1473 
1474 	sprintf(get_line(0, 0), "Name = %s", component_name(comp));
1475 }
1476 
1477 static void
1478 sumarg_create(char *buf, size_t buflen, void *obj)
1479 {
1480 	CREATE4args *args = (CREATE4args *)obj;
1481 
1482 	snprintf(buf, buflen, "%s %s ", component_name(&args->objname),
1483 	    sum_type_name(args->objtype.type));
1484 }
1485 
1486 static void
1487 dtlarg_create(void *obj)
1488 {
1489 	CREATE4args *args = (CREATE4args *)obj;
1490 
1491 	sprintf(get_line(0, 0), "Name = %s", component_name(&args->objname));
1492 	detail_createtype4(&args->objtype);
1493 	detail_fattr4(&args->createattrs);
1494 }
1495 
1496 static void
1497 sumarg_delprge(char *buf, size_t buflen, void *obj)
1498 {
1499 	DELEGPURGE4args *args = (DELEGPURGE4args *)obj;
1500 
1501 	snprintf(buf, buflen, "%s", sum_clientid(args->clientid));
1502 }
1503 
1504 static void
1505 dtlarg_delprge(void *obj)
1506 {
1507 	DELEGPURGE4args *args = (DELEGPURGE4args *)obj;
1508 
1509 	detail_clientid(args->clientid);
1510 }
1511 
1512 static void
1513 sumarg_delret(char *buf, size_t buflen, void *obj)
1514 {
1515 	DELEGRETURN4args *args = (DELEGRETURN4args *)obj;
1516 
1517 	snprintf(buf, buflen, "%s", sum_deleg_stateid(&args->deleg_stateid));
1518 }
1519 
1520 static void
1521 dtlarg_delret(void *obj)
1522 {
1523 	DELEGRETURN4args *args = (DELEGRETURN4args *)obj;
1524 
1525 	detail_deleg_stateid(&args->deleg_stateid);
1526 }
1527 
1528 static void
1529 sumarg_getattr(char *buf, size_t buflen, void *obj)
1530 {
1531 	GETATTR4args *args = (GETATTR4args *)obj;
1532 
1533 	sum_attr_bitmap(buf, buflen, &args->attr_request);
1534 }
1535 
1536 static void
1537 dtlarg_getattr(void *obj)
1538 {
1539 	GETATTR4args *args = (GETATTR4args *)obj;
1540 
1541 	detail_attr_bitmap("", &args->attr_request, NULL);
1542 }
1543 
1544 static void
1545 sumarg_cb_getattr(char *buf, size_t buflen, void *obj)
1546 {
1547 	CB_GETATTR4args *args = (CB_GETATTR4args *)obj;
1548 	char *bp = buf;
1549 
1550 	snprintf(bp, buflen, "%s ", sum_fh4(&args->fh));
1551 	bp += strlen(bp);
1552 	sum_attr_bitmap(bp, buflen - (bp - buf), &args->attr_request);
1553 }
1554 
1555 static void
1556 dtlarg_cb_getattr(void *obj)
1557 {
1558 	CB_GETATTR4args *args = (CB_GETATTR4args *)obj;
1559 
1560 	detail_fh4(&args->fh);
1561 	detail_attr_bitmap("", &args->attr_request, NULL);
1562 }
1563 
1564 static void
1565 sumarg_cb_recall(char *buf, size_t buflen, void *obj)
1566 {
1567 	CB_RECALL4args *args = (CB_RECALL4args *)obj;
1568 	char *bp = buf;
1569 
1570 	snprintf(bp, buflen, "%s %s TR=%s", sum_fh4(&args->fh),
1571 	    sum_stateid(&args->stateid), args->truncate ? "T" : "F");
1572 }
1573 
1574 static void
1575 dtlarg_cb_recall(void *obj)
1576 {
1577 	CB_RECALL4args *args = (CB_RECALL4args *)obj;
1578 
1579 	detail_fh4(&args->fh);
1580 	detail_stateid(&args->stateid);
1581 	sprintf(get_line(0, 0), "Truncate = %s",
1582 	    args->truncate ? "True" : "False");
1583 }
1584 
1585 
1586 /*
1587  * name openhow seqid claim access deny owner
1588  */
1589 static void
1590 sumarg_open(char *buf, size_t buflen, void *obj)
1591 {
1592 	OPEN4args *args = (OPEN4args *)obj;
1593 	char *bp = buf;
1594 	int blen = buflen, len;
1595 
1596 	sum_name(bp, buflen, &args->claim);
1597 	bp += (len = strlen(bp));
1598 	blen -= len;
1599 
1600 	sum_openflag(bp, blen, &args->openhow);
1601 	bp += (len = strlen(bp));
1602 	blen -= len;
1603 
1604 	snprintf(bp, blen, " SQ=%u", args->seqid);
1605 	bp += (len = strlen(bp));
1606 	blen -= len;
1607 
1608 	sum_claim(bp, blen, &args->claim);
1609 	bp += (len = strlen(bp));
1610 	blen -= len;
1611 
1612 	snprintf(bp, blen, " AC=%s DN=%s OO=%04X",
1613 	    sum_open_share_access(args->share_access),
1614 	    sum_open_share_deny(args->share_deny),
1615 	    owner_hash(&args->owner.owner));
1616 }
1617 
1618 static void
1619 dtlarg_open(void *obj)
1620 {
1621 	OPEN4args *args = (OPEN4args *)obj;
1622 
1623 	detail_claim(&args->claim);
1624 	detail_openflag(&args->openhow);
1625 	detail_open_owner(&args->owner);
1626 	sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
1627 	sprintf(get_line(0, 0), "Access = 0x%x (%s)",
1628 	    args->share_access, sum_open_share_access(args->share_access));
1629 	sprintf(get_line(0, 0), "Deny   = 0x%x (%s)",
1630 	    args->share_deny, sum_open_share_access(args->share_deny));
1631 }
1632 
1633 static void
1634 sumarg_openattr(char *buf, size_t buflen, void *obj)
1635 {
1636 	OPENATTR4args *args = (OPENATTR4args *)obj;
1637 
1638 	snprintf(buf, buflen, "CD=%s",
1639 	    args->createdir ? "T" : "F");
1640 }
1641 
1642 static void
1643 dtlarg_openattr(void *obj)
1644 {
1645 	OPENATTR4args *args = (OPENATTR4args *)obj;
1646 
1647 	sprintf(get_line(0, 0), "CreateDir = %s",
1648 	    args->createdir ? "True" : "False");
1649 }
1650 
1651 static void
1652 sumarg_open_confirm(char *buf, size_t buflen, void *obj)
1653 {
1654 	char *bp = buf;
1655 	OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj;
1656 
1657 	snprintf(bp, buflen, "SQ=%u %s", args->seqid,
1658 	    sum_open_stateid(&args->open_stateid));
1659 }
1660 
1661 static void
1662 dtlarg_open_confirm(void *obj)
1663 {
1664 	OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj;
1665 
1666 	sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
1667 	detail_open_stateid(&args->open_stateid);
1668 }
1669 
1670 static void
1671 sumarg_open_downgrd(char *buf, size_t buflen, void *obj)
1672 {
1673 	OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj;
1674 
1675 	snprintf(buf, buflen, "SQ=%u %s AC=%s DN=%s",
1676 	    args->seqid, sum_open_stateid(&args->open_stateid),
1677 	    sum_open_share_access(args->share_access),
1678 	    sum_open_share_deny(args->share_deny));
1679 }
1680 
1681 static void
1682 dtlarg_open_downgrd(void *obj)
1683 {
1684 	OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj;
1685 
1686 	sprintf(get_line(0, 0), "Open Sequence ID = %u", args->seqid);
1687 	detail_open_stateid(&args->open_stateid);
1688 	sprintf(get_line(0, 0), "Access = 0x%x (%s)",
1689 	    args->share_access, sum_open_share_access(args->share_access));
1690 	sprintf(get_line(0, 0), "Deny   = 0x%x (%s)",
1691 	    args->share_deny, sum_open_share_access(args->share_deny));
1692 }
1693 
1694 static void
1695 sumarg_putfh(char *buf, size_t buflen, void *obj)
1696 {
1697 	PUTFH4args *args = (PUTFH4args *)obj;
1698 
1699 	snprintf(buf, buflen, "%s", sum_fh4(&args->object));
1700 }
1701 
1702 static void
1703 dtlarg_putfh(void *obj)
1704 {
1705 	PUTFH4args *args = (PUTFH4args *)obj;
1706 
1707 	detail_fh4(&args->object);
1708 }
1709 
1710 static void
1711 sumarg_link(char *buf, size_t buflen, void *obj)
1712 {
1713 	LINK4args *args = (LINK4args *)obj;
1714 
1715 	snprintf(buf, buflen, "%s", component_name(&args->newname));
1716 }
1717 
1718 static void
1719 dtlarg_link(void *obj)
1720 {
1721 	LINK4args *args = (LINK4args *)obj;
1722 
1723 	sprintf(get_line(0, 0), "New name = %s",
1724 	    component_name(&args->newname));
1725 }
1726 
1727 static void
1728 sum_open_to_lock_owner(char *buf, int buflen, open_to_lock_owner4 *own)
1729 {
1730 	snprintf(buf, buflen, " OSQ=%u %s LSQ=%u LO=%04X", own->open_seqid,
1731 	    sum_open_stateid(&own->open_stateid), own->lock_seqid,
1732 	    owner_hash(&own->lock_owner.owner));
1733 }
1734 
1735 static void
1736 sum_exist_lock_owner(char *buf, int buflen, exist_lock_owner4 *own)
1737 {
1738 	snprintf(buf, buflen, " LSQ=%u %s", own->lock_seqid,
1739 	    sum_lock_stateid(&own->lock_stateid));
1740 }
1741 
1742 static void
1743 sum_locker(char *buf, size_t len, locker4 *lk)
1744 {
1745 	if (lk->new_lock_owner == TRUE)
1746 		sum_open_to_lock_owner(buf, len, &lk->locker4_u.open_owner);
1747 	else
1748 		sum_exist_lock_owner(buf, len, &lk->locker4_u.lock_owner);
1749 }
1750 
1751 static char *
1752 sum_lock_type_name(enum nfs_lock_type4 type)
1753 {
1754 	char *result;
1755 
1756 	switch (type) {
1757 	case READ_LT:
1758 		result = "RD";
1759 		break;
1760 	case WRITE_LT:
1761 		result = "WR";
1762 		break;
1763 	case READW_LT:
1764 		result = "RDW";
1765 		break;
1766 	case WRITEW_LT:
1767 		result = "WRW";
1768 		break;
1769 	default:
1770 		result = "?";
1771 		break;
1772 	}
1773 
1774 	return (result);
1775 }
1776 
1777 static void
1778 sumarg_lock(char *buf, size_t buflen, void *obj)
1779 {
1780 	LOCK4args *args = (LOCK4args *)obj;
1781 	char *bp = buf;
1782 
1783 	snprintf(buf, buflen, "%s%s%llu:%llu",
1784 	    sum_lock_type_name(args->locktype),
1785 	    args->reclaim ? " reclaim " : " ",
1786 	    args->offset, args->length);
1787 
1788 	bp += strlen(buf);
1789 	sum_locker(bp, buflen - (bp - buf), &args->locker);
1790 }
1791 
1792 static void
1793 detail_open_to_lock_owner(open_to_lock_owner4 *own)
1794 {
1795 	sprintf(get_line(0, 0), "Open Sequence ID = %u", own->open_seqid);
1796 	detail_open_stateid(&own->open_stateid);
1797 	sprintf(get_line(0, 0), "Lock Sequence ID = %u", own->lock_seqid);
1798 	detail_lock_owner(&own->lock_owner);
1799 }
1800 
1801 static void
1802 detail_exist_lock_owner(exist_lock_owner4 *own)
1803 {
1804 	detail_lock_stateid(&own->lock_stateid);
1805 	sprintf(get_line(0, 0), "Lock Sequence ID = %u", own->lock_seqid);
1806 }
1807 
1808 static void
1809 detail_locker(locker4 *lk)
1810 {
1811 	if (lk->new_lock_owner == TRUE)
1812 		detail_open_to_lock_owner(&lk->locker4_u.open_owner);
1813 	else
1814 		detail_exist_lock_owner(&lk->locker4_u.lock_owner);
1815 }
1816 
1817 static void
1818 dtlarg_lock(void *obj)
1819 {
1820 	LOCK4args *args = (LOCK4args *)obj;
1821 
1822 	sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
1823 	sprintf(get_line(0, 0), "Reclaim = %s",
1824 	    args->reclaim ? "TRUE" : "FALSE");
1825 	sprintf(get_line(0, 0), "Offset = %llu", args->offset);
1826 	sprintf(get_line(0, 0), "Length = %llu", args->length);
1827 	detail_locker(&args->locker);
1828 }
1829 
1830 static void
1831 sumarg_lockt(char *buf, size_t buflen, void *obj)
1832 {
1833 	LOCKT4args *args = (LOCKT4args *)obj;
1834 
1835 	snprintf(buf, buflen, "%s %llu:%llu",
1836 	    sum_lock_type_name(args->locktype),
1837 	    args->offset, args->length);
1838 }
1839 
1840 static void
1841 dtlarg_lockt(void *obj)
1842 {
1843 	LOCKT4args *args = (LOCKT4args *)obj;
1844 
1845 	sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
1846 	detail_lock_owner(&args->owner);
1847 	sprintf(get_line(0, 0), "Offset = %llu", args->offset);
1848 	sprintf(get_line(0, 0), "Length = %llu", args->length);
1849 }
1850 
1851 static void
1852 sumarg_locku(char *buf, size_t buflen, void *obj)
1853 {
1854 	LOCKU4args *args = (LOCKU4args *)obj;
1855 
1856 	snprintf(buf, buflen, "%llu:%llu LSQ=%u %s",
1857 	    args->offset, args->length, args->seqid,
1858 	    sum_lock_stateid(&args->lock_stateid));
1859 }
1860 
1861 
1862 static void
1863 dtlarg_locku(void *obj)
1864 {
1865 	LOCKU4args *args = (LOCKU4args *)obj;
1866 
1867 	sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype));
1868 	sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid);
1869 	detail_lock_stateid(&args->lock_stateid);
1870 	sprintf(get_line(0, 0), "Offset = %llu", args->offset);
1871 	sprintf(get_line(0, 0), "Length = %llu", args->length);
1872 }
1873 
1874 static void
1875 sumarg_lookup(char *buf, size_t buflen, void *obj)
1876 {
1877 	LOOKUP4args *args = (LOOKUP4args *)obj;
1878 
1879 	sum_compname4(buf, buflen, &args->objname);
1880 }
1881 
1882 static void
1883 dtlarg_lookup(void *obj)
1884 {
1885 	LOOKUP4args *args = (LOOKUP4args *)obj;
1886 
1887 	detail_compname4(&args->objname);
1888 }
1889 
1890 static void
1891 sumarg_read(char *buf, size_t buflen, void *obj)
1892 {
1893 	READ4args *args = (READ4args *)obj;
1894 
1895 	snprintf(buf, buflen, "%s at %llu for %u",
1896 	    sum_stateid(&args->stateid), args->offset, args->count);
1897 }
1898 
1899 static void
1900 dtlarg_read(void *obj)
1901 {
1902 	READ4args *args = (READ4args *)obj;
1903 
1904 	sprintf(get_line(0, 0), "Offset = %llu", args->offset);
1905 	sprintf(get_line(0, 0), "Count = %u", args->count);
1906 	detail_stateid(&args->stateid);
1907 }
1908 
1909 static void
1910 sumarg_readdir(char *buf, size_t buflen, void *obj)
1911 {
1912 	READDIR4args *args = (READDIR4args *)obj;
1913 
1914 	snprintf(buf, buflen, "Cookie=%llu (%s) for %u/%u",
1915 	    args->cookie, tohex(args->cookieverf, NFS4_VERIFIER_SIZE),
1916 	    args->dircount, args->maxcount);
1917 }
1918 
1919 static void
1920 dtlarg_readdir(void *obj)
1921 {
1922 	READDIR4args *args = (READDIR4args *)obj;
1923 
1924 	sprintf(get_line(0, 0), "Cookie = %llu", args->cookie);
1925 	sprintf(get_line(0, 0), "Verifier = %s",
1926 	    tohex(args->cookieverf, NFS4_VERIFIER_SIZE));
1927 	sprintf(get_line(0, 0), "Dircount = %u", args->dircount);
1928 	sprintf(get_line(0, 0), "Maxcount = %u", args->maxcount);
1929 	detail_attr_bitmap("", &args->attr_request, NULL);
1930 }
1931 
1932 static void
1933 dtlarg_release_lkown(void *obj)
1934 {
1935 	RELEASE_LOCKOWNER4args *args = (RELEASE_LOCKOWNER4args *)obj;
1936 
1937 	detail_lock_owner(&args->lock_owner);
1938 }
1939 
1940 static void
1941 sumarg_release_lkown(char *buf, size_t buflen, void *obj)
1942 {
1943 	RELEASE_LOCKOWNER4args *args = (RELEASE_LOCKOWNER4args *)obj;
1944 
1945 	snprintf(buf, buflen, "LO=%04X", owner_hash(&args->lock_owner.owner));
1946 }
1947 
1948 static void
1949 sumarg_rename(char *buf, size_t buflen, void *obj)
1950 {
1951 	RENAME4args *args = (RENAME4args *)obj;
1952 
1953 	snprintf(buf, buflen, "%s to %s",
1954 	    component_name(&args->oldname),
1955 	    component_name(&args->newname));
1956 }
1957 
1958 static void
1959 dtlarg_rename(void *obj)
1960 {
1961 	RENAME4args *args = (RENAME4args *)obj;
1962 
1963 	sprintf(get_line(0, 0), "Old name = %s",
1964 	    component_name(&args->oldname));
1965 	sprintf(get_line(0, 0), "New name = %s",
1966 	    component_name(&args->newname));
1967 }
1968 
1969 static void
1970 sumarg_renew(char *buf, size_t buflen, void *obj)
1971 {
1972 	RENEW4args *args = (RENEW4args *)obj;
1973 
1974 	snprintf(buf, buflen, "%s", sum_clientid(args->clientid));
1975 }
1976 static void
1977 dtlarg_renew(void *obj)
1978 {
1979 	RENEW4args *args = (RENEW4args *)obj;
1980 
1981 	detail_clientid(args->clientid);
1982 }
1983 
1984 static void
1985 sumarg_secinfo(char *buf, size_t buflen, void *obj)
1986 {
1987 	SECINFO4args *args = (SECINFO4args *)obj;
1988 
1989 	snprintf(buf, buflen, "%s",
1990 	    component_name(&args->name));
1991 }
1992 
1993 static void
1994 dtlarg_secinfo(void *obj)
1995 {
1996 	SECINFO4args *args = (SECINFO4args *)obj;
1997 
1998 	sprintf(get_line(0, 0), "Name = %s",
1999 	    component_name(&args->name));
2000 }
2001 
2002 static void
2003 sumarg_setattr(char *buf, size_t buflen, void *obj)
2004 {
2005 	SETATTR4args *args = (SETATTR4args *)obj;
2006 
2007 	snprintf(buf, buflen, "%s", sum_stateid(&args->stateid));
2008 }
2009 
2010 static void
2011 dtlarg_setattr(void *obj)
2012 {
2013 	SETATTR4args *args = (SETATTR4args *)obj;
2014 
2015 	detail_stateid(&args->stateid);
2016 	detail_fattr4(&args->obj_attributes);
2017 }
2018 
2019 static void
2020 sumarg_setclid(char *buf, size_t buflen, void *obj)
2021 {
2022 	SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
2023 
2024 	snprintf(buf, buflen, "Prog=%u ID=%s Addr=%s CBID=%u",
2025 	    args->callback.cb_program,
2026 	    args->callback.cb_location.r_netid,
2027 	    args->callback.cb_location.r_addr, args->callback_ident);
2028 }
2029 
2030 static void
2031 dtlarg_setclid(void *obj)
2032 {
2033 	SETCLIENTID4args *args = (SETCLIENTID4args *)obj;
2034 
2035 	sprintf(get_line(0, 0), "Verifier=%s",
2036 	    tohex(args->client.verifier, NFS4_VERIFIER_SIZE));
2037 	sprintf(get_line(0, 0), "ID = (%d) %s",
2038 	    args->client.id.id_len,
2039 	    tohex(args->client.id.id_val, args->client.id.id_len));
2040 
2041 	sprintf(get_line(0, 0), "Callback Program = %u",
2042 	    args->callback.cb_program);
2043 	sprintf(get_line(0, 0), "Callback Net ID = %s",
2044 	    args->callback.cb_location.r_netid);
2045 	sprintf(get_line(0, 0), "Callback Addr = %s",
2046 	    args->callback.cb_location.r_addr);
2047 	sprintf(get_line(0, 0), "Callback Ident = %u", args->callback_ident);
2048 }
2049 
2050 static void
2051 sumarg_setclid_cfm(char *buf, size_t buflen, void *obj)
2052 {
2053 	SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj;
2054 
2055 	snprintf(buf, buflen, "%s CFV=%s", sum_clientid(args->clientid),
2056 	    tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
2057 }
2058 
2059 static void
2060 dtlarg_setclid_cfm(void *obj)
2061 {
2062 	SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj;
2063 
2064 	detail_clientid(args->clientid);
2065 	sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
2066 	    tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE));
2067 }
2068 
2069 
2070 static void
2071 dtlarg_verify(void *obj)
2072 {
2073 	NVERIFY4args *args = (NVERIFY4args *)obj;
2074 
2075 	detail_fattr4(&args->obj_attributes);
2076 }
2077 
2078 static void
2079 sumarg_write(char *buf, size_t buflen, void *obj)
2080 {
2081 	WRITE4args *args = (WRITE4args *)obj;
2082 
2083 	snprintf(buf, buflen, "%s at %llu for %u",
2084 	    sum_stateid(&args->stateid), args->offset, args->data.data_len);
2085 }
2086 
2087 static void
2088 dtlarg_write(void *obj)
2089 {
2090 	WRITE4args *args = (WRITE4args *)obj;
2091 
2092 	sprintf(get_line(0, 0), "Offset = %llu", args->offset);
2093 	sprintf(get_line(0, 0), "Count = %u", args->data.data_len);
2094 	sprintf(get_line(0, 0), "Stable = %s", stable_how4_name(args->stable));
2095 	detail_stateid(&args->stateid);
2096 }
2097 
2098 static char *
2099 sum_fh4(nfs_fh4 *fh)
2100 {
2101 	static char buf[20];
2102 
2103 	sprintf(buf, "FH=%04X", fh4_hash(fh));
2104 
2105 	return (buf);
2106 }
2107 
2108 static void
2109 detail_fh4(nfs_fh4 *fh)
2110 {
2111 	int i;
2112 	uchar_t *cp;
2113 	char *bufp;
2114 
2115 	sprintf(get_line(0, 0), "File handle = [%04X]", fh4_hash(fh));
2116 	bufp = get_line(0, 0);
2117 	sprintf(bufp, "(%d) ", fh->nfs_fh4_len);
2118 	bufp += strlen(bufp);
2119 	/* XXX use tohex()? */
2120 	for (i = 0, cp = (uchar_t *)fh->nfs_fh4_val;
2121 	    i < fh->nfs_fh4_len;
2122 	    i++, cp++) {
2123 		if (i != 0 && i % 32 == 0)
2124 			bufp = get_line(0, 0);
2125 		sprintf(bufp, "%02x", *cp);
2126 		bufp += strlen(bufp);
2127 	}
2128 }
2129 
2130 static void
2131 detail_fattr4(fattr4 *attrp)
2132 {
2133 	unpkd_attrmap_t provided;
2134 	uint_t attrnum;
2135 	XDR attrxdr;
2136 	jmp_buf old_errbuf;
2137 
2138 	xdrmem_create(&attrxdr, attrp->attr_vals.attrlist4_val,
2139 	    attrp->attr_vals.attrlist4_len, XDR_DECODE);
2140 
2141 	bcopy(xdr_err, old_errbuf, sizeof (old_errbuf));
2142 	if (setjmp(xdr_err)) {
2143 		sprintf(get_line(0, 0), "<attr_vals too short>");
2144 		goto done;
2145 	}
2146 
2147 	detail_attr_bitmap("", &attrp->attrmask, &provided);
2148 	for (attrnum = 0; attrnum < MAX_ATTRIBUTES; attrnum++) {
2149 		if (provided.map[attrnum]) {
2150 			attr_info[attrnum].prt_details(&attrxdr);
2151 		}
2152 	}
2153 
2154 done:
2155 	bcopy(old_errbuf, xdr_err, sizeof (old_errbuf));
2156 }
2157 
2158 static void
2159 sum_attr_bitmap(char *buf, size_t buflen, bitmap4 *mapp)
2160 {
2161 	uint_t num_words;
2162 	char *bp;
2163 	size_t curlen, remaining;
2164 
2165 	buf[0] = '\0';
2166 	for (num_words = 0; num_words < mapp->bitmap4_len; num_words++) {
2167 		curlen = strlen(buf);
2168 		if (curlen + sizeof ("<Too Long>") >= buflen) {
2169 			strcpy(buf + buflen - sizeof ("<Too Long>"),
2170 			    "<Too Long>");
2171 			return;
2172 		}
2173 		bp = buf + curlen;
2174 		remaining = buflen - curlen;
2175 		snprintf(bp, remaining,
2176 		    num_words == 0 ? "%x" : " %x",
2177 		    mapp->bitmap4_val[num_words]);
2178 	}
2179 }
2180 
2181 /*
2182  * Print detail information for the given attribute bitmap, and fill in the
2183  * unpacked version of the map if "unpacked" is non-null.  Returns the
2184  * number of bytes in the bitmap.  "prefix" is an initial string that is
2185  * printed at the front of each line.
2186  */
2187 
2188 static void
2189 detail_attr_bitmap(char *prefix, bitmap4 *bitp, unpkd_attrmap_t *unpacked)
2190 {
2191 	uint_t num_words;
2192 	uint32_t *wp;
2193 	uint_t byte_num;
2194 
2195 	if (unpacked != NULL)
2196 		memset(unpacked, 0, sizeof (unpkd_attrmap_t));
2197 
2198 	/*
2199 	 * Break the bitmap into octets, then print in hex and
2200 	 * symbolically.
2201 	 */
2202 
2203 	for (num_words = 0, wp = bitp->bitmap4_val;
2204 	    num_words < bitp->bitmap4_len;
2205 	    num_words++, wp++) {
2206 		for (byte_num = 0; byte_num < 4; byte_num++) {
2207 			uchar_t val = (*wp) >> (byte_num * 8);
2208 			char *buf = get_line(0, 0);
2209 			uint_t attrnum;
2210 			int bit;
2211 
2212 			sprintf(buf, "%s  0x%02x  ", prefix, val);
2213 			attrnum = num_words * 32 + byte_num * 8;
2214 			for (bit = 7; bit >= 0; bit--) {
2215 				if (val & (1 << bit)) {
2216 					strcat(buf, " ");
2217 					strcat(buf,
2218 					    attr_name(attrnum + bit));
2219 					if (unpacked != NULL)
2220 						unpacked->map[attrnum + bit] =
2221 						    1;
2222 				}
2223 			}
2224 		}
2225 	}
2226 }
2227 
2228 /*
2229  * Format the summary line results from a COMPOUND4 call.
2230  */
2231 
2232 static void
2233 sum_comp4res(char *line, char *(*sumres_fn)(void))
2234 {
2235 	nfsstat4 status;
2236 	static utf8string tag;
2237 
2238 	status = getxdr_long();
2239 	if (!xdr_utf8string(&xdrm, &tag))
2240 		longjmp(xdr_err, 1);
2241 
2242 	sprintf(line, "(%.20s) %s %s", utf8localize(&tag),
2243 	    status_name(status), sumres_fn());
2244 
2245 	xdr_free(xdr_utf8string, (char *)&tag);
2246 }
2247 
2248 
2249 /*
2250  * Return a set of summary strings for the result data that's next in the
2251  * XDR stream, up to SUM_COMPND_MAX characters.  If the strings don't fit,
2252  * include a "..." at the end of the string.
2253  */
2254 
2255 static char *
2256 sum_compound4res(void)
2257 {
2258 	static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
2259 	int numres;
2260 	const size_t buflen = sizeof (buf);
2261 	char *bp;
2262 	nfs_resop4 one_res;
2263 
2264 	buf[0] = '\0';
2265 	if (setjmp(xdr_err)) {
2266 		bp = buf + strlen(buf);
2267 		snprintf(bp, buflen - (bp - buf),
2268 		    nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag);
2269 		return (buf);
2270 	}
2271 
2272 	numres = getxdr_long();
2273 	bp = buf;
2274 	while (numres-- > 0) {
2275 		char *result;
2276 
2277 		bzero(&one_res, sizeof (one_res));
2278 
2279 		if (!xdr_nfs_resop4(&xdrm, &one_res)) {
2280 			xdr_free(xdr_nfs_resop4, (char *)&one_res);
2281 			longjmp(xdr_err, 1);
2282 		}
2283 
2284 		snprintf(bp, buflen - (bp - buf), "%s ",
2285 		    opcode_name(one_res.resop));
2286 		bp += strlen(bp);
2287 
2288 		result = sum_result(&one_res);
2289 		if (strlen(result) > 0) {
2290 			snprintf(bp, buflen - (bp - buf), "%s ", result);
2291 			bp += strlen(bp);
2292 		}
2293 
2294 		/* nfs4_skip_bytes set by xdr_nfs4_argop4() */
2295 		if (nfs4_skip_bytes != 0)
2296 			nfs4_xdr_skip(nfs4_skip_bytes);
2297 
2298 		xdr_free(xdr_nfs_resop4, (char *)&one_res);
2299 		/* add "..." if past the "end" of the buffer */
2300 		if (bp - buf > SUM_COMPND_MAX) {
2301 			strcpy(buf + SUM_COMPND_MAX - strlen("..."),
2302 			    "...");
2303 			break;
2304 		}
2305 	}
2306 
2307 	return (buf);
2308 }
2309 
2310 
2311 /*
2312  * Return a set of summary strings for the result data that's next in the
2313  * XDR stream, up to SUM_COMPND_MAX characters.  If the strings don't fit,
2314  * include a "..." at the end of the string.
2315  */
2316 
2317 static char *
2318 sum_cb_compound4res(void)
2319 {
2320 	static char buf[SUM_COMPND_MAX + 2]; /* 1 for null, 1 for overflow */
2321 	int numres;
2322 	const size_t buflen = sizeof (buf);
2323 	char *bp;
2324 	nfs_cb_resop4 one_res;
2325 
2326 	buf[0] = '\0';
2327 	if (setjmp(xdr_err)) {
2328 		bp = buf + strlen(buf);
2329 		snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented"
2330 		    " RPC>");
2331 		return (buf);
2332 	}
2333 
2334 	numres = getxdr_long();
2335 	bp = buf;
2336 	while (numres-- > 0) {
2337 		bzero(&one_res, sizeof (one_res));
2338 		if (!xdr_nfs_cb_resop4(&xdrm, &one_res)) {
2339 			xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
2340 			longjmp(xdr_err, 1);
2341 		}
2342 		snprintf(bp, buflen - (bp - buf), "%s %s ",
2343 		    cb_opcode_name(one_res.resop),
2344 		    sum_cb_result(&one_res));
2345 		bp += strlen(bp);
2346 
2347 		xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
2348 
2349 		/* add "..." if past the "end" of the buffer */
2350 		if (bp - buf > SUM_COMPND_MAX) {
2351 			strcpy(buf + SUM_COMPND_MAX - strlen("..."),
2352 			    "...");
2353 			break;
2354 		}
2355 	}
2356 
2357 	return (buf);
2358 }
2359 
2360 
2361 /*
2362  * Return the summarized results for the given resultdata.
2363  */
2364 
2365 static char *
2366 sum_result(nfs_resop4 *resp)
2367 {
2368 	static char buf[1024];
2369 	void (*fmtproc)(char *, size_t, void *);
2370 
2371 	buf[0] = '\0';
2372 	if (resp->resop < num_opcodes)
2373 		fmtproc = opcode_info[resp->resop].sumres;
2374 	else if (resp->resop == OP_ILLEGAL)
2375 		fmtproc = sum_nfsstat4;
2376 	else
2377 		fmtproc = NULL;
2378 
2379 	if (fmtproc != NULL)
2380 		fmtproc(buf, sizeof (buf), &resp->nfs_resop4_u);
2381 
2382 	return (buf);
2383 }
2384 
2385 /*
2386  * Return the summarized results for the given resultdata.
2387  */
2388 
2389 static char *
2390 sum_cb_result(nfs_cb_resop4 *resp)
2391 {
2392 	static char buf[1024];
2393 	void (*fmtproc)(char *, size_t, void *);
2394 
2395 	buf[0] = '\0';
2396 	if (resp->resop < cb_num_opcodes)
2397 		fmtproc = cb_opcode_info[resp->resop].sumres;
2398 	else if (resp->resop == OP_CB_ILLEGAL)
2399 		fmtproc = sum_nfsstat4;
2400 	else
2401 		fmtproc = NULL;
2402 
2403 	if (fmtproc != NULL)
2404 		fmtproc(buf, sizeof (buf), &resp->nfs_cb_resop4_u);
2405 
2406 	return (buf);
2407 }
2408 
2409 
2410 static void
2411 dtl_change_info(char *msg, change_info4 *infop)
2412 {
2413 	sprintf(get_line(0, 0), "%s:", msg);
2414 	sprintf(get_line(0, 0), "  Atomic = %s",
2415 	    infop->atomic ? "TRUE" : "FALSE");
2416 	detail_fattr4_change("  Before", infop->before);
2417 	detail_fattr4_change("  After", infop->after);
2418 }
2419 
2420 static void
2421 detail_fattr4_change(char *msg, fattr4_change chg)
2422 {
2423 	sprintf(get_line(0, 0), "%s: 0x%llx", msg, chg);
2424 					/* XXX print as time_t, too? */
2425 }
2426 
2427 static void
2428 sum_nfsstat4(char *buf, size_t buflen, void *obj)
2429 {
2430 	nfsstat4 status = *(nfsstat4 *)obj;
2431 
2432 	strncpy(buf, status_name(status), buflen);
2433 }
2434 
2435 static void
2436 dtl_nfsstat4(void *obj)
2437 {
2438 	nfsstat4 status = *(nfsstat4 *)obj;
2439 
2440 	sprintf(get_line(0, 0), "Status = %d (%s)", status,
2441 	    status_name(status));
2442 }
2443 
2444 static void
2445 sumres_access(char *buf, size_t buflen, void *obj)
2446 {
2447 	ACCESS4res *res = (ACCESS4res *)obj;
2448 	char *bp = buf;
2449 	int len, blen = buflen;
2450 
2451 	strcpy(bp, status_name(res->status));
2452 	if (res->status == NFS4_OK) {
2453 		bp += (len = strlen(bp));
2454 		blen -= len;
2455 
2456 		snprintf(bp, blen, " Supp=");
2457 		bp += (len = strlen(bp));
2458 		blen -= len;
2459 
2460 		sum_access4(bp, blen, res->ACCESS4res_u.resok4.supported);
2461 		bp += (len = strlen(bp));
2462 		blen -= len;
2463 
2464 		snprintf(bp, blen, " Allow=");
2465 		bp += (len = strlen(bp));
2466 		blen -= len;
2467 
2468 		sum_access4(bp, blen, res->ACCESS4res_u.resok4.access);
2469 	}
2470 }
2471 
2472 static void
2473 dtlres_access(void *obj)
2474 {
2475 	ACCESS4res *res = (ACCESS4res *)obj;
2476 
2477 	dtl_nfsstat4(obj);
2478 	if (res->status == NFS4_OK) {
2479 		detail_access4("Supported Attributes",
2480 		    res->ACCESS4res_u.resok4.supported);
2481 		detail_access4("Allowed Attributes",
2482 		    res->ACCESS4res_u.resok4.access);
2483 	}
2484 }
2485 
2486 static void
2487 sumres_close(char *buf, size_t buflen, void *obj)
2488 {
2489 	CLOSE4res *res = (CLOSE4res *)obj;
2490 
2491 	if (res->status == NFS4_OK)
2492 		snprintf(buf, buflen, "%s",
2493 		    sum_open_stateid(&res->CLOSE4res_u.open_stateid));
2494 }
2495 
2496 static void
2497 dtlres_close(void *obj)
2498 {
2499 	CLOSE4res *res = (CLOSE4res *)obj;
2500 
2501 	dtl_nfsstat4(obj);
2502 	if (res->status == NFS4_OK) {
2503 		detail_open_stateid(&res->CLOSE4res_u.open_stateid);
2504 	}
2505 }
2506 
2507 static void
2508 sumres_commit(char *buf, size_t buflen, void *obj)
2509 {
2510 	COMMIT4res *res = (COMMIT4res *)obj;
2511 
2512 	if (res->status == NFS4_OK)
2513 		snprintf(buf, buflen, "Verf=%s",
2514 		    tohex(res->COMMIT4res_u.resok4.writeverf,
2515 		    NFS4_VERIFIER_SIZE));
2516 }
2517 
2518 static void
2519 dtlres_commit(void *obj)
2520 {
2521 	COMMIT4res *res = (COMMIT4res *)obj;
2522 
2523 	dtl_nfsstat4(obj);
2524 	if (res->status == NFS4_OK) {
2525 		sprintf(get_line(0, 0), "Verifier = %s",
2526 		    tohex(res->COMMIT4res_u.resok4.writeverf,
2527 		    NFS4_VERIFIER_SIZE));
2528 	}
2529 }
2530 
2531 static void
2532 dtlres_create(void *obj)
2533 {
2534 	CREATE4res *res = (CREATE4res *)obj;
2535 
2536 	dtl_nfsstat4(obj);
2537 	if (res->status == NFS4_OK) {
2538 		dtl_change_info("Change Information",
2539 		    &res->CREATE4res_u.resok4.cinfo);
2540 		detail_attr_bitmap("", &res->CREATE4res_u.resok4.attrset,
2541 		    NULL);
2542 	}
2543 }
2544 
2545 static void
2546 sumres_getattr(char *buf, size_t buflen, void *obj)
2547 {
2548 	GETATTR4res *res = (GETATTR4res *)obj;
2549 
2550 	strncpy(buf, status_name(res->status), buflen);
2551 }
2552 
2553 static void
2554 dtlres_getattr(void *obj)
2555 {
2556 	GETATTR4res *res = (GETATTR4res *)obj;
2557 
2558 	dtl_nfsstat4(obj);
2559 	if (res->status == NFS4_OK) {
2560 		detail_fattr4(&res->GETATTR4res_u.resok4.obj_attributes);
2561 	}
2562 }
2563 
2564 static void
2565 sumres_cb_getattr(char *buf, size_t buflen, void *obj)
2566 {
2567 	CB_GETATTR4res *res = (CB_GETATTR4res *)obj;
2568 
2569 	strncpy(buf, status_name(res->status), buflen);
2570 }
2571 
2572 static void
2573 dtlres_cb_getattr(void *obj)
2574 {
2575 	CB_GETATTR4res *res = (CB_GETATTR4res *)obj;
2576 
2577 	dtl_nfsstat4(obj);
2578 	if (res->status == NFS4_OK) {
2579 		detail_fattr4(&res->CB_GETATTR4res_u.resok4.obj_attributes);
2580 	}
2581 }
2582 
2583 
2584 static void
2585 sumres_getfh(char *buf, size_t buflen, void *obj)
2586 {
2587 	char *bp;
2588 	GETFH4res *res = (GETFH4res *)obj;
2589 
2590 	strncpy(buf, status_name(res->status), buflen);
2591 	if (res->status == NFS4_OK) {
2592 		bp = buf + strlen(buf);
2593 		snprintf(bp, buflen - (bp - buf), " %s",
2594 		    sum_fh4(&res->GETFH4res_u.resok4.object));
2595 	}
2596 }
2597 
2598 static void
2599 dtlres_getfh(void *obj)
2600 {
2601 	GETFH4res *res = (GETFH4res *)obj;
2602 
2603 	dtl_nfsstat4(obj);
2604 	if (res->status == NFS4_OK) {
2605 		detail_fh4(&res->GETFH4res_u.resok4.object);
2606 	}
2607 }
2608 
2609 static void
2610 dtlres_link(void *obj)
2611 {
2612 	LINK4res *res = (LINK4res *)obj;
2613 
2614 	dtl_nfsstat4(obj);
2615 	if (res->status == NFS4_OK) {
2616 		dtl_change_info("Change Information",
2617 		    &res->LINK4res_u.resok4.cinfo);
2618 	}
2619 }
2620 
2621 static void
2622 sumres_lock(char *buf, size_t buflen, void *obj)
2623 {
2624 	char *bp;
2625 	LOCK4res *res = (LOCK4res *)obj;
2626 
2627 	strncpy(buf, status_name(res->status), buflen);
2628 	if (res->status == NFS4_OK) {
2629 		bp = buf + strlen(buf);
2630 		snprintf(bp, buflen - (bp - buf), " %s",
2631 		    sum_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid));
2632 	}
2633 	if (res->status == NFS4ERR_DENIED) {
2634 		bp = buf + strlen(buf);
2635 		snprintf(bp, buflen - (bp - buf), " %s",
2636 		    sum_lock_denied(&res->LOCK4res_u.denied));
2637 	}
2638 }
2639 
2640 static void
2641 dtlres_lock(void *obj)
2642 {
2643 	LOCK4res *res = (LOCK4res *)obj;
2644 
2645 	dtl_nfsstat4(obj);
2646 	if (res->status == NFS4_OK) {
2647 		detail_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid);
2648 	}
2649 	if (res->status == NFS4ERR_DENIED) {
2650 		detail_lock_denied(&res->LOCK4res_u.denied);
2651 	}
2652 }
2653 
2654 static void
2655 sumres_lockt(char *buf, size_t buflen, void *obj)
2656 {
2657 	char *bp;
2658 	LOCKT4res *res = (LOCKT4res *)obj;
2659 
2660 	strcpy(buf, status_name(res->status));
2661 	if (res->status == NFS4ERR_DENIED) {
2662 		bp = buf + strlen(buf);
2663 		snprintf(bp, buflen - (bp - buf), " %s",
2664 		    sum_lock_denied(&res->LOCKT4res_u.denied));
2665 	}
2666 }
2667 
2668 static void
2669 dtlres_lockt(void *obj)
2670 {
2671 	LOCKT4res *res = (LOCKT4res *)obj;
2672 
2673 	dtl_nfsstat4(obj);
2674 	if (res->status == NFS4ERR_DENIED) {
2675 		detail_lock_denied(&res->LOCKT4res_u.denied);
2676 	}
2677 }
2678 
2679 static void
2680 sumres_locku(char *buf, size_t buflen, void *obj)
2681 {
2682 	char *bp;
2683 	LOCKU4res *res = (LOCKU4res *)obj;
2684 
2685 	strncpy(buf, status_name(res->status), buflen);
2686 	bp = buf + strlen(buf);
2687 	if (res->status == NFS4_OK)
2688 		snprintf(bp, buflen - (bp - buf), " %s",
2689 		    sum_lock_stateid(&res->LOCKU4res_u.lock_stateid));
2690 }
2691 
2692 static void
2693 dtlres_locku(void *obj)
2694 {
2695 	LOCKU4res *res = (LOCKU4res *)obj;
2696 
2697 	dtl_nfsstat4(obj);
2698 	if (res->status == NFS4_OK)
2699 		detail_lock_stateid(&res->LOCKU4res_u.lock_stateid);
2700 }
2701 
2702 static void
2703 sumres_open(char *buf, size_t buflen, void *obj)
2704 {
2705 	char *bp = buf;
2706 	OPEN4res *res = (OPEN4res *)obj;
2707 	uint_t rflags;
2708 	int len, blen = buflen;
2709 
2710 	strncpy(bp, status_name(res->status), blen);
2711 
2712 	if (res->status == NFS4_OK) {
2713 		bp += (len = strlen(bp));
2714 		blen -= len;
2715 
2716 		snprintf(bp, blen, " %s",
2717 		    sum_stateid(&res->OPEN4res_u.resok4.stateid));
2718 		bp += (len = strlen(bp));
2719 		blen -= len;
2720 
2721 		if ((rflags = res->OPEN4res_u.resok4.rflags) != 0) {
2722 			snprintf(bp, blen, "%s", sum_open_rflags(rflags));
2723 			bp += (len = strlen(bp));
2724 			blen -= len;
2725 		}
2726 
2727 		sum_delegation(bp, blen, &res->OPEN4res_u.resok4.delegation);
2728 	}
2729 }
2730 
2731 static void
2732 dtlres_open(void *obj)
2733 {
2734 	OPEN4res *res = (OPEN4res *)obj;
2735 
2736 	dtl_nfsstat4(obj);
2737 	if (res->status == NFS4_OK) {
2738 		detail_stateid(&res->OPEN4res_u.resok4.stateid);
2739 		dtl_change_info("Change Information",
2740 		    &res->OPEN4res_u.resok4.cinfo);
2741 		sprintf(get_line(0, 0), "Flags = 0x%x (%s)",
2742 		    res->OPEN4res_u.resok4.rflags,
2743 		    detail_open_rflags(res->OPEN4res_u.resok4.rflags));
2744 		detail_attr_bitmap("", &res->OPEN4res_u.resok4.attrset,
2745 		    NULL);
2746 		detail_delegation(&res->OPEN4res_u.resok4.delegation);
2747 	}
2748 }
2749 
2750 static void
2751 sumres_open_confirm(char *buf, size_t buflen, void *obj)
2752 {
2753 	char *bp;
2754 	OPEN_CONFIRM4res *res = (OPEN_CONFIRM4res *)obj;
2755 
2756 	strncpy(buf, status_name(res->status), buflen);
2757 	if (res->status == NFS4_OK) {
2758 		bp = buf + strlen(buf);
2759 		snprintf(bp, buflen - (bp - buf), " %s",
2760 		    sum_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
2761 		    open_stateid));
2762 	}
2763 }
2764 
2765 static void
2766 dtlres_open_confirm(void *obj)
2767 {
2768 	OPEN_CONFIRM4res *res = (OPEN_CONFIRM4res *)obj;
2769 
2770 	dtl_nfsstat4(obj);
2771 	if (res->status == NFS4_OK) {
2772 		detail_open_stateid(&res->OPEN_CONFIRM4res_u.resok4.
2773 		    open_stateid);
2774 	}
2775 }
2776 
2777 static void
2778 sumres_open_downgrd(char *buf, size_t buflen, void *obj)
2779 {
2780 	char *bp;
2781 	OPEN_DOWNGRADE4res *res = (OPEN_DOWNGRADE4res *)obj;
2782 
2783 	strncpy(buf, status_name(res->status), buflen);
2784 	if (res->status == NFS4_OK) {
2785 		bp = buf + strlen(buf);
2786 		snprintf(bp, buflen - (bp - buf), " %s",
2787 		    sum_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
2788 		    open_stateid));
2789 	}
2790 }
2791 
2792 static void
2793 dtlres_open_downgrd(void *obj)
2794 {
2795 	OPEN_DOWNGRADE4res *res = (OPEN_DOWNGRADE4res *)obj;
2796 
2797 	dtl_nfsstat4(obj);
2798 	if (res->status == NFS4_OK) {
2799 		detail_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4.
2800 		    open_stateid);
2801 	}
2802 }
2803 
2804 static void
2805 sumres_read(char *buf, size_t buflen, void *obj)
2806 {
2807 	char *bp;
2808 	READ4res *res = (READ4res *)obj;
2809 
2810 	strncpy(buf, status_name(res->status), buflen);
2811 	if (res->status == NFS4_OK) {
2812 		bp = buf + strlen(buf);
2813 		snprintf(bp, buflen - (bp - buf), " (%u bytes) %s",
2814 		    res->READ4res_u.resok4.data.data_len,
2815 		    res->READ4res_u.resok4.eof ? "EOF" : "");
2816 	}
2817 }
2818 
2819 static void
2820 dtlres_read(void *obj)
2821 {
2822 	READ4res *res = (READ4res *)obj;
2823 
2824 	dtl_nfsstat4(obj);
2825 	if (res->status == NFS4_OK) {
2826 		sprintf(get_line(0, 0), "Count = %u bytes read",
2827 		    res->READ4res_u.resok4.data.data_len);
2828 		sprintf(get_line(0, 0), "End of file = %s",
2829 		    res->READ4res_u.resok4.eof ? "TRUE" : "FALSE");
2830 	}
2831 }
2832 
2833 static void
2834 sumres_readdir(char *buf, size_t buflen, void *obj)
2835 {
2836 	char *bp;
2837 	READDIR4res *res = (READDIR4res *)obj;
2838 	int num_entries = 0;
2839 	entry4 *ep;
2840 
2841 	strncpy(buf, status_name(res->status), buflen);
2842 	if (res->status == NFS4_OK) {
2843 		for (ep = res->READDIR4res_u.resok4.reply.entries;
2844 		    ep != NULL;
2845 		    ep = ep->nextentry)
2846 			num_entries++;
2847 		bp = buf + strlen(buf);
2848 		snprintf(bp, buflen - (bp - buf), " %d entries (%s)",
2849 		    num_entries,
2850 		    res->READDIR4res_u.resok4.reply.eof
2851 		    ? "No more" : "More");
2852 	}
2853 }
2854 
2855 static void
2856 dtlres_readdir(void *obj)
2857 {
2858 	READDIR4res *res = (READDIR4res *)obj;
2859 	int num_entries = 0;
2860 	entry4 *ep;
2861 
2862 	dtl_nfsstat4(obj);
2863 	if (res->status == NFS4_OK) {
2864 		for (ep = res->READDIR4res_u.resok4.reply.entries;
2865 		    ep != NULL;
2866 		    ep = ep->nextentry) {
2867 			num_entries++;
2868 			sprintf(get_line(0, 0),
2869 			    "------------------ entry #%d",
2870 			    num_entries);
2871 			sprintf(get_line(0, 0), "Cookie = %llu",
2872 			    ep->cookie);
2873 			sprintf(get_line(0, 0), "Name = %s",
2874 			    component_name(&ep->name));
2875 			detail_fattr4(&ep->attrs);
2876 		}
2877 		if (num_entries == 0)
2878 			sprintf(get_line(0, 0), "(No entries)");
2879 		sprintf(get_line(0, 0), "EOF = %s",
2880 		    res->READDIR4res_u.resok4.reply.eof ? "TRUE" : "FALSE");
2881 		sprintf(get_line(0, 0), "Verifer = %s",
2882 		    tohex(res->READDIR4res_u.resok4.cookieverf,
2883 		    NFS4_VERIFIER_SIZE));
2884 	}
2885 }
2886 
2887 static void
2888 sumres_readlnk(char *buf, size_t buflen, void *obj)
2889 {
2890 	char *bp;
2891 	READLINK4res *res = (READLINK4res *)obj;
2892 
2893 	strncpy(buf, status_name(res->status), buflen);
2894 	if (res->status == NFS4_OK) {
2895 		bp = buf + strlen(buf);
2896 		snprintf(bp, buflen - (bp - buf), " %s",
2897 		    linktext_name(&res->READLINK4res_u.resok4.link));
2898 	}
2899 }
2900 
2901 static void
2902 dtlres_readlnk(void *obj)
2903 {
2904 	READLINK4res *res = (READLINK4res *)obj;
2905 
2906 	dtl_nfsstat4(obj);
2907 	if (res->status == NFS4_OK) {
2908 		sprintf(get_line(0, 0), "Link = %s",
2909 		    linktext_name(&res->READLINK4res_u.resok4.link));
2910 	}
2911 }
2912 
2913 static void
2914 dtlres_remove(void *obj)
2915 {
2916 	REMOVE4res *res = (REMOVE4res *)obj;
2917 
2918 	dtl_nfsstat4(obj);
2919 	if (res->status == NFS4_OK) {
2920 		dtl_change_info("Change Information",
2921 		    &res->REMOVE4res_u.resok4.cinfo);
2922 	}
2923 }
2924 
2925 static void
2926 dtlres_rename(void *obj)
2927 {
2928 	RENAME4res *res = (RENAME4res *)obj;
2929 
2930 	dtl_nfsstat4(obj);
2931 	if (res->status == NFS4_OK) {
2932 		dtl_change_info("Source Change Information",
2933 		    &res->RENAME4res_u.resok4.source_cinfo);
2934 		dtl_change_info("Target Change Information",
2935 		    &res->RENAME4res_u.resok4.target_cinfo);
2936 	}
2937 }
2938 
2939 static void
2940 sumres_secinfo(char *buf, size_t buflen, void *obj)
2941 {
2942 	char *bp;
2943 	SECINFO4res *res = (SECINFO4res *)obj;
2944 
2945 	strncpy(buf, status_name(res->status), buflen);
2946 	bp = buf + strlen(buf);
2947 	if (res->status == NFS4_OK) {
2948 		uint_t numinfo = res->SECINFO4res_u.resok4.SECINFO4resok_len;
2949 		secinfo4 *infop;
2950 
2951 		for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val;
2952 		    numinfo != 0;
2953 		    infop++, numinfo--) {
2954 			snprintf(bp, buflen - (bp - buf), " %s",
2955 			    flavor_name(infop->flavor));
2956 			bp += strlen(bp);
2957 		}
2958 	}
2959 }
2960 
2961 static void
2962 dtlres_secinfo(void *obj)
2963 {
2964 	SECINFO4res *res = (SECINFO4res *)obj;
2965 
2966 	dtl_nfsstat4(obj);
2967 	if (res->status == NFS4_OK) {
2968 		uint_t numinfo =
2969 		    res->SECINFO4res_u.resok4.SECINFO4resok_len;
2970 		secinfo4 *infop;
2971 
2972 		for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val;
2973 		    numinfo != 0;
2974 		    infop++, numinfo--) {
2975 			detail_secinfo4(infop);
2976 		}
2977 	}
2978 }
2979 
2980 static void
2981 sumres_setattr(char *buf, size_t buflen, void *obj)
2982 {
2983 	SETATTR4res *res = (SETATTR4res *)obj;
2984 	size_t len;
2985 
2986 	(void) snprintf(buf, buflen, "%s ", status_name(res->status));
2987 	len = strlen(buf);
2988 	sum_attr_bitmap(buf + len, buflen - len, &res->attrsset);
2989 }
2990 
2991 static void
2992 dtlres_setattr(void *obj)
2993 {
2994 	SETATTR4res *res = (SETATTR4res *)obj;
2995 
2996 	dtl_nfsstat4(obj);
2997 	detail_attr_bitmap("", &res->attrsset, NULL);
2998 }
2999 
3000 static void
3001 sumres_setclid(char *buf, size_t buflen, void *obj)
3002 {
3003 	char *bp;
3004 	SETCLIENTID4res *res = (SETCLIENTID4res *)obj;
3005 
3006 	strncpy(buf, status_name(res->status), buflen);
3007 	switch (res->status) {
3008 	case NFS_OK:
3009 		bp = buf + strlen(buf);
3010 		snprintf(bp, buflen - (bp - buf), " %s CFV=%s",
3011 		    sum_clientid(res->SETCLIENTID4res_u.resok4.clientid),
3012 		    tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
3013 		    NFS4_VERIFIER_SIZE));
3014 		break;
3015 	case NFS4ERR_CLID_INUSE:
3016 		bp = buf + strlen(buf);
3017 		snprintf(bp, buflen - (bp - buf), " ID=%s Addr=%s",
3018 		    res->SETCLIENTID4res_u.client_using.r_netid,
3019 		    res->SETCLIENTID4res_u.client_using.r_addr);
3020 		break;
3021 	}
3022 }
3023 
3024 static void
3025 dtlres_setclid(void *obj)
3026 {
3027 	SETCLIENTID4res *res = (SETCLIENTID4res *)obj;
3028 
3029 	dtl_nfsstat4(obj);
3030 	switch (res->status) {
3031 	case NFS_OK:
3032 		detail_clientid(res->SETCLIENTID4res_u.resok4.clientid);
3033 		sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s",
3034 		    tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm,
3035 		    NFS4_VERIFIER_SIZE));
3036 		break;
3037 	case NFS4ERR_CLID_INUSE:
3038 		sprintf(get_line(0, 0), "Used by Net ID = %s",
3039 		    res->SETCLIENTID4res_u.client_using.r_netid);
3040 		sprintf(get_line(0, 0), "Used by Addr = %s",
3041 		    res->SETCLIENTID4res_u.client_using.r_addr);
3042 		break;
3043 	}
3044 }
3045 
3046 static void
3047 sumres_write(char *buf, size_t buflen, void *obj)
3048 {
3049 	char *bp;
3050 	WRITE4res *res = (WRITE4res *)obj;
3051 
3052 	strncpy(buf, status_name(res->status), buflen);
3053 	if (res->status == NFS4_OK) {
3054 		bp = buf + strlen(buf);
3055 		snprintf(bp, buflen - (bp - buf), " %u (%s)",
3056 		    res->WRITE4res_u.resok4.count,
3057 		    stable_how4_name(res->WRITE4res_u.resok4.committed));
3058 	}
3059 }
3060 
3061 static void
3062 dtlres_write(void *obj)
3063 {
3064 	WRITE4res *res = (WRITE4res *)obj;
3065 
3066 	dtl_nfsstat4(obj);
3067 	if (res->status == NFS4_OK) {
3068 		sprintf(get_line(0, 0), "Count = %u bytes written",
3069 		    res->WRITE4res_u.resok4.count);
3070 		sprintf(get_line(0, 0), "Stable = %s",
3071 		    stable_how4_name(res->WRITE4res_u.resok4.committed));
3072 		sprintf(get_line(0, 0), "Verifier = %s",
3073 		    tohex(res->WRITE4res_u.resok4.writeverf,
3074 		    NFS4_VERIFIER_SIZE));
3075 	}
3076 }
3077 
3078 /*
3079  * Print details about the nfs_resop4 that is next in the XDR stream.
3080  */
3081 
3082 static void
3083 detail_nfs_resop4(void)
3084 {
3085 	int numres;
3086 	nfs_resop4 one_res;
3087 	void (*fmtproc)(void *);
3088 
3089 	numres = getxdr_long();
3090 	(void) sprintf(get_line(0, 0), "Number of results = %d",
3091 	    numres);
3092 
3093 	while (numres-- > 0) {
3094 		bzero(&one_res, sizeof (one_res));
3095 
3096 		if (!xdr_nfs_resop4(&xdrm, &one_res)) {
3097 			xdr_free(xdr_nfs_resop4, (char *)&one_res);
3098 			longjmp(xdr_err, 1);
3099 		}
3100 
3101 		get_line(0, 0);		/* blank line to separate ops */
3102 		sprintf(get_line(0, 0), "Op = %d (%s)",
3103 		    one_res.resop, opcode_name(one_res.resop));
3104 		if (one_res.resop < num_opcodes)
3105 			fmtproc = opcode_info[one_res.resop].dtlres;
3106 		else if (one_res.resop == OP_ILLEGAL)
3107 			fmtproc = dtl_nfsstat4;
3108 		else
3109 			fmtproc = NULL;
3110 
3111 		if (fmtproc != NULL)
3112 			fmtproc(&one_res.nfs_resop4_u);
3113 
3114 		/* nfs4_skip_bytes set by xdr_nfs_resop4()() */
3115 		if (nfs4_skip_bytes)
3116 			nfs4_xdr_skip(nfs4_skip_bytes);
3117 
3118 		xdr_free(xdr_nfs_resop4, (char *)&one_res);
3119 	}
3120 }
3121 
3122 
3123 /*
3124  * Print details about the nfs_cb_resop4 that is next in the XDR stream.
3125  */
3126 
3127 static void
3128 detail_cb_resop4(void)
3129 {
3130 	int numres;
3131 	nfs_cb_resop4 one_res;
3132 	void (*fmtproc)(void *);
3133 
3134 	numres = getxdr_long();
3135 	(void) sprintf(get_line(0, 0), "Number of results = %d",
3136 	    numres);
3137 
3138 	while (numres-- > 0) {
3139 		bzero(&one_res, sizeof (one_res));
3140 		if (!xdr_nfs_cb_resop4(&xdrm, &one_res))
3141 			longjmp(xdr_err, 1);
3142 
3143 		get_line(0, 0);		/* blank line to separate ops */
3144 		sprintf(get_line(0, 0), "Op = %d (%s)",
3145 		    one_res.resop, cb_opcode_name(one_res.resop));
3146 		if (one_res.resop < cb_num_opcodes)
3147 			fmtproc = cb_opcode_info[one_res.resop].dtlres;
3148 		else if (one_res.resop == OP_CB_ILLEGAL)
3149 			fmtproc = dtl_nfsstat4;
3150 		else
3151 			fmtproc = NULL;
3152 
3153 		if (fmtproc != NULL)
3154 			fmtproc(&one_res.nfs_cb_resop4_u);
3155 
3156 		xdr_free(xdr_nfs_cb_resop4, (char *)&one_res);
3157 	}
3158 }
3159 
3160 
3161 /*
3162  * Return the name of a lock type.
3163  */
3164 static char *
3165 lock_type_name(enum nfs_lock_type4 type)
3166 {
3167 	char *result;
3168 
3169 	switch (type) {
3170 	case READ_LT:
3171 		result = "READ";
3172 		break;
3173 	case WRITE_LT:
3174 		result = "WRITE";
3175 		break;
3176 	case READW_LT:
3177 		result = "READW";
3178 		break;
3179 	case WRITEW_LT:
3180 		result = "WRITEW";
3181 		break;
3182 	default:
3183 		result = "?";
3184 		break;
3185 	}
3186 
3187 	return (result);
3188 }
3189 
3190 /*
3191  * Return the name of an opcode.
3192  */
3193 
3194 static char *
3195 opcode_name(uint_t opnum)
3196 {
3197 	static char buf[20];
3198 
3199 	if (opnum < num_opcodes)
3200 		return (opcode_info[opnum].name);
3201 
3202 	if (opnum == OP_ILLEGAL)
3203 		return ("ILLEGAL");
3204 
3205 	sprintf(buf, "op %d", opnum);
3206 	return (buf);
3207 }
3208 
3209 /*
3210  * Return the name of an opcode.
3211  */
3212 static char *
3213 cb_opcode_name(uint_t opnum)
3214 {
3215 	static char buf[20];
3216 
3217 	if (opnum < cb_num_opcodes)
3218 		return (cb_opcode_info[opnum].name);
3219 
3220 	if (opnum == OP_CB_ILLEGAL)
3221 		return ("CB_ILLEGAL");
3222 
3223 	sprintf(buf, "op %d", opnum);
3224 	return (buf);
3225 }
3226 
3227 
3228 /*
3229  * Fill in a summary string for the given access bitmask.
3230  */
3231 
3232 static void
3233 sum_access4(char *buf, size_t buflen, uint32_t bits)
3234 {
3235 	buf[0] = '\0';
3236 
3237 	if (bits & ACCESS4_READ)
3238 		(void) strncat(buf, "rd,", buflen);
3239 	if (bits & ACCESS4_LOOKUP)
3240 		(void) strncat(buf, "lk,", buflen);
3241 	if (bits & ACCESS4_MODIFY)
3242 		(void) strncat(buf, "mo,", buflen);
3243 	if (bits & ACCESS4_EXTEND)
3244 		(void) strncat(buf, "ext,", buflen);
3245 	if (bits & ACCESS4_DELETE)
3246 		(void) strncat(buf, "dl,", buflen);
3247 	if (bits & ACCESS4_EXECUTE)
3248 		(void) strncat(buf, "exc,", buflen);
3249 	if (buf[0] != '\0')
3250 		buf[strlen(buf) - 1] = '\0';
3251 }
3252 
3253 /*
3254  * Print detail information about the given access bitmask.
3255  */
3256 
3257 static void
3258 detail_access4(char *descrip, uint32_t bits)
3259 {
3260 	sprintf(get_line(0, 0), "%s = 0x%08x", descrip, bits);
3261 
3262 	(void) sprintf(get_line(0, 0), "	%s",
3263 	    getflag(bits, ACCESS4_READ, "Read", "(no read)"));
3264 	(void) sprintf(get_line(0, 0), "	%s",
3265 	    getflag(bits, ACCESS4_LOOKUP, "Lookup", "(no lookup)"));
3266 	(void) sprintf(get_line(0, 0), "	%s",
3267 	    getflag(bits, ACCESS4_MODIFY, "Modify", "(no modify)"));
3268 	(void) sprintf(get_line(0, 0), "	%s",
3269 	    getflag(bits, ACCESS4_EXTEND, "Extend", "(no extend)"));
3270 	(void) sprintf(get_line(0, 0), "	%s",
3271 	    getflag(bits, ACCESS4_DELETE, "Delete", "(no delete)"));
3272 	(void) sprintf(get_line(0, 0), "	%s",
3273 	    getflag(bits, ACCESS4_EXECUTE, "Execute", "(no execute)"));
3274 }
3275 
3276 
3277 /*
3278  * Fill in a summary string for the given open_claim4.
3279  */
3280 static void
3281 sum_name(char *buf, size_t buflen, open_claim4 *claim)
3282 {
3283 	char *bp = buf;
3284 
3285 	switch (claim->claim) {
3286 	case CLAIM_NULL:
3287 		snprintf(bp, buflen, "%s ",
3288 		    component_name(&claim->open_claim4_u.file));
3289 		break;
3290 	case CLAIM_PREVIOUS:
3291 		break;
3292 	case CLAIM_DELEGATE_CUR:
3293 		snprintf(bp, buflen, "%s ",
3294 		    component_name(&claim->open_claim4_u.
3295 		    delegate_cur_info.file));
3296 		break;
3297 	case CLAIM_DELEGATE_PREV:
3298 		snprintf(bp, buflen, "%s ",
3299 		    component_name(&claim->open_claim4_u.
3300 		    file_delegate_prev));
3301 		break;
3302 	}
3303 }
3304 
3305 /*
3306  * Fill in a summary string for the given open_claim4.
3307  */
3308 static void
3309 sum_claim(char *buf, size_t buflen, open_claim4 *claim)
3310 {
3311 	char *bp = buf;
3312 
3313 	switch (claim->claim) {
3314 	case CLAIM_NULL:
3315 		snprintf(bp, buflen, " CT=N");
3316 		break;
3317 	case CLAIM_PREVIOUS:
3318 		snprintf(bp, buflen, " CT=P DT=%s",
3319 		    get_deleg_typestr(claim->open_claim4_u.delegate_type));
3320 		break;
3321 	case CLAIM_DELEGATE_CUR:
3322 		snprintf(bp, buflen, " CT=DC %s",
3323 		    sum_deleg_stateid(&claim->open_claim4_u.
3324 		    delegate_cur_info.delegate_stateid));
3325 		break;
3326 	case CLAIM_DELEGATE_PREV:
3327 		snprintf(bp, buflen, " CT=DP");
3328 		break;
3329 	default:
3330 		snprintf(bp, buflen, " CT=?");
3331 		break;
3332 	}
3333 }
3334 
3335 static char *
3336 get_deleg_typestr(open_delegation_type4 dt)
3337 {
3338 	char *str = "";
3339 
3340 	switch (dt) {
3341 	case OPEN_DELEGATE_NONE:
3342 		str = "N";
3343 		break;
3344 	case OPEN_DELEGATE_READ:
3345 		str = "R";
3346 		break;
3347 	case OPEN_DELEGATE_WRITE:
3348 		str = "W";
3349 		break;
3350 	default:
3351 		str = "?";
3352 	}
3353 
3354 	return (str);
3355 }
3356 
3357 /*
3358  * Print detail information for the given open_claim4.
3359  */
3360 
3361 static void
3362 detail_claim(open_claim4 *claim)
3363 {
3364 	sprintf(get_line(0, 0), "Claim Type = %d (%s)",
3365 	    claim->claim, claim_name(claim->claim));
3366 
3367 	switch (claim->claim) {
3368 	case CLAIM_NULL:
3369 		detail_compname4(&claim->open_claim4_u.file);
3370 		break;
3371 	case CLAIM_PREVIOUS:
3372 		sprintf(get_line(0, 0), "Delegate Type = %s (val = %d)",
3373 		    get_deleg_typestr(claim->open_claim4_u.delegate_type),
3374 		    claim->open_claim4_u.delegate_type);
3375 		break;
3376 	case CLAIM_DELEGATE_CUR:
3377 		detail_compname4(&claim->open_claim4_u.delegate_cur_info.file);
3378 		detail_deleg_stateid(&claim->open_claim4_u.delegate_cur_info.
3379 		    delegate_stateid);
3380 		break;
3381 	case CLAIM_DELEGATE_PREV:
3382 		detail_compname4(&claim->open_claim4_u.file_delegate_prev);
3383 		break;
3384 	}
3385 }
3386 
3387 /*
3388  * Return a summary string for the given clientid4.
3389  */
3390 static char *
3391 sum_clientid(clientid4 client)
3392 {
3393 	static char buf[50];
3394 
3395 	snprintf(buf, sizeof (buf), "CL=%llx", client);
3396 
3397 	return (buf);
3398 }
3399 
3400 /*
3401  * Print a detail string for the given clientid4.
3402  */
3403 static void
3404 detail_clientid(clientid4 client)
3405 {
3406 	sprintf(get_line(0, 0), "Client ID = %llx", client);
3407 }
3408 
3409 /*
3410  * Write a summary string for the given delegation into buf.
3411  */
3412 
3413 static void
3414 sum_delegation(char *buf, size_t buflen, open_delegation4 *delp)
3415 {
3416 	switch (delp->delegation_type) {
3417 	case OPEN_DELEGATE_NONE:
3418 		snprintf(buf, buflen, " DT=N");
3419 		break;
3420 	case OPEN_DELEGATE_READ:
3421 		snprintf(buf, buflen, " DT=R %s",
3422 		    sum_deleg_stateid(&delp->open_delegation4_u.write.
3423 		    stateid));
3424 		break;
3425 	case OPEN_DELEGATE_WRITE:
3426 		snprintf(buf, buflen, " DT=W %s %s",
3427 		    sum_deleg_stateid(&delp->open_delegation4_u.write.
3428 		    stateid),
3429 		    sum_space_limit(&delp->open_delegation4_u.write.
3430 		    space_limit));
3431 		break;
3432 	default:
3433 		snprintf(buf, buflen, " DT=?");
3434 		break;
3435 	}
3436 }
3437 
3438 static void
3439 detail_delegation(open_delegation4 *delp)
3440 {
3441 	sprintf(get_line(0, 0), "Delegation Type = %d (%s)",
3442 	    delp->delegation_type,
3443 	    delegation_type_name(delp->delegation_type));
3444 
3445 	switch (delp->delegation_type) {
3446 	case OPEN_DELEGATE_NONE:
3447 		/* no-op */
3448 		break;
3449 	case OPEN_DELEGATE_READ:
3450 		detail_deleg_stateid(&delp->open_delegation4_u.read.stateid);
3451 		sprintf(get_line(0, 0), "Recall = %s",
3452 		    delp->open_delegation4_u.read.recall ?
3453 		    "TRUE" : "FALSE");
3454 		sprintf(get_line(0, 0), "[nfsacl4]");
3455 		break;
3456 	case OPEN_DELEGATE_WRITE:
3457 		detail_deleg_stateid(&delp->open_delegation4_u.write.stateid);
3458 		sprintf(get_line(0, 0), "Recall = %s",
3459 		    delp->open_delegation4_u.write.recall ?
3460 		    "TRUE" : "FALSE");
3461 		detail_space_limit(&delp->open_delegation4_u.write.
3462 		    space_limit);
3463 		sprintf(get_line(0, 0), "[nfsacl4]");
3464 		break;
3465 	}
3466 }
3467 
3468 
3469 static void
3470 detail_open_owner(open_owner4 *owner)
3471 {
3472 	sprintf(get_line(0, 0), "Open Owner hash = [%04X] ",
3473 	    owner_hash(&owner->owner));
3474 	sprintf(get_line(0, 0), "    len = %u   val = %s ",
3475 	    owner->owner.owner_len,
3476 	    tohex(owner->owner.owner_val, owner->owner.owner_len));
3477 	detail_clientid(owner->clientid);
3478 }
3479 
3480 static void
3481 detail_lock_owner(lock_owner4 *owner)
3482 {
3483 	sprintf(get_line(0, 0), "Lock Owner hash = [%04X] ",
3484 	    owner_hash(&owner->owner));
3485 	sprintf(get_line(0, 0), "    len = %u   val = %s ",
3486 	    owner->owner.owner_len,
3487 	    tohex(owner->owner.owner_val, owner->owner.owner_len));
3488 	detail_clientid(owner->clientid);
3489 }
3490 
3491 static void
3492 sum_openflag(char *bufp, int buflen, openflag4 *flagp)
3493 {
3494 	if (flagp->opentype == OPEN4_CREATE) {
3495 		switch (flagp->openflag4_u.how.mode) {
3496 		case UNCHECKED4:
3497 			snprintf(bufp, buflen, "OT=CR(U)");
3498 			break;
3499 		case GUARDED4:
3500 			snprintf(bufp, buflen, "OT=CR(G)");
3501 			break;
3502 		case EXCLUSIVE4:
3503 			snprintf(bufp, buflen, "OT=CR(E)");
3504 			break;
3505 		default:
3506 			snprintf(bufp, buflen, "OT=CR(?:%d)",
3507 			    flagp->openflag4_u.how.mode);
3508 			break;
3509 		}
3510 	} else
3511 		snprintf(bufp, buflen, "OT=NC");
3512 }
3513 
3514 static void
3515 detail_openflag(openflag4 *flagp)
3516 {
3517 	sprintf(get_line(0, 0), "Open Type = %s",
3518 	    flagp->opentype == OPEN4_CREATE ? "CREATE" : "NOCREATE");
3519 	if (flagp->opentype == OPEN4_CREATE)
3520 		detail_createhow4(&flagp->openflag4_u.how);
3521 }
3522 
3523 /*
3524  * Fill in buf with the given path.
3525  */
3526 static void
3527 sum_pathname4(char *buf, size_t buflen, pathname4 *pathp)
3528 {
3529 	char *bp = buf;
3530 	uint_t component;
3531 
3532 	for (component = 0; component < pathp->pathname4_len;
3533 	    component++) {
3534 		snprintf(bp, buflen - (bp - buf),
3535 		    component == 0 ? "%s" : "/%s",
3536 		    component_name(&pathp->pathname4_val[component]));
3537 		bp += strlen(bp);
3538 	}
3539 }
3540 
3541 static void
3542 sum_compname4(char *buf, size_t buflen, component4 *comp)
3543 {
3544 	snprintf(buf, buflen, "%s", component_name(comp));
3545 }
3546 
3547 static void
3548 detail_compname4(component4 *comp)
3549 {
3550 	sprintf(get_line(0, 0), "%s", component_name(comp));
3551 }
3552 
3553 static void
3554 detail_pathname4(pathname4 *pathp, char *what)
3555 {
3556 	char *bp = get_line(0, 0);
3557 	uint_t component;
3558 
3559 	sprintf(bp, what);
3560 	bp += strlen(bp);
3561 
3562 	for (component = 0; component < pathp->pathname4_len; component++) {
3563 		sprintf(bp, component == 0 ? "%s" : "/%s",
3564 		    component_name(&pathp->pathname4_val[component]));
3565 		bp += strlen(bp);
3566 	}
3567 }
3568 
3569 /*
3570  * Print detail information about the rpcsec_gss_info that is XDR-encoded
3571  * at mem.
3572  */
3573 
3574 static void
3575 detail_rpcsec_gss(rpcsec_gss_info *info)
3576 {
3577 	sprintf(get_line(0, 0), "OID = %s",
3578 	    tohex(info->oid.sec_oid4_val, info->oid.sec_oid4_len));
3579 	sprintf(get_line(0, 0), "QOP = %u", info->qop);
3580 	sprintf(get_line(0, 0), "Service = %d (%s)",
3581 	    info->service, gss_svc_name(info->service));
3582 }
3583 
3584 /*
3585  * Print detail information about the given secinfo4.
3586  */
3587 
3588 static void
3589 detail_secinfo4(secinfo4 *infop)
3590 {
3591 	sprintf(get_line(0, 0), "Flavor = %d (%s)",
3592 	    infop->flavor, flavor_name(infop->flavor));
3593 	switch (infop->flavor) {
3594 	case RPCSEC_GSS:
3595 		detail_rpcsec_gss(&infop->secinfo4_u.flavor_info);
3596 		break;
3597 	}
3598 }
3599 
3600 
3601 /*
3602  * Return a summary string corresponding to the given nfs_space_limit4.
3603  */
3604 
3605 static char *
3606 sum_space_limit(nfs_space_limit4 *limitp)
3607 {
3608 	static char buf[64];
3609 	int buflen = sizeof (buf);
3610 
3611 	buf[0] = '\0';
3612 	switch (limitp->limitby) {
3613 	case NFS_LIMIT_SIZE:
3614 		snprintf(buf, buflen, "LB=SZ(%llu)",
3615 		    limitp->nfs_space_limit4_u.filesize);
3616 		break;
3617 	case NFS_LIMIT_BLOCKS:
3618 		snprintf(buf, buflen, "LB=BL(%u*%u)",
3619 		    limitp->nfs_space_limit4_u.mod_blocks.num_blocks,
3620 		    limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
3621 		break;
3622 	default:
3623 		snprintf(buf, buflen, "LB=?(%d)", limitp->limitby);
3624 		break;
3625 	}
3626 
3627 	return (buf);
3628 }
3629 
3630 /*
3631  * Print detail information about the given nfs_space_limit4.
3632  */
3633 
3634 static void
3635 detail_space_limit(nfs_space_limit4 *limitp)
3636 {
3637 	sprintf(get_line(0, 0), "LimitBy = %d (%s)",
3638 	    limitp->limitby,
3639 	    limitby_name(limitp->limitby));
3640 
3641 	switch (limitp->limitby) {
3642 	case NFS_LIMIT_SIZE:
3643 		sprintf(get_line(0, 0), "Bytes = %llu",
3644 		    limitp->nfs_space_limit4_u.filesize);
3645 		break;
3646 	case NFS_LIMIT_BLOCKS:
3647 		sprintf(get_line(0, 0), "Blocks = %u",
3648 		    limitp->nfs_space_limit4_u.mod_blocks.num_blocks);
3649 		sprintf(get_line(0, 0), "Bytes Per Block = %u",
3650 		    limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block);
3651 		break;
3652 	}
3653 }
3654 
3655 
3656 /*
3657  * Return the short name of a file type.
3658  */
3659 
3660 static char *
3661 sum_type_name(nfs_ftype4 type)
3662 {
3663 	static char buf[20];
3664 
3665 	if (type < num_ftypes)
3666 		return (ftype_names[type].short_name);
3667 	else {
3668 		sprintf(buf, "type %d", type);
3669 		return (buf);
3670 	}
3671 }
3672 
3673 
3674 /*
3675  * Return string with long/short flag names
3676  */
3677 
3678 static char *
3679 get_flags(uint_t flag, ftype_names_t *names, uint_t num_flags, int shortname,
3680     char *prefix)
3681 {
3682 	static char buf[200];
3683 	char *bp = buf, *str;
3684 	int i, len, blen = sizeof (buf);
3685 	ftype_names_t *fn = NULL;
3686 
3687 	*bp = '\0';
3688 
3689 	if (prefix) {
3690 		snprintf(bp, blen, "%s", prefix);
3691 		bp += (len = sizeof (bp));
3692 		blen -= len;
3693 	}
3694 
3695 	for (i = 0; i < 32; i++)
3696 		if (flag & (1 << i)) {
3697 			fn = names + (i < num_flags ? i : num_flags);
3698 			str = (shortname ? fn->short_name : fn->long_name);
3699 
3700 			snprintf(bp, blen, "%s,", str);
3701 			bp += (len = strlen(bp));
3702 			blen -= len;
3703 		}
3704 
3705 	if (fn)
3706 		*(bp - 1) = '\0';
3707 	else
3708 		*buf = '\0';
3709 
3710 	return (buf);
3711 }
3712 
3713 
3714 /*
3715  * Return the long name of a file type.
3716  */
3717 
3718 static char *
3719 detail_type_name(nfs_ftype4 type)
3720 {
3721 	static char buf[20];
3722 
3723 	if (type < num_ftypes)
3724 		return (ftype_names[type].long_name);
3725 	else {
3726 		sprintf(buf, "type %d", type);
3727 		return (buf);
3728 	}
3729 }
3730 
3731 /*
3732  * Return the name of an attribute.
3733  */
3734 
3735 static char *
3736 attr_name(uint_t attrnum)
3737 {
3738 	static char buf[20];
3739 
3740 	if (attrnum < MAX_ATTRIBUTES)
3741 		return (attr_info[attrnum].name);
3742 	else {
3743 		sprintf(buf, "attr #%d", attrnum);
3744 		return (buf);
3745 	}
3746 }
3747 
3748 /*
3749  * Return the name of the given open_claim_type4.
3750  */
3751 
3752 static char *
3753 claim_name(enum open_claim_type4 claim_type)
3754 {
3755 	char *result;
3756 
3757 	switch (claim_type) {
3758 	case CLAIM_NULL:
3759 		result = "NULL";
3760 		break;
3761 	case CLAIM_PREVIOUS:
3762 		result = "PREVIOUS";
3763 		break;
3764 	case CLAIM_DELEGATE_CUR:
3765 		result = "DELEGATE CURRENT";
3766 		break;
3767 	case CLAIM_DELEGATE_PREV:
3768 		result = "DELEGATE PREVIOUS";
3769 		break;
3770 	default:
3771 		result = "?";
3772 		break;
3773 	}
3774 
3775 	return (result);
3776 }
3777 
3778 /*
3779  * Return a string naming the given delegation.
3780  */
3781 
3782 static char *
3783 delegation_type_name(enum open_delegation_type4 type)
3784 {
3785 	char *result;
3786 
3787 	switch (type) {
3788 	case OPEN_DELEGATE_NONE:
3789 		result = "NONE";
3790 		break;
3791 	case OPEN_DELEGATE_READ:
3792 		result = "READ";
3793 		break;
3794 	case OPEN_DELEGATE_WRITE:
3795 		result = "WRITE";
3796 		break;
3797 	default:
3798 		result = "?";
3799 		break;
3800 	}
3801 
3802 	return (result);
3803 }
3804 
3805 /*
3806  * Return the name of the given authentication flavor.
3807  */
3808 
3809 static char *
3810 flavor_name(uint_t flavor)
3811 {
3812 	char *result;
3813 	static char buf[50];
3814 
3815 	switch (flavor) {
3816 	case AUTH_SYS:
3817 		result = "AUTH_SYS";
3818 		break;
3819 	case AUTH_NONE:
3820 		result = "AUTH_NONE";
3821 		break;
3822 	case AUTH_DH:
3823 		result = "AUTH_DH";
3824 		break;
3825 	case RPCSEC_GSS:
3826 		result = "RPCSEC_GSS";
3827 		break;
3828 	default:
3829 		sprintf(buf, "[flavor %d]", flavor);
3830 		result = buf;
3831 		break;
3832 	}
3833 
3834 	return (result);
3835 }
3836 
3837 /*
3838  * Return the name of the given rpc_gss_svc_t.
3839  */
3840 
3841 static char *
3842 gss_svc_name(rpc_gss_svc_t svc)
3843 {
3844 	char *result;
3845 	static char buf[50];
3846 
3847 	switch (svc) {
3848 	case RPC_GSS_SVC_NONE:
3849 		result = "NONE";
3850 		break;
3851 	case RPC_GSS_SVC_INTEGRITY:
3852 		result = "INTEGRITY";
3853 		break;
3854 	case RPC_GSS_SVC_PRIVACY:
3855 		result = "PRIVACY";
3856 		break;
3857 	default:
3858 		sprintf(buf, "Service %d", svc);
3859 		result = buf;
3860 		break;
3861 	}
3862 
3863 	return (result);
3864 }
3865 
3866 /*
3867  * Return a string name for the given limit_by4.
3868  */
3869 
3870 static char *
3871 limitby_name(enum limit_by4 limitby)
3872 {
3873 	char *result;
3874 
3875 	switch (limitby) {
3876 	case NFS_LIMIT_SIZE:
3877 		result = "SIZE";
3878 		break;
3879 	case NFS_LIMIT_BLOCKS:
3880 		result = "BLOCKS";
3881 		break;
3882 	default:
3883 		result = "?";
3884 		break;
3885 	}
3886 
3887 	return (result);
3888 }
3889 
3890 static char *
3891 status_name(int status)
3892 {
3893 	char *p;
3894 
3895 	switch (status) {
3896 	case NFS4_OK:		p = "NFS4_OK"; break;
3897 	case NFS4ERR_PERM:	p = "NFS4ERR_PERM"; break;
3898 	case NFS4ERR_NOENT:	p = "NFS4ERR_NOENT"; break;
3899 	case NFS4ERR_IO:	p = "NFS4ERR_IO"; break;
3900 	case NFS4ERR_NXIO:	p = "NFS4ERR_NXIO"; break;
3901 	case NFS4ERR_ACCESS:	p = "NFS4ERR_ACCESS"; break;
3902 	case NFS4ERR_EXIST:	p = "NFS4ERR_EXIST"; break;
3903 	case NFS4ERR_XDEV:	p = "NFS4ERR_XDEV"; break;
3904 	case NFS4ERR_NOTDIR:	p = "NFS4ERR_NOTDIR"; break;
3905 	case NFS4ERR_ISDIR:	p = "NFS4ERR_ISDIR"; break;
3906 	case NFS4ERR_INVAL:	p = "NFS4ERR_INVAL"; break;
3907 	case NFS4ERR_FBIG:	p = "NFS4ERR_FBIG"; break;
3908 	case NFS4ERR_NOSPC:	p = "NFS4ERR_NOSPC"; break;
3909 	case NFS4ERR_ROFS:	p = "NFS4ERR_ROFS"; break;
3910 	case NFS4ERR_MLINK:	p = "NFS4ERR_MLINK"; break;
3911 	case NFS4ERR_NAMETOOLONG:p = "NFS4ERR_NAMETOOLONG"; break;
3912 	case NFS4ERR_NOTEMPTY:	p = "NFS4ERR_NOTEMPTY"; break;
3913 	case NFS4ERR_DQUOT:	p = "NFS4ERR_DQUOT"; break;
3914 	case NFS4ERR_STALE:	p = "NFS4ERR_STALE"; break;
3915 	case NFS4ERR_BADHANDLE:	p = "NFS4ERR_BADHANDLE"; break;
3916 	case NFS4ERR_BAD_COOKIE:p = "NFS4ERR_BAD_COOKIE"; break;
3917 	case NFS4ERR_NOTSUPP:	p = "NFS4ERR_NOTSUPP"; break;
3918 	case NFS4ERR_TOOSMALL:	p = "NFS4ERR_TOOSMALL"; break;
3919 	case NFS4ERR_SERVERFAULT:p = "NFS4ERR_SERVERFAULT"; break;
3920 	case NFS4ERR_BADTYPE:	p = "NFS4ERR_BADTYPE"; break;
3921 	case NFS4ERR_DELAY:	p = "NFS4ERR_DELAY"; break;
3922 	case NFS4ERR_SAME:	p = "NFS4ERR_SAME"; break;
3923 	case NFS4ERR_DENIED:	p = "NFS4ERR_DENIED"; break;
3924 	case NFS4ERR_EXPIRED:	p = "NFS4ERR_EXPIRED"; break;
3925 	case NFS4ERR_LOCKED:	p = "NFS4ERR_LOCKED"; break;
3926 	case NFS4ERR_GRACE:	p = "NFS4ERR_GRACE"; break;
3927 	case NFS4ERR_FHEXPIRED:	p = "NFS4ERR_FHEXPIRED"; break;
3928 	case NFS4ERR_SHARE_DENIED: p = "NFS4ERR_SHARE_DENIED"; break;
3929 	case NFS4ERR_WRONGSEC:	p = "NFS4ERR_WRONGSEC"; break;
3930 	case NFS4ERR_CLID_INUSE: p = "NFS4ERR_CLID_INUSE"; break;
3931 	case NFS4ERR_RESOURCE:	p = "NFS4ERR_RESOURCE"; break;
3932 	case NFS4ERR_MOVED:	p = "NFS4ERR_MOVED"; break;
3933 	case NFS4ERR_NOFILEHANDLE: p = "NFS4ERR_NOFILEHANDLE"; break;
3934 	case NFS4ERR_MINOR_VERS_MISMATCH: p = "NFS4ERR_MINOR_VERS_MISMATCH";
3935 	break;
3936 	case NFS4ERR_STALE_CLIENTID: p = "NFS4ERR_STALE_CLIENTID"; break;
3937 	case NFS4ERR_STALE_STATEID: p = "NFS4ERR_STALE_STATEID"; break;
3938 	case NFS4ERR_OLD_STATEID: p = "NFS4ERR_OLD_STATEID"; break;
3939 	case NFS4ERR_BAD_STATEID: p = "NFS4ERR_BAD_STATEID"; break;
3940 	case NFS4ERR_BAD_SEQID: p = "NFS4ERR_BAD_SEQID"; break;
3941 	case NFS4ERR_NOT_SAME: p = "NFS4ERR_NOT_SAME"; break;
3942 	case NFS4ERR_LOCK_RANGE: p = "NFS4ERR_LOCK_RANGE"; break;
3943 	case NFS4ERR_SYMLINK: p = "NFS4ERR_SYMLINK"; break;
3944 	case NFS4ERR_RESTOREFH: p = "NFS4ERR_RESTOREFH"; break;
3945 	case NFS4ERR_LEASE_MOVED: p = "NFS4ERR_LEASE_MOVED"; break;
3946 	case NFS4ERR_ATTRNOTSUPP: p = "NFS4ERR_ATTRNOTSUPP"; break;
3947 	case NFS4ERR_NO_GRACE: p = "NFS4ERR_NO_GRACE"; break;
3948 	case NFS4ERR_RECLAIM_BAD: p = "NFS4ERR_RECLAIM_BAD"; break;
3949 	case NFS4ERR_RECLAIM_CONFLICT: p = "NFS4ERR_RECLAIM_CONFLICT"; break;
3950 	case NFS4ERR_BADXDR: p = "NFS4ERR_BADXDR"; break;
3951 	case NFS4ERR_LOCKS_HELD: p = "NFS4ERR_LOCKS_HELD"; break;
3952 	case NFS4ERR_OPENMODE: p = "NFS4ERR_OPENMODE"; break;
3953 	case NFS4ERR_BADOWNER: p = "NFS4ERR_BADOWNER"; break;
3954 	case NFS4ERR_BADCHAR: p = "NFS4ERR_BADCHAR"; break;
3955 	case NFS4ERR_BADNAME: p = "NFS4ERR_BADNAME"; break;
3956 	case NFS4ERR_BAD_RANGE: p = "NFS4ERR_BAD_RANGE"; break;
3957 	case NFS4ERR_LOCK_NOTSUPP: p = "NFS4ERR_LOCK_NOTSUPP"; break;
3958 	case NFS4ERR_OP_ILLEGAL: p = "NFS4ERR_OP_ILLEGAL"; break;
3959 	case NFS4ERR_DEADLOCK: p = "NFS4ERR_DEADLOCK"; break;
3960 	case NFS4ERR_FILE_OPEN: p = "NFS4ERR_FILE_OPEN"; break;
3961 	case NFS4ERR_ADMIN_REVOKED: p = "NFS4ERR_ADMIN_REVOKED"; break;
3962 	case NFS4ERR_CB_PATH_DOWN: p = "NFS4ERR_CB_PATH_DOWN"; break;
3963 	default:		p = "(unknown error)"; break;
3964 	}
3965 
3966 	return (p);
3967 }
3968 
3969 char *
3970 nfsstat4_to_name(int status)
3971 {
3972 	return (status_name(status));
3973 }
3974 
3975 /*
3976  * Attribute print functions.  See attr_info_t.
3977  */
3978 
3979 static void
3980 prt_supported_attrs(XDR *xdr)
3981 {
3982 	static bitmap4 val;
3983 
3984 	if (!xdr_bitmap4(xdr, &val))
3985 		longjmp(xdr_err, 1);
3986 	sprintf(get_line(0, 0), "Supported Attributes:");
3987 	detail_attr_bitmap("\t", &val, NULL);
3988 	xdr_free(xdr_bitmap4, (char *)&val);
3989 }
3990 
3991 static void
3992 prt_type(XDR *xdr)
3993 {
3994 	nfs_ftype4 val;
3995 
3996 	if (!xdr_nfs_ftype4(xdr, &val))
3997 		longjmp(xdr_err, 1);
3998 	sprintf(get_line(0, 0), "Type = %s", sum_type_name(val));
3999 }
4000 
4001 static void
4002 prt_fh_expire_type(XDR *xdr)
4003 {
4004 	fattr4_fh_expire_type val;
4005 	char *buf;
4006 	bool_t first = TRUE;
4007 
4008 	if (!xdr_fattr4_fh_expire_type(xdr, &val))
4009 		longjmp(xdr_err, 1);
4010 	buf = get_line(0, 0);
4011 
4012 	sprintf(buf, "Filehandle expire type = ");
4013 	if ((val & (FH4_NOEXPIRE_WITH_OPEN | FH4_VOLATILE_ANY |
4014 	    FH4_VOL_MIGRATION | FH4_VOL_RENAME)) == 0) {
4015 		strcat(buf, "Persistent");
4016 		return;
4017 	}
4018 	if (val & FH4_NOEXPIRE_WITH_OPEN) {
4019 		strcat(buf, "No Expire With OPEN");
4020 		first = FALSE;
4021 	}
4022 	if (val & FH4_VOLATILE_ANY) {
4023 		if (first)
4024 			first = FALSE;
4025 		else
4026 			strcat(buf, ", ");
4027 		strcat(buf, "Volatile at any time");
4028 	}
4029 	if (val & FH4_VOL_MIGRATION) {
4030 		if (first)
4031 			first = FALSE;
4032 		else
4033 			strcat(buf, ", ");
4034 		strcat(buf, "Volatile at Migration");
4035 	}
4036 	if (val & FH4_VOL_RENAME) {
4037 		if (first)
4038 			first = FALSE;
4039 		else
4040 			strcat(buf, ", ");
4041 		strcat(buf, "Volatile at Rename");
4042 	}
4043 }
4044 
4045 static void
4046 prt_change(XDR *xdr)
4047 {
4048 	changeid4 val;
4049 
4050 	if (!xdr_changeid4(xdr, &val))
4051 		longjmp(xdr_err, 1);
4052 	sprintf(get_line(0, 0), "Change ID = 0x%llx", val);
4053 					/* XXX print as time_t, too? */
4054 }
4055 
4056 static void
4057 prt_size(XDR *xdr)
4058 {
4059 	uint64_t val;
4060 
4061 	if (!xdr_uint64_t(xdr, &val))
4062 		longjmp(xdr_err, 1);
4063 	sprintf(get_line(0, 0), "Size = %llu", val);
4064 }
4065 
4066 static void
4067 prt_link_support(XDR *xdr)
4068 {
4069 	bool_t val;
4070 
4071 	if (!xdr_bool(xdr, &val))
4072 		longjmp(xdr_err, 1);
4073 	sprintf(get_line(0, 0), "Link Support = %s",
4074 	    val ? "TRUE" : "FALSE");
4075 }
4076 
4077 static void
4078 prt_symlink_support(XDR *xdr)
4079 {
4080 	bool_t val;
4081 
4082 	if (!xdr_bool(xdr, &val))
4083 		longjmp(xdr_err, 1);
4084 	sprintf(get_line(0, 0), "Symlink Support = %s",
4085 	    val ? "TRUE" : "FALSE");
4086 }
4087 
4088 static void
4089 prt_named_attr(XDR *xdr)
4090 {
4091 	bool_t val;
4092 
4093 	if (!xdr_bool(xdr, &val))
4094 		longjmp(xdr_err, 1);
4095 	sprintf(get_line(0, 0), "Has Named Attributes = %s",
4096 	    val ? "TRUE" : "FALSE");
4097 }
4098 
4099 static void
4100 prt_fsid(XDR *xdr)
4101 {
4102 	fsid4 val;
4103 
4104 	if (!xdr_fsid4(xdr, &val))
4105 		longjmp(xdr_err, 1);
4106 	sprintf(get_line(0, 0), "FS ID: Major = %llx, Minor = %llx",
4107 	    val.major, val.minor);
4108 }
4109 
4110 static void
4111 prt_unique_handles(XDR *xdr)
4112 {
4113 	bool_t val;
4114 
4115 	if (!xdr_bool(xdr, &val))
4116 		longjmp(xdr_err, 1);
4117 	sprintf(get_line(0, 0), "Unique Handles = %s",
4118 	    val ? "TRUE" : "FALSE");
4119 }
4120 
4121 static void
4122 prt_lease_time(XDR *xdr)
4123 {
4124 	uint32_t val;
4125 
4126 	if (!xdr_uint32_t(xdr, &val))
4127 		longjmp(xdr_err, 1);
4128 	sprintf(get_line(0, 0), "Lease Time = %u", val);
4129 }
4130 
4131 static void
4132 prt_rdattr_error(XDR *xdr)
4133 {
4134 	nfsstat4 val;
4135 
4136 	if (!xdr_nfsstat4(xdr, &val))
4137 		longjmp(xdr_err, 1);
4138 	sprintf(get_line(0, 0), "Rdattr Error = %u (%s)",
4139 	    val, status_name(val));
4140 }
4141 
4142 static void
4143 prt_acl(XDR *xdr)
4144 {
4145 	static fattr4_acl val;
4146 	char buffy[NFS4_OPAQUE_LIMIT];
4147 	int i, len;
4148 
4149 	if (!xdr_fattr4_acl(xdr, &val))
4150 		longjmp(xdr_err, 1);
4151 	sprintf(get_line(0, 0), "ACL of %d entries", val.fattr4_acl_len);
4152 	for (i = 0; i < val.fattr4_acl_len; i++) {
4153 		sprintf(get_line(0, 0), "nfsace4[%d]", i);
4154 
4155 		sprintf(get_line(0, 0), "  type = %x",
4156 		    val.fattr4_acl_val[i].type);
4157 		detail_acetype4(val.fattr4_acl_val[i].type);
4158 
4159 		sprintf(get_line(0, 0), "  flags = %x",
4160 		    val.fattr4_acl_val[i].flag);
4161 		detail_aceflag4(val.fattr4_acl_val[i].flag);
4162 
4163 		sprintf(get_line(0, 0), "  mask = %x",
4164 		    val.fattr4_acl_val[i].access_mask);
4165 		detail_acemask4(val.fattr4_acl_val[i].access_mask);
4166 
4167 		len = val.fattr4_acl_val[i].who.utf8string_len;
4168 		if (len >= NFS4_OPAQUE_LIMIT)
4169 			len = NFS4_OPAQUE_LIMIT - 1;
4170 		(void) strncpy(buffy, val.fattr4_acl_val[i].who.utf8string_val,
4171 		    len);
4172 		buffy[len] = '\0';
4173 		sprintf(get_line(0, 0), "  who = %s", buffy);
4174 	}
4175 	xdr_free(xdr_fattr4_acl, (char *)&val);
4176 }
4177 
4178 static void
4179 detail_acetype4(acetype4 type)
4180 {
4181 	if (type >= ACETYPE4_NAMES_MAX) {
4182 		sprintf(get_line(0, 0), "     unknown type");
4183 	} else {
4184 		sprintf(get_line(0, 0), "     %s", acetype4_names[type]);
4185 	}
4186 }
4187 
4188 static void
4189 detail_uint32_bitmap(uint32_t mask, char *mask_names[], int names_max)
4190 {
4191 	char buffy[BUFSIZ], *name;
4192 	char *indent = "     ";
4193 	char *spacer = "  ";
4194 	int pending = 0;
4195 	int bit;
4196 	int len, namelen, spacelen;
4197 
4198 	strcpy(buffy, indent);
4199 	len = strlen(buffy);
4200 	spacelen = strlen(spacer);
4201 
4202 	for (bit = 0; bit < names_max; bit++) {
4203 		if (mask & (1 << bit)) {
4204 			name = mask_names[bit];
4205 			namelen = strlen(name);
4206 			/* 80 - 6 for "NFS:  " = 74 */
4207 			if ((len + spacelen + namelen) >= 74) {
4208 				sprintf(get_line(0, 0), "%s", buffy);
4209 				strcpy(buffy, indent);
4210 				len = strlen(buffy);
4211 				pending = 0;
4212 			}
4213 			(void) strlcat(buffy, spacer, sizeof (buffy));
4214 			(void) strlcat(buffy, name, sizeof (buffy));
4215 			pending = 1;
4216 			len += spacelen + namelen;
4217 		}
4218 	}
4219 	if (pending)
4220 		sprintf(get_line(0, 0), "%s", buffy);
4221 }
4222 
4223 static void
4224 detail_aceflag4(aceflag4 flag)
4225 {
4226 	detail_uint32_bitmap(flag, aceflag4_names, ACEFLAG4_NAMES_MAX);
4227 }
4228 
4229 static void
4230 detail_acemask4(acemask4 mask)
4231 {
4232 	detail_uint32_bitmap(mask, acemask4_names, ACEMASK4_NAMES_MAX);
4233 }
4234 
4235 static void
4236 prt_aclsupport(XDR *xdr)
4237 {
4238 	fattr4_aclsupport val;
4239 
4240 	if (!xdr_fattr4_aclsupport(xdr, &val))
4241 		longjmp(xdr_err, 1);
4242 	if (val & ACL4_SUPPORT_ALLOW_ACL)
4243 		sprintf(get_line(0, 0), "ALLOW ACL Supported");
4244 	if (val & ACL4_SUPPORT_DENY_ACL)
4245 		sprintf(get_line(0, 0), "DENY ACL Supported");
4246 	if (val & ACL4_SUPPORT_AUDIT_ACL)
4247 		sprintf(get_line(0, 0), "AUDIT ACL Supported");
4248 	if (val & ACL4_SUPPORT_ALARM_ACL)
4249 		sprintf(get_line(0, 0), "ALARM ACL Supported");
4250 }
4251 
4252 static void
4253 prt_archive(XDR *xdr)
4254 {
4255 	bool_t val;
4256 
4257 	if (!xdr_bool(xdr, &val))
4258 		longjmp(xdr_err, 1);
4259 	sprintf(get_line(0, 0), "Archived = %s",
4260 	    val ? "TRUE" : "FALSE");
4261 }
4262 
4263 static void
4264 prt_cansettime(XDR *xdr)
4265 {
4266 	bool_t val;
4267 
4268 	if (!xdr_bool(xdr, &val))
4269 		longjmp(xdr_err, 1);
4270 	sprintf(get_line(0, 0), "Server Can Set Time = %s",
4271 	    val ? "TRUE" : "FALSE");
4272 }
4273 
4274 static void
4275 prt_case_insensitive(XDR *xdr)
4276 {
4277 	bool_t val;
4278 
4279 	if (!xdr_bool(xdr, &val))
4280 		longjmp(xdr_err, 1);
4281 	sprintf(get_line(0, 0), "Case Insensitive Lookups = %s",
4282 	    val ? "TRUE" : "FALSE");
4283 }
4284 
4285 static void
4286 prt_case_preserving(XDR *xdr)
4287 {
4288 	bool_t val;
4289 
4290 	if (!xdr_bool(xdr, &val))
4291 		longjmp(xdr_err, 1);
4292 	sprintf(get_line(0, 0), "Case Preserving = %s",
4293 	    val ? "TRUE" : "FALSE");
4294 }
4295 
4296 static void
4297 prt_chown_restricted(XDR *xdr)
4298 {
4299 	bool_t val;
4300 
4301 	if (!xdr_bool(xdr, &val))
4302 		longjmp(xdr_err, 1);
4303 	sprintf(get_line(0, 0), "Chown Is Restricted = %s",
4304 	    val ? "TRUE" : "FALSE");
4305 }
4306 
4307 static void
4308 prt_filehandle(XDR *xdr)
4309 {
4310 	static nfs_fh4 val;
4311 
4312 	if (!xdr_nfs_fh4(xdr, &val))
4313 		longjmp(xdr_err, 1);
4314 	detail_fh4(&val);
4315 	xdr_free(xdr_nfs_fh4, (char *)&val);
4316 }
4317 
4318 static void
4319 prt_fileid(XDR *xdr)
4320 {
4321 	uint64_t val;
4322 
4323 	if (!xdr_uint64_t(xdr, &val))
4324 		longjmp(xdr_err, 1);
4325 	sprintf(get_line(0, 0), "File ID = %llu", val);
4326 }
4327 
4328 static void
4329 prt_mounted_on_fileid(XDR *xdr)
4330 {
4331 	uint64_t val;
4332 
4333 	if (!xdr_uint64_t(xdr, &val))
4334 		longjmp(xdr_err, 1);
4335 	sprintf(get_line(0, 0), "Mounted On File ID = %llu", val);
4336 }
4337 
4338 static void
4339 prt_files_avail(XDR *xdr)
4340 {
4341 	uint64_t val;
4342 
4343 	if (!xdr_uint64_t(xdr, &val))
4344 		longjmp(xdr_err, 1);
4345 	sprintf(get_line(0, 0), "Files Available = %llu", val);
4346 }
4347 
4348 static void
4349 prt_files_free(XDR *xdr)
4350 {
4351 	uint64_t val;
4352 
4353 	if (!xdr_uint64_t(xdr, &val))
4354 		longjmp(xdr_err, 1);
4355 	sprintf(get_line(0, 0), "Files Free = %llu", val);
4356 }
4357 
4358 static void
4359 prt_files_total(XDR *xdr)
4360 {
4361 	uint64_t val;
4362 
4363 	if (!xdr_uint64_t(xdr, &val))
4364 		longjmp(xdr_err, 1);
4365 	sprintf(get_line(0, 0), "Files Total = %llu", val);
4366 }
4367 
4368 static void
4369 prt_fs_location(fs_location4 *fsl)
4370 {
4371 	int i;
4372 
4373 	for (i = 0; i < fsl->server.server_len; i++)
4374 		sprintf(get_line(0, 0), "server: %s",
4375 		    utf8localize(&fsl->server.server_val[i]));
4376 
4377 	detail_pathname4(&fsl->rootpath, "rootpath: ");
4378 }
4379 
4380 static void
4381 prt_fs_locations(XDR *xdr)
4382 {
4383 	static fs_locations4 val;
4384 	int i;
4385 
4386 	if (!xdr_fs_locations4(xdr, &val))
4387 		longjmp(xdr_err, 1);
4388 	sprintf(get_line(0, 0), "[fs_locations]");
4389 	detail_pathname4(&val.fs_root, "fs_root: ");
4390 	for (i = 0; i < val.locations.locations_len; i++)
4391 		prt_fs_location(&val.locations.locations_val[i]);
4392 	xdr_free(xdr_fs_locations4, (char *)&val);
4393 }
4394 
4395 static void
4396 prt_hidden(XDR *xdr)
4397 {
4398 	bool_t val;
4399 
4400 	if (!xdr_bool(xdr, &val))
4401 		longjmp(xdr_err, 1);
4402 	sprintf(get_line(0, 0), "Hidden = %s",
4403 	    val ? "TRUE" : "FALSE");
4404 }
4405 
4406 static void
4407 prt_homogeneous(XDR *xdr)
4408 {
4409 	bool_t val;
4410 
4411 	if (!xdr_bool(xdr, &val))
4412 		longjmp(xdr_err, 1);
4413 	sprintf(get_line(0, 0), "FS Is Homogeneous = %s",
4414 	    val ? "TRUE" : "FALSE");
4415 }
4416 
4417 static void
4418 prt_maxfilesize(XDR *xdr)
4419 {
4420 	uint64_t val;
4421 
4422 	if (!xdr_uint64_t(xdr, &val))
4423 		longjmp(xdr_err, 1);
4424 	sprintf(get_line(0, 0), "Maximum File Size = %llu", val);
4425 }
4426 
4427 static void
4428 prt_maxlink(XDR *xdr)
4429 {
4430 	uint32_t val;
4431 
4432 	if (!xdr_uint32_t(xdr, &val))
4433 		longjmp(xdr_err, 1);
4434 	sprintf(get_line(0, 0), "Maximum Number of Links = %u", val);
4435 }
4436 
4437 static void
4438 prt_maxname(XDR *xdr)
4439 {
4440 	uint32_t val;
4441 
4442 	if (!xdr_uint32_t(xdr, &val))
4443 		longjmp(xdr_err, 1);
4444 	sprintf(get_line(0, 0), "Maximum File Name Length = %u", val);
4445 }
4446 
4447 static void
4448 prt_maxread(XDR *xdr)
4449 {
4450 	uint64_t val;
4451 
4452 	if (!xdr_uint64_t(xdr, &val))
4453 		longjmp(xdr_err, 1);
4454 	sprintf(get_line(0, 0), "Maximum Read Size = %llu", val);
4455 }
4456 
4457 static void
4458 prt_maxwrite(XDR *xdr)
4459 {
4460 	uint64_t val;
4461 
4462 	if (!xdr_uint64_t(xdr, &val))
4463 		longjmp(xdr_err, 1);
4464 
4465 	sprintf(get_line(0, 0), "Maximum Write Size = %llu", val);
4466 }
4467 
4468 static void
4469 prt_mimetype(XDR *xdr)
4470 {
4471 	static utf8string val;
4472 
4473 	if (!xdr_utf8string(xdr, &val))
4474 		longjmp(xdr_err, 1);
4475 	sprintf(get_line(0, 0), "MIME Type = %s", utf8localize(&val));
4476 	xdr_free(xdr_utf8string, (char *)&val);
4477 }
4478 
4479 static void
4480 prt_mode(XDR *xdr)
4481 {
4482 	mode4 val;
4483 
4484 	if (!xdr_mode4(xdr, &val))
4485 		longjmp(xdr_err, 1);
4486 	sprintf(get_line(0, 0), "Mode = 0%03o", val);
4487 }
4488 
4489 static void
4490 prt_no_trunc(XDR *xdr)
4491 {
4492 	bool_t val;
4493 
4494 	if (!xdr_bool(xdr, &val))
4495 		longjmp(xdr_err, 1);
4496 	sprintf(get_line(0, 0), "Long Names Are Error (no_trunc) = %s",
4497 	    val ? "TRUE" : "FALSE");
4498 }
4499 
4500 static void
4501 prt_numlinks(XDR *xdr)
4502 {
4503 	uint32_t val;
4504 
4505 	if (!xdr_uint32_t(xdr, &val))
4506 		longjmp(xdr_err, 1);
4507 	sprintf(get_line(0, 0), "Number of Links = %u", val);
4508 }
4509 
4510 static void
4511 prt_owner(XDR *xdr)
4512 {
4513 	static utf8string val;
4514 
4515 	if (!xdr_utf8string(xdr, &val))
4516 		longjmp(xdr_err, 1);
4517 	sprintf(get_line(0, 0), "Owner = %s", utf8localize(&val));
4518 	xdr_free(xdr_utf8string, (char *)&val);
4519 }
4520 
4521 static void
4522 prt_owner_group(XDR *xdr)
4523 {
4524 	static utf8string val;
4525 
4526 	if (!xdr_utf8string(xdr, &val))
4527 		longjmp(xdr_err, 1);
4528 	sprintf(get_line(0, 0), "Group = %s", utf8localize(&val));
4529 	xdr_free(xdr_utf8string, (char *)&val);
4530 }
4531 
4532 static void
4533 prt_quota_avail_hard(XDR *xdr)
4534 {
4535 	uint64_t val;
4536 
4537 	if (!xdr_uint64_t(xdr, &val))
4538 		longjmp(xdr_err, 1);
4539 	sprintf(get_line(0, 0), "Quota Hard Limit = %llu", val);
4540 }
4541 
4542 static void
4543 prt_quota_avail_soft(XDR *xdr)
4544 {
4545 	uint64_t val;
4546 
4547 	if (!xdr_uint64_t(xdr, &val))
4548 		longjmp(xdr_err, 1);
4549 	sprintf(get_line(0, 0), "Quota Soft Limit = %llu", val);
4550 }
4551 
4552 static void
4553 prt_quota_used(XDR *xdr)
4554 {
4555 	uint64_t val;
4556 
4557 	if (!xdr_uint64_t(xdr, &val))
4558 		longjmp(xdr_err, 1);
4559 	sprintf(get_line(0, 0), "Quota Used = %llu", val);
4560 }
4561 
4562 static void
4563 prt_rawdev(XDR *xdr)
4564 {
4565 	specdata4 val;
4566 
4567 	if (!xdr_specdata4(xdr, &val))
4568 		longjmp(xdr_err, 1);
4569 	sprintf(get_line(0, 0), "Raw Device ID = %u, %u",
4570 	    val.specdata1, val.specdata2);
4571 }
4572 
4573 static void
4574 prt_space_avail(XDR *xdr)
4575 {
4576 	uint64_t val;
4577 
4578 	if (!xdr_uint64_t(xdr, &val))
4579 		longjmp(xdr_err, 1);
4580 	sprintf(get_line(0, 0), "Space Available = %llu", val);
4581 }
4582 
4583 static void
4584 prt_space_free(XDR *xdr)
4585 {
4586 	uint64_t val;
4587 
4588 	if (!xdr_uint64_t(xdr, &val))
4589 		longjmp(xdr_err, 1);
4590 	sprintf(get_line(0, 0), "Space Free = %llu", val);
4591 }
4592 
4593 static void
4594 prt_space_total(XDR *xdr)
4595 {
4596 	uint64_t val;
4597 
4598 	if (!xdr_uint64_t(xdr, &val))
4599 		longjmp(xdr_err, 1);
4600 	sprintf(get_line(0, 0), "Total Disk Space = %llu", val);
4601 }
4602 
4603 static void
4604 prt_space_used(XDR *xdr)
4605 {
4606 	uint64_t val;
4607 
4608 	if (!xdr_uint64_t(xdr, &val))
4609 		longjmp(xdr_err, 1);
4610 	sprintf(get_line(0, 0), "Space Used (this object) = %llu", val);
4611 }
4612 
4613 static void
4614 prt_system(XDR *xdr)
4615 {
4616 	bool_t val;
4617 
4618 	if (!xdr_bool(xdr, &val))
4619 		longjmp(xdr_err, 1);
4620 	sprintf(get_line(0, 0), "System File = %s",
4621 	    val ? "TRUE" : "FALSE");
4622 }
4623 
4624 static void
4625 prt_time_access(XDR *xdr)
4626 {
4627 	nfstime4 val;
4628 
4629 	if (!xdr_nfstime4(xdr, &val))
4630 		longjmp(xdr_err, 1);
4631 	sprintf(get_line(0, 0), "Last Access Time = %s",
4632 	    format_time(val.seconds, val.nseconds));
4633 }
4634 
4635 static void
4636 prt_time_access_set(XDR *xdr)
4637 {
4638 	settime4 val;
4639 
4640 	if (!xdr_settime4(xdr, &val))
4641 		longjmp(xdr_err, 1);
4642 	if (val.set_it == SET_TO_CLIENT_TIME4) {
4643 		sprintf(get_line(0, 0), "Access Time = %s (set to client time)",
4644 		    format_time(val.settime4_u.time.seconds,
4645 		    val.settime4_u.time.nseconds));
4646 	} else if (val.set_it == SET_TO_SERVER_TIME4) {
4647 		sprintf(get_line(0, 0), "Access Time (set to server time)");
4648 	} else
4649 		longjmp(xdr_err, 1);
4650 }
4651 
4652 static void
4653 prt_time_backup(XDR *xdr)
4654 {
4655 	nfstime4 val;
4656 
4657 	if (!xdr_nfstime4(xdr, &val))
4658 		longjmp(xdr_err, 1);
4659 	sprintf(get_line(0, 0), "Last Backup Time = %s",
4660 	    format_time(val.seconds, val.nseconds));
4661 }
4662 
4663 static void
4664 prt_time_create(XDR *xdr)
4665 {
4666 	nfstime4 val;
4667 
4668 	if (!xdr_nfstime4(xdr, &val))
4669 		longjmp(xdr_err, 1);
4670 	sprintf(get_line(0, 0), "Creation Time = %s",
4671 	    format_time(val.seconds, val.nseconds));
4672 }
4673 
4674 static void
4675 prt_time_delta(XDR *xdr)
4676 {
4677 	nfstime4 val;
4678 
4679 	if (!xdr_nfstime4(xdr, &val))
4680 		longjmp(xdr_err, 1);
4681 	sprintf(get_line(0, 0), "Server Time Granularity = %lld.%09d sec",
4682 	    val.seconds, val.nseconds);
4683 }
4684 
4685 static void
4686 prt_time_metadata(XDR *xdr)
4687 {
4688 	nfstime4 val;
4689 
4690 	if (!xdr_nfstime4(xdr, &val))
4691 		longjmp(xdr_err, 1);
4692 	sprintf(get_line(0, 0), "Last Metadata Change Time = %s",
4693 	    format_time(val.seconds, val.nseconds));
4694 }
4695 
4696 static void
4697 prt_time_modify(XDR *xdr)
4698 {
4699 	nfstime4 val;
4700 
4701 	if (!xdr_nfstime4(xdr, &val))
4702 		longjmp(xdr_err, 1);
4703 	sprintf(get_line(0, 0), "Last Modification Time = %s",
4704 	    format_time(val.seconds, val.nseconds));
4705 }
4706 
4707 static void
4708 prt_time_modify_set(XDR *xdr)
4709 {
4710 	settime4 val;
4711 
4712 	if (!xdr_settime4(xdr, &val))
4713 		longjmp(xdr_err, 1);
4714 	if (val.set_it == SET_TO_CLIENT_TIME4) {
4715 		sprintf(get_line(0, 0),
4716 		    "Modification Time = %s (set to client time)",
4717 		    format_time(val.settime4_u.time.seconds,
4718 		    val.settime4_u.time.nseconds));
4719 	} else if (val.set_it == SET_TO_SERVER_TIME4) {
4720 		sprintf(get_line(0, 0),
4721 		    "Modification Time (set to server time)");
4722 	} else
4723 		longjmp(xdr_err, 1);
4724 }
4725 
4726 /*
4727  * Display the UTF8 string that is next in the XDR stream.
4728  */
4729 
4730 static void
4731 showxdr_utf8string(char *fmt)
4732 {
4733 	static utf8string string;
4734 
4735 	if (!xdr_utf8string(&xdrm, &string))
4736 		longjmp(xdr_err, 1);
4737 	sprintf(get_line(0, 0), fmt, utf8localize(&string));
4738 	xdr_free(xdr_utf8string, (char *)&string);
4739 }
4740 
4741 /*
4742  * utf8string is defined in nfs4_prot.x as an opaque array, which means
4743  * when it is decoded into a string, the string might not have a trailing
4744  * null.  Also, the string will still be encoded in UTF-8, rather than
4745  * whatever character encoding is associated with the current locale.  This
4746  * routine converts a utf8string into a (null-terminated) C string.  One day
4747  * it will convert into the current character encoding, too.  To avoid
4748  * dealing with storage management issues, it allocates storage for each
4749  * new string, then this storage is "freed" when the packet has been
4750  * processed.
4751  */
4752 
4753 #define	MAX_UTF8_STRINGS	512
4754 
4755 static char *utf_buf[MAX_UTF8_STRINGS];
4756 static size_t utf_buflen[MAX_UTF8_STRINGS];
4757 static uint_t cur_utf_buf = 0;
4758 
4759 static char *
4760 utf8localize(utf8string *utf8str)
4761 {
4762 	size_t newsize, oldsize, len;
4763 	char *result, *cp;
4764 
4765 	len = utf8str->utf8string_len;
4766 	if (len == 0)
4767 		return ("");
4768 	if (cur_utf_buf >= MAX_UTF8_STRINGS)
4769 		return ("[Too Many UTF-8 Strings]");
4770 
4771 	newsize = oldsize = utf_buflen[cur_utf_buf];
4772 
4773 
4774 	if (oldsize < len + 1) {
4775 		/* truncate opaques at NFS4_OPAQUE_LIMIT */
4776 		if (len > NFS4_OPAQUE_LIMIT)
4777 			len = NFS4_OPAQUE_LIMIT;
4778 		newsize = len + 1;
4779 	}
4780 	if (newsize != oldsize) {
4781 		utf_buf[cur_utf_buf] = realloc(utf_buf[cur_utf_buf],
4782 		    newsize);
4783 		if (utf_buf[cur_utf_buf] == NULL) {
4784 			pr_err("out of memory\n");
4785 			utf_buflen[cur_utf_buf] = 0;
4786 			return ("");
4787 		}
4788 		utf_buflen[cur_utf_buf] = newsize;
4789 	}
4790 
4791 	result = utf_buf[cur_utf_buf];
4792 	strncpy(result, utf8str->utf8string_val, len);
4793 	result[len] = '\0';
4794 	for (cp = result; cp < result + len; cp++) {
4795 		if (!isprint(*cp)) {
4796 			*cp = '.';
4797 		}
4798 	}
4799 
4800 	cur_utf_buf++;
4801 
4802 	return (result);
4803 }
4804 
4805 static void
4806 utf8free()
4807 {
4808 	cur_utf_buf = 0;
4809 }
4810 
4811 
4812 /*
4813  * adler16(): adler32 hash code shamelessly copied and mutiliated from
4814  * usr/src/uts/common/io/ppp/spppcomp/zlib.[ch]
4815  *
4816  * The alg was originally created to provide a running
4817  * checksum, but we don't need that -- we just want to
4818  * chksum data described by buf,len; therefore, the first
4819  * parameter was removed (held the running checksum),
4820  * and s1/s2 are always set to their required initial
4821  * values (1 and 0).  I also ripped out code which only
4822  * applied to large data sets (bufs larger than 5k).  All
4823  * I wanted was their core checksum alg (which is supposed
4824  * to do really well).  The v2/v3 hash alg didn't work well
4825  * at all for v4 stuff -- it produced too many collisions.
4826  *
4827  * The copyright info from uts/common/io/ppp/spppcomp/zlib.[ch]
4828  * is included below.
4829  */
4830 
4831 /* -----zlib.c copyright info below */
4832 /*
4833  * Copyright 2000 Sun Microsystems, Inc.
4834  * All rights reserved.
4835  *
4836  * Updated from zlib-1.0.4 to zlib-1.1.3 by James Carlson.
4837  *
4838  * This file is derived from various .h and .c files from the zlib-1.0.4
4839  * distribution by Jean-loup Gailly and Mark Adler, with some additions
4840  * by Paul Mackerras to aid in implementing Deflate compression and
4841  * decompression for PPP packets.  See zlib.h for conditions of
4842  * distribution and use.
4843  *
4844  * Changes that have been made include:
4845  * - added Z_PACKET_FLUSH (see zlib.h for details)
4846  * - added inflateIncomp and deflateOutputPending
4847  * - allow strm->next_out to be NULL, meaning discard the output
4848  *
4849  * $Id: zlib.c,v 1.11 1998/09/13 23:37:12 paulus Exp $
4850  */
4851 /* +++ adler32.c */
4852 /*
4853  * adler32.c -- compute the Adler-32 checksum of a data stream
4854  * Copyright (C) 1995-1998 Mark Adler
4855  * For conditions of distribution and use, see copyright notice in zlib.h
4856  */
4857 /* From: adler32.c,v 1.10 1996/05/22 11:52:18 me Exp $ */
4858 /* -----zlib.c copyright info above */
4859 
4860 /* -----zlib.h copyright info below */
4861 /*
4862  * Copyright 2000 Sun Microsystems, Inc.
4863  * All rights reserved.
4864  *
4865  * Permission to use, copy, modify, and distribute this software and
4866  * its documentation is hereby granted, provided that the above
4867  * copyright notice appears in all copies.
4868  *
4869  * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
4870  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
4871  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
4872  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT.  SUN SHALL NOT BE LIABLE
4873  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
4874  * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
4875  *
4876  * This file has been altered from its original by Sun Microsystems to
4877  * fit local coding style.
4878  */
4879 /* -----zlib.h copyright info above */
4880 
4881 #define	DO1(buf, i)  {s1 += buf[i]; s2 += s1; }
4882 #define	DO2(buf, i)  DO1(buf, i); DO1(buf, i+1);
4883 #define	DO4(buf, i)  DO2(buf, i); DO2(buf, i+2);
4884 #define	DO8(buf, i)  DO4(buf, i); DO4(buf, i+4);
4885 #define	DO16(buf)   DO8(buf, 0); DO8(buf, 8);
4886 
4887 static uint32_t
4888 adler16(void *p, int len)
4889 {
4890 	uint32_t s1 = 1;
4891 	uint32_t s2 = 0;
4892 	uchar_t *buf = p;
4893 
4894 	while (len >= 16) {
4895 		DO16(buf);
4896 		buf += 16;
4897 		len -= 16;
4898 	}
4899 
4900 	while (len > 0) {
4901 		s1 += *buf++;
4902 		s2 += s1;
4903 		len--;
4904 	}
4905 
4906 	return ((uint32_t)(s2 ^ s1) & 0xFFFFU);
4907 }
4908