xref: /freebsd/contrib/lib9p/utils.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 /*
2  * Copyright 2016 Jakub Klama <jceel@FreeBSD.org>
3  * All rights reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted providing that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include <errno.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <assert.h>
35 #include <inttypes.h>
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <sys/uio.h>
39 #if defined(__FreeBSD__)
40 #include <sys/sbuf.h>
41 #else
42 #include "sbuf/sbuf.h"
43 #endif
44 #include "lib9p.h"
45 #include "fcall.h"
46 #include "linux_errno.h"
47 
48 #ifdef __APPLE__
49   #define GETGROUPS_GROUP_TYPE_IS_INT
50 #endif
51 
52 #define N(ary)          (sizeof(ary) / sizeof(*ary))
53 
54 /* See l9p_describe_bits() below. */
55 struct descbits {
56 	uint64_t	db_mask;	/* mask value */
57 	uint64_t	db_match;	/* match value */
58 	const char	*db_name;	/* name for matched value */
59 };
60 
61 
62 static bool l9p_describe_bits(const char *, uint64_t, const char *,
63     const struct descbits *, struct sbuf *);
64 static void l9p_describe_fid(const char *, uint32_t, struct sbuf *);
65 static void l9p_describe_mode(const char *, uint32_t, struct sbuf *);
66 static void l9p_describe_name(const char *, char *, struct sbuf *);
67 static void l9p_describe_perm(const char *, uint32_t, struct sbuf *);
68 static void l9p_describe_lperm(const char *, uint32_t, struct sbuf *);
69 static void l9p_describe_qid(const char *, struct l9p_qid *, struct sbuf *);
70 static void l9p_describe_l9stat(const char *, struct l9p_stat *,
71     enum l9p_version, struct sbuf *);
72 static void l9p_describe_statfs(const char *, struct l9p_statfs *,
73     struct sbuf *);
74 static void l9p_describe_time(struct sbuf *, const char *, uint64_t, uint64_t);
75 static void l9p_describe_readdir(struct sbuf *, struct l9p_f_io *);
76 static void l9p_describe_size(const char *, uint64_t, struct sbuf *);
77 static void l9p_describe_ugid(const char *, uint32_t, struct sbuf *);
78 static void l9p_describe_getattr_mask(uint64_t, struct sbuf *);
79 static void l9p_describe_unlinkat_flags(const char *, uint32_t, struct sbuf *);
80 static const char *lookup_linux_errno(uint32_t);
81 
82 /*
83  * Using indexed initializers, we can have these occur in any order.
84  * Using adjacent-string concatenation ("T" #name, "R" #name), we
85  * get both Tfoo and Rfoo strings with one copy of the name.
86  * Alas, there is no stupid cpp trick to lowercase-ify, so we
87  * have to write each name twice.  In which case we might as well
88  * make the second one a string in the first place and not bother
89  * with the stringizing.
90  *
91  * This table should have entries for each enum value in fcall.h.
92  */
93 #define X(NAME, name)	[L9P_T##NAME - L9P__FIRST] = "T" name, \
94 			[L9P_R##NAME - L9P__FIRST] = "R" name
95 static const char *ftype_names[] = {
96 	X(VERSION,	"version"),
97 	X(AUTH,		"auth"),
98 	X(ATTACH,	"attach"),
99 	X(ERROR,	"error"),
100 	X(LERROR,	"lerror"),
101 	X(FLUSH,	"flush"),
102 	X(WALK,		"walk"),
103 	X(OPEN,		"open"),
104 	X(CREATE,	"create"),
105 	X(READ,		"read"),
106 	X(WRITE,	"write"),
107 	X(CLUNK,	"clunk"),
108 	X(REMOVE,	"remove"),
109 	X(STAT,		"stat"),
110 	X(WSTAT,	"wstat"),
111 	X(STATFS,	"statfs"),
112 	X(LOPEN,	"lopen"),
113 	X(LCREATE,	"lcreate"),
114 	X(SYMLINK,	"symlink"),
115 	X(MKNOD,	"mknod"),
116 	X(RENAME,	"rename"),
117 	X(READLINK,	"readlink"),
118 	X(GETATTR,	"getattr"),
119 	X(SETATTR,	"setattr"),
120 	X(XATTRWALK,	"xattrwalk"),
121 	X(XATTRCREATE,	"xattrcreate"),
122 	X(READDIR,	"readdir"),
123 	X(FSYNC,	"fsync"),
124 	X(LOCK,		"lock"),
125 	X(GETLOCK,	"getlock"),
126 	X(LINK,		"link"),
127 	X(MKDIR,	"mkdir"),
128 	X(RENAMEAT,	"renameat"),
129 	X(UNLINKAT,	"unlinkat"),
130 };
131 #undef X
132 
133 void
134 l9p_seek_iov(struct iovec *iov1, size_t niov1, struct iovec *iov2,
135     size_t *niov2, size_t seek)
136 {
137 	size_t remainder = 0;
138 	size_t left = seek;
139 	size_t i, j;
140 
141 	for (i = 0; i < niov1; i++) {
142 		size_t toseek = MIN(left, iov1[i].iov_len);
143 		left -= toseek;
144 
145 		if (toseek == iov1[i].iov_len)
146 			continue;
147 
148 		if (left == 0) {
149 			remainder = toseek;
150 			break;
151 		}
152 	}
153 
154 	for (j = i; j < niov1; j++) {
155 		iov2[j - i].iov_base = (char *)iov1[j].iov_base + remainder;
156 		iov2[j - i].iov_len = iov1[j].iov_len - remainder;
157 		remainder = 0;
158 	}
159 
160 	*niov2 = j - i;
161 }
162 
163 size_t
164 l9p_truncate_iov(struct iovec *iov, size_t niov, size_t length)
165 {
166 	size_t i, done = 0;
167 
168 	for (i = 0; i < niov; i++) {
169 		size_t toseek = MIN(length - done, iov[i].iov_len);
170 		done += toseek;
171 
172 		if (toseek < iov[i].iov_len) {
173 			iov[i].iov_len = toseek;
174 			return (i + 1);
175 		}
176 	}
177 
178 	return (niov);
179 }
180 
181 /*
182  * This wrapper for getgrouplist() that malloc'ed memory, and
183  * papers over FreeBSD vs Mac differences in the getgrouplist()
184  * argument types.
185  *
186  * Note that this function guarantees that *either*:
187  *     return value != NULL and *angroups has been set
188  * or: return value == NULL and *angroups is 0
189  */
190 gid_t *
191 l9p_getgrlist(const char *name, gid_t basegid, int *angroups)
192 {
193 #ifdef GETGROUPS_GROUP_TYPE_IS_INT
194 	int i, *int_groups;
195 #endif
196 	gid_t *groups;
197 	int ngroups;
198 
199 	/*
200 	 * Todo, perhaps: while getgrouplist() returns -1, expand.
201 	 * For now just use NGROUPS_MAX.
202 	 */
203 	ngroups = NGROUPS_MAX;
204 	groups = malloc((size_t)ngroups * sizeof(*groups));
205 #ifdef GETGROUPS_GROUP_TYPE_IS_INT
206 	int_groups = groups ? malloc((size_t)ngroups * sizeof(*int_groups)) :
207 	    NULL;
208 	if (int_groups == NULL) {
209 		free(groups);
210 		groups = NULL;
211 	}
212 #endif
213 	if (groups == NULL) {
214 		*angroups = 0;
215 		return (NULL);
216 	}
217 #ifdef GETGROUPS_GROUP_TYPE_IS_INT
218 	(void) getgrouplist(name, (int)basegid, int_groups, &ngroups);
219 	for (i = 0; i < ngroups; i++)
220 		groups[i] = (gid_t)int_groups[i];
221 #else
222 	(void) getgrouplist(name, basegid, groups, &ngroups);
223 #endif
224 	*angroups = ngroups;
225 	return (groups);
226 }
227 
228 /*
229  * For the various debug describe ops: decode bits in a bit-field-y
230  * value.  For example, we might produce:
231  *     value=0x3c[FOO,BAR,QUUX,?0x20]
232  * when FOO is bit 0x10, BAR is 0x08, and QUUX is 0x04 (as defined
233  * by the table).  This leaves 0x20 (bit 5) as a mystery, while bits
234  * 4, 3, and 2 were decoded.  (Bits 0 and 1 were 0 on input hence
235  * were not attempted here.)
236  *
237  * For general use we take a uint64_t <value>.  The bit description
238  * table <db> is an array of {mask, match, str} values ending with
239  * {0, 0, NULL}.
240  *
241  * If <str> is non-NULL we'll print it and the mask as well (if
242  * str is NULL we'll print neither).  The mask is always printed in
243  * hex at the moment.  See undec description too.
244  *
245  * For convenience, you can use a mask-and-match value, e.g., to
246  * decode a 2-bit field in bits 0 and 1 you can mask against 3 and
247  * match the values 0, 1, 2, and 3.  To handle this, make sure that
248  * all masks-with-same-match are sequential.
249  *
250  * If there are any nonzero undecoded bits, print them after
251  * all the decode-able bits have been handled.
252  *
253  * The <oc> argument defines the open and close bracket characters,
254  * typically "[]", that surround the entire string.  If NULL, no
255  * brackets are added, else oc[0] goes in the front and oc[1] at
256  * the end, after printing any <str><value> part.
257  *
258  * Returns true if it printed anything (other than the implied
259  * str-and-value, that is).
260  */
261 static bool
262 l9p_describe_bits(const char *str, uint64_t value, const char *oc,
263     const struct descbits *db, struct sbuf *sb)
264 {
265 	const char *sep;
266 	char bracketbuf[2] = "";
267 	bool printed = false;
268 
269 	if (str != NULL)
270 		sbuf_printf(sb, "%s0x%" PRIx64, str, value);
271 
272 	if (oc != NULL)
273 		bracketbuf[0] = oc[0];
274 	sep = bracketbuf;
275 	for (; db->db_name != NULL; db++) {
276 		if ((value & db->db_mask) == db->db_match) {
277 			sbuf_printf(sb, "%s%s", sep, db->db_name);
278 			sep = ",";
279 			printed = true;
280 
281 			/*
282 			 * Clear the field, and make sure we
283 			 * won't match a zero-valued field with
284 			 * this same mask.
285 			 */
286 			value &= ~db->db_mask;
287 			while (db[1].db_mask == db->db_mask &&
288 			    db[1].db_name != NULL)
289 				db++;
290 		}
291 	}
292 	if (value != 0) {
293 		sbuf_printf(sb, "%s?0x%" PRIx64, sep, value);
294 		printed = true;
295 	}
296 	if (printed && oc != NULL) {
297 		bracketbuf[0] = oc[1];
298 		sbuf_cat(sb, bracketbuf);
299 	}
300 	return (printed);
301 }
302 
303 /*
304  * Show file ID.
305  */
306 static void
307 l9p_describe_fid(const char *str, uint32_t fid, struct sbuf *sb)
308 {
309 
310 	sbuf_printf(sb, "%s%" PRIu32, str, fid);
311 }
312 
313 /*
314  * Show user or group ID.
315  */
316 static void
317 l9p_describe_ugid(const char *str, uint32_t ugid, struct sbuf *sb)
318 {
319 
320 	sbuf_printf(sb, "%s%" PRIu32, str, ugid);
321 }
322 
323 /*
324  * Show file mode (O_RDWR, O_RDONLY, etc).  The argument is
325  * an l9p_omode, not a Linux flags mode.  Linux flags are
326  * decoded with l9p_describe_lflags.
327  */
328 static void
329 l9p_describe_mode(const char *str, uint32_t mode, struct sbuf *sb)
330 {
331 	static const struct descbits bits[] = {
332 		{ L9P_OACCMODE,	L9P_OREAD,	"OREAD" },
333 		{ L9P_OACCMODE,	L9P_OWRITE,	"OWRITE" },
334 		{ L9P_OACCMODE,	L9P_ORDWR,	"ORDWR" },
335 		{ L9P_OACCMODE,	L9P_OEXEC,	"OEXEC" },
336 
337 		{ L9P_OCEXEC,	L9P_OCEXEC,	"OCEXEC" },
338 		{ L9P_ODIRECT,	L9P_ODIRECT,	"ODIRECT" },
339 		{ L9P_ORCLOSE,	L9P_ORCLOSE,	"ORCLOSE" },
340 		{ L9P_OTRUNC,	L9P_OTRUNC,	"OTRUNC" },
341 		{ 0, 0, NULL }
342 	};
343 
344 	(void) l9p_describe_bits(str, mode, "[]", bits, sb);
345 }
346 
347 /*
348  * Show Linux mode/flags.
349  */
350 static void
351 l9p_describe_lflags(const char *str, uint32_t flags, struct sbuf *sb)
352 {
353 	static const struct descbits bits[] = {
354 	    { L9P_OACCMODE,	L9P_OREAD,		"O_READ" },
355 	    { L9P_OACCMODE,	L9P_OWRITE,		"O_WRITE" },
356 	    { L9P_OACCMODE,	L9P_ORDWR,		"O_RDWR" },
357 	    { L9P_OACCMODE,	L9P_OEXEC,		"O_EXEC" },
358 
359 	    { L9P_L_O_APPEND,	L9P_L_O_APPEND,		"O_APPEND" },
360 	    { L9P_L_O_CLOEXEC,	L9P_L_O_CLOEXEC,	"O_CLOEXEC" },
361 	    { L9P_L_O_CREAT,	L9P_L_O_CREAT,		"O_CREAT" },
362 	    { L9P_L_O_DIRECT,	L9P_L_O_DIRECT,		"O_DIRECT" },
363 	    { L9P_L_O_DIRECTORY, L9P_L_O_DIRECTORY,	"O_DIRECTORY" },
364 	    { L9P_L_O_DSYNC,	L9P_L_O_DSYNC,		"O_DSYNC" },
365 	    { L9P_L_O_EXCL,	L9P_L_O_EXCL,		"O_EXCL" },
366 	    { L9P_L_O_FASYNC,	L9P_L_O_FASYNC,		"O_FASYNC" },
367 	    { L9P_L_O_LARGEFILE, L9P_L_O_LARGEFILE,	"O_LARGEFILE" },
368 	    { L9P_L_O_NOATIME,	L9P_L_O_NOATIME,	"O_NOATIME" },
369 	    { L9P_L_O_NOCTTY,	L9P_L_O_NOCTTY,		"O_NOCTTY" },
370 	    { L9P_L_O_NOFOLLOW,	L9P_L_O_NOFOLLOW,	"O_NOFOLLOW" },
371 	    { L9P_L_O_NONBLOCK,	L9P_L_O_NONBLOCK,	"O_NONBLOCK" },
372 	    { L9P_L_O_PATH,	L9P_L_O_PATH,		"O_PATH" },
373 	    { L9P_L_O_SYNC,	L9P_L_O_SYNC,		"O_SYNC" },
374 	    { L9P_L_O_TMPFILE,	L9P_L_O_TMPFILE,	"O_TMPFILE" },
375 	    { L9P_L_O_TMPFILE,	L9P_L_O_TMPFILE,	"O_TMPFILE" },
376 	    { L9P_L_O_TRUNC,	L9P_L_O_TRUNC,		"O_TRUNC" },
377 	    { 0, 0, NULL }
378 	};
379 
380 	(void) l9p_describe_bits(str, flags, "[]", bits, sb);
381 }
382 
383 /*
384  * Show file name or other similar, potentially-very-long string.
385  * Actual strings get quotes, a NULL name (if it occurs) gets
386  * <null> (no quotes), so you can tell the difference.
387  */
388 static void
389 l9p_describe_name(const char *str, char *name, struct sbuf *sb)
390 {
391 	size_t len;
392 
393 	if (name == NULL) {
394 		sbuf_printf(sb, "%s<null>", str);
395 		return;
396 	}
397 
398 	len = strlen(name);
399 
400 	if (len > 32)
401 		sbuf_printf(sb, "%s\"%.*s...\"", str, 32 - 3, name);
402 	else
403 		sbuf_printf(sb, "%s\"%.*s\"", str, (int)len, name);
404 }
405 
406 /*
407  * Show permissions (rwx etc).  Prints the value in hex only if
408  * the rwx bits do not cover the entire value.
409  */
410 static void
411 l9p_describe_perm(const char *str, uint32_t mode, struct sbuf *sb)
412 {
413 	char pbuf[12];
414 
415 	strmode(mode & 0777, pbuf);
416 	if ((mode & ~(uint32_t)0777) != 0)
417 		sbuf_printf(sb, "%s0x%" PRIx32 "<%.9s>", str, mode, pbuf + 1);
418 	else
419 		sbuf_printf(sb, "%s<%.9s>", str, pbuf + 1);
420 }
421 
422 /*
423  * Show "extended" permissions: regular permissions, but also the
424  * various DM* extension bits from 9P2000.u.
425  */
426 static void
427 l9p_describe_ext_perm(const char *str, uint32_t mode, struct sbuf *sb)
428 {
429 	static const struct descbits bits[] = {
430 		{ L9P_DMDIR,	L9P_DMDIR,	"DMDIR" },
431 		{ L9P_DMAPPEND,	L9P_DMAPPEND,	"DMAPPEND" },
432 		{ L9P_DMEXCL,	L9P_DMEXCL,	"DMEXCL" },
433 		{ L9P_DMMOUNT,	L9P_DMMOUNT,	"DMMOUNT" },
434 		{ L9P_DMAUTH,	L9P_DMAUTH,	"DMAUTH" },
435 		{ L9P_DMTMP,	L9P_DMTMP,	"DMTMP" },
436 		{ L9P_DMSYMLINK, L9P_DMSYMLINK,	"DMSYMLINK" },
437 		{ L9P_DMDEVICE,	L9P_DMDEVICE,	"DMDEVICE" },
438 		{ L9P_DMNAMEDPIPE, L9P_DMNAMEDPIPE, "DMNAMEDPIPE" },
439 		{ L9P_DMSOCKET,	L9P_DMSOCKET,	"DMSOCKET" },
440 		{ L9P_DMSETUID,	L9P_DMSETUID,	"DMSETUID" },
441 		{ L9P_DMSETGID,	L9P_DMSETGID,	"DMSETGID" },
442 		{ 0, 0, NULL }
443 	};
444 	bool need_sep;
445 
446 	sbuf_printf(sb, "%s[", str);
447 	need_sep = l9p_describe_bits(NULL, mode & ~(uint32_t)0777, NULL,
448 	    bits, sb);
449 	l9p_describe_perm(need_sep ? "," : "", mode & 0777, sb);
450 	sbuf_cat(sb, "]");
451 }
452 
453 /*
454  * Show Linux-specific permissions: regular permissions, but also
455  * the S_IFMT field.
456  */
457 static void
458 l9p_describe_lperm(const char *str, uint32_t mode, struct sbuf *sb)
459 {
460 	static const struct descbits bits[] = {
461 		{ S_IFMT,	S_IFIFO,	"S_IFIFO" },
462 		{ S_IFMT,	S_IFCHR,	"S_IFCHR" },
463 		{ S_IFMT,	S_IFDIR,	"S_IFDIR" },
464 		{ S_IFMT,	S_IFBLK,	"S_IFBLK" },
465 		{ S_IFMT,	S_IFREG,	"S_IFREG" },
466 		{ S_IFMT,	S_IFLNK,	"S_IFLNK" },
467 		{ S_IFMT,	S_IFSOCK,	"S_IFSOCK" },
468 		{ 0, 0, NULL }
469 	};
470 	bool need_sep;
471 
472 	sbuf_printf(sb, "%s[", str);
473 	need_sep = l9p_describe_bits(NULL, mode & ~(uint32_t)0777, NULL,
474 	    bits, sb);
475 	l9p_describe_perm(need_sep ? "," : "", mode & 0777, sb);
476 	sbuf_cat(sb, "]");
477 }
478 
479 /*
480  * Show qid (<type, version, path> tuple).
481  */
482 static void
483 l9p_describe_qid(const char *str, struct l9p_qid *qid, struct sbuf *sb)
484 {
485 	static const struct descbits bits[] = {
486 		/*
487 		 * NB: L9P_QTFILE is 0, i.e., is implied by no
488 		 * other bits being set.  We get this produced
489 		 * when we mask against 0xff and compare for
490 		 * L9P_QTFILE, but we must do it first so that
491 		 * we mask against the original (not-adjusted)
492 		 * value.
493 		 */
494 		{ 0xff,		L9P_QTFILE,	"FILE" },
495 		{ L9P_QTDIR,	L9P_QTDIR,	"DIR" },
496 		{ L9P_QTAPPEND,	L9P_QTAPPEND,	"APPEND" },
497 		{ L9P_QTEXCL,	L9P_QTEXCL,	"EXCL" },
498 		{ L9P_QTMOUNT,	L9P_QTMOUNT,	"MOUNT" },
499 		{ L9P_QTAUTH,	L9P_QTAUTH,	"AUTH" },
500 		{ L9P_QTTMP,	L9P_QTTMP,	"TMP" },
501 		{ L9P_QTSYMLINK, L9P_QTSYMLINK,	"SYMLINK" },
502 		{ 0, 0, NULL }
503 	};
504 
505 	assert(qid != NULL);
506 
507 	sbuf_cat(sb, str);
508 	(void) l9p_describe_bits("<", qid->type, "[]", bits, sb);
509 	sbuf_printf(sb, ",%" PRIu32 ",0x%016" PRIx64 ">",
510 	    qid->version, qid->path);
511 }
512 
513 /*
514  * Show size.
515  */
516 static void
517 l9p_describe_size(const char *str, uint64_t size, struct sbuf *sb)
518 {
519 
520 	sbuf_printf(sb, "%s%" PRIu64, str, size);
521 }
522 
523 /*
524  * Show l9stat (including 9P2000.u extensions if appropriate).
525  */
526 static void
527 l9p_describe_l9stat(const char *str, struct l9p_stat *st,
528     enum l9p_version version, struct sbuf *sb)
529 {
530 	bool dotu = version >= L9P_2000U;
531 
532 	assert(st != NULL);
533 
534 	sbuf_printf(sb, "%stype=0x%04" PRIx32 " dev=0x%08" PRIx32, str,
535 	    st->type, st->dev);
536 	l9p_describe_qid(" qid=", &st->qid, sb);
537 	l9p_describe_ext_perm(" mode=", st->mode, sb);
538 	if (st->atime != (uint32_t)-1)
539 		sbuf_printf(sb, " atime=%" PRIu32, st->atime);
540 	if (st->mtime != (uint32_t)-1)
541 		sbuf_printf(sb, " mtime=%" PRIu32, st->mtime);
542 	if (st->length != (uint64_t)-1)
543 		sbuf_printf(sb, " length=%" PRIu64, st->length);
544 	l9p_describe_name(" name=", st->name, sb);
545 	/*
546 	 * It's pretty common to have NULL name+gid+muid.  They're
547 	 * just noise if NULL *and* dot-u; decode only if non-null
548 	 * or not-dot-u.
549 	 */
550 	if (st->uid != NULL || !dotu)
551 		l9p_describe_name(" uid=", st->uid, sb);
552 	if (st->gid != NULL || !dotu)
553 		l9p_describe_name(" gid=", st->gid, sb);
554 	if (st->muid != NULL || !dotu)
555 		l9p_describe_name(" muid=", st->muid, sb);
556 	if (dotu) {
557 		if (st->extension != NULL)
558 			l9p_describe_name(" extension=", st->extension, sb);
559 		sbuf_printf(sb,
560 		    " n_uid=%" PRIu32 " n_gid=%" PRIu32 " n_muid=%" PRIu32,
561 		    st->n_uid, st->n_gid, st->n_muid);
562 	}
563 }
564 
565 static void
566 l9p_describe_statfs(const char *str, struct l9p_statfs *st, struct sbuf *sb)
567 {
568 
569 	assert(st != NULL);
570 
571 	sbuf_printf(sb, "%stype=0x%04lx bsize=%lu blocks=%" PRIu64
572 	    " bfree=%" PRIu64 " bavail=%" PRIu64 " files=%" PRIu64
573 	    " ffree=%" PRIu64 " fsid=0x%" PRIx64 " namelen=%" PRIu32 ">",
574 	    str, (u_long)st->type, (u_long)st->bsize, st->blocks,
575 	    st->bfree, st->bavail, st->files,
576 	    st->ffree, st->fsid, st->namelen);
577 }
578 
579 /*
580  * Decode a <seconds,nsec> timestamp.
581  *
582  * Perhaps should use asctime_r.  For now, raw values.
583  */
584 static void
585 l9p_describe_time(struct sbuf *sb, const char *s, uint64_t sec, uint64_t nsec)
586 {
587 
588 	sbuf_cat(sb, s);
589 	if (nsec > 999999999)
590 		sbuf_printf(sb, "%" PRIu64 ".<invalid nsec %" PRIu64 ">)",
591 		    sec, nsec);
592 	else
593 		sbuf_printf(sb, "%" PRIu64 ".%09" PRIu64, sec, nsec);
594 }
595 
596 /*
597  * Decode readdir data (.L format, variable length names).
598  */
599 static void
600 l9p_describe_readdir(struct sbuf *sb, struct l9p_f_io *io)
601 {
602 	uint32_t count;
603 #ifdef notyet
604 	int i;
605 	struct l9p_message msg;
606 	struct l9p_dirent de;
607 #endif
608 
609 	if ((count = io->count) == 0) {
610 		sbuf_printf(sb, " EOF (count=0)");
611 		return;
612 	}
613 
614 	/*
615 	 * Can't do this yet because we do not have the original
616 	 * req.
617 	 */
618 #ifdef notyet
619 	sbuf_printf(sb, " count=%" PRIu32 " [", count);
620 
621 	l9p_init_msg(&msg, req, L9P_UNPACK);
622 	for (i = 0; msg.lm_size < count; i++) {
623 		if (l9p_pudirent(&msg, &de) < 0) {
624 			sbuf_printf(sb, " bad count");
625 			break;
626 		}
627 
628 		sbuf_printf(sb, i ? ", " : " ");
629 		l9p_describe_qid(" qid=", &de.qid, sb);
630 		sbuf_printf(sb, " offset=%" PRIu64 " type=%d",
631 		    de.offset, de.type);
632 		l9p_describe_name(" name=", de.name);
633 		free(de.name);
634 	}
635 	sbuf_printf(sb, "]=%d dir entries", i);
636 #else /* notyet */
637 	sbuf_printf(sb, " count=%" PRIu32, count);
638 #endif
639 }
640 
641 /*
642  * Decode Tgetattr request_mask field.
643  */
644 static void
645 l9p_describe_getattr_mask(uint64_t request_mask, struct sbuf *sb)
646 {
647 	static const struct descbits bits[] = {
648 		/*
649 		 * Note: ALL and BASIC must occur first and second.
650 		 * This is a little dirty: it depends on the way the
651 		 * describe_bits code clears the values.  If we
652 		 * match ALL, we clear all those bits and do not
653 		 * match BASIC; if we match BASIC, we clear all
654 		 * those bits and do not match individual bits.  Thus
655 		 * if we have BASIC but not all the additional bits,
656 		 * we'll see, e.g., [BASIC,BTIME,GEN]; if we have
657 		 * all the additional bits too, we'll see [ALL].
658 		 *
659 		 * Since <undec> is true below, we'll also spot any
660 		 * bits added to the protocol since we made this table.
661 		 */
662 		{ L9PL_GETATTR_ALL,	L9PL_GETATTR_ALL,	"ALL" },
663 		{ L9PL_GETATTR_BASIC,	L9PL_GETATTR_BASIC,	"BASIC" },
664 
665 		/* individual bits in BASIC */
666 		{ L9PL_GETATTR_MODE,	L9PL_GETATTR_MODE,	"MODE" },
667 		{ L9PL_GETATTR_NLINK,	L9PL_GETATTR_NLINK,	"NLINK" },
668 		{ L9PL_GETATTR_UID,	L9PL_GETATTR_UID,	"UID" },
669 		{ L9PL_GETATTR_GID,	L9PL_GETATTR_GID,	"GID" },
670 		{ L9PL_GETATTR_RDEV,	L9PL_GETATTR_RDEV,	"RDEV" },
671 		{ L9PL_GETATTR_ATIME,	L9PL_GETATTR_ATIME,	"ATIME" },
672 		{ L9PL_GETATTR_MTIME,	L9PL_GETATTR_MTIME,	"MTIME" },
673 		{ L9PL_GETATTR_CTIME,	L9PL_GETATTR_CTIME,	"CTIME" },
674 		{ L9PL_GETATTR_INO,	L9PL_GETATTR_INO,	"INO" },
675 		{ L9PL_GETATTR_SIZE,	L9PL_GETATTR_SIZE,	"SIZE" },
676 		{ L9PL_GETATTR_BLOCKS,	L9PL_GETATTR_BLOCKS,	"BLOCKS" },
677 
678 		/* additional bits in ALL */
679 		{ L9PL_GETATTR_BTIME,	L9PL_GETATTR_BTIME,	"BTIME" },
680 		{ L9PL_GETATTR_GEN,	L9PL_GETATTR_GEN,	"GEN" },
681 		{ L9PL_GETATTR_DATA_VERSION, L9PL_GETATTR_DATA_VERSION,
682 							"DATA_VERSION" },
683 		{ 0, 0, NULL }
684 	};
685 
686 	(void) l9p_describe_bits(" request_mask=", request_mask, "[]", bits,
687 	    sb);
688 }
689 
690 /*
691  * Decode Tunlinkat flags.
692  */
693 static void
694 l9p_describe_unlinkat_flags(const char *str, uint32_t flags, struct sbuf *sb)
695 {
696 	static const struct descbits bits[] = {
697 		{ L9PL_AT_REMOVEDIR, L9PL_AT_REMOVEDIR, "AT_REMOVEDIR" },
698 		{ 0, 0, NULL }
699 	};
700 
701 	(void) l9p_describe_bits(str, flags, "[]", bits, sb);
702 }
703 
704 static const char *
705 lookup_linux_errno(uint32_t linux_errno)
706 {
707 	static char unknown[50];
708 
709 	/*
710 	 * Error numbers in the "base" range (1..ERANGE) are common
711 	 * across BSD, MacOS, Linux, and Plan 9.
712 	 *
713 	 * Error numbers outside that range require translation.
714 	 */
715 	const char *const table[] = {
716 #define X0(name) [name] = name ## _STR
717 #define	X(name) [name] = name ## _STR
718 		X(LINUX_EAGAIN),
719 		X(LINUX_EDEADLK),
720 		X(LINUX_ENAMETOOLONG),
721 		X(LINUX_ENOLCK),
722 		X(LINUX_ENOSYS),
723 		X(LINUX_ENOTEMPTY),
724 		X(LINUX_ELOOP),
725 		X(LINUX_ENOMSG),
726 		X(LINUX_EIDRM),
727 		X(LINUX_ECHRNG),
728 		X(LINUX_EL2NSYNC),
729 		X(LINUX_EL3HLT),
730 		X(LINUX_EL3RST),
731 		X(LINUX_ELNRNG),
732 		X(LINUX_EUNATCH),
733 		X(LINUX_ENOCSI),
734 		X(LINUX_EL2HLT),
735 		X(LINUX_EBADE),
736 		X(LINUX_EBADR),
737 		X(LINUX_EXFULL),
738 		X(LINUX_ENOANO),
739 		X(LINUX_EBADRQC),
740 		X(LINUX_EBADSLT),
741 		X(LINUX_EBFONT),
742 		X(LINUX_ENOSTR),
743 		X(LINUX_ENODATA),
744 		X(LINUX_ETIME),
745 		X(LINUX_ENOSR),
746 		X(LINUX_ENONET),
747 		X(LINUX_ENOPKG),
748 		X(LINUX_EREMOTE),
749 		X(LINUX_ENOLINK),
750 		X(LINUX_EADV),
751 		X(LINUX_ESRMNT),
752 		X(LINUX_ECOMM),
753 		X(LINUX_EPROTO),
754 		X(LINUX_EMULTIHOP),
755 		X(LINUX_EDOTDOT),
756 		X(LINUX_EBADMSG),
757 		X(LINUX_EOVERFLOW),
758 		X(LINUX_ENOTUNIQ),
759 		X(LINUX_EBADFD),
760 		X(LINUX_EREMCHG),
761 		X(LINUX_ELIBACC),
762 		X(LINUX_ELIBBAD),
763 		X(LINUX_ELIBSCN),
764 		X(LINUX_ELIBMAX),
765 		X(LINUX_ELIBEXEC),
766 		X(LINUX_EILSEQ),
767 		X(LINUX_ERESTART),
768 		X(LINUX_ESTRPIPE),
769 		X(LINUX_EUSERS),
770 		X(LINUX_ENOTSOCK),
771 		X(LINUX_EDESTADDRREQ),
772 		X(LINUX_EMSGSIZE),
773 		X(LINUX_EPROTOTYPE),
774 		X(LINUX_ENOPROTOOPT),
775 		X(LINUX_EPROTONOSUPPORT),
776 		X(LINUX_ESOCKTNOSUPPORT),
777 		X(LINUX_EOPNOTSUPP),
778 		X(LINUX_EPFNOSUPPORT),
779 		X(LINUX_EAFNOSUPPORT),
780 		X(LINUX_EADDRINUSE),
781 		X(LINUX_EADDRNOTAVAIL),
782 		X(LINUX_ENETDOWN),
783 		X(LINUX_ENETUNREACH),
784 		X(LINUX_ENETRESET),
785 		X(LINUX_ECONNABORTED),
786 		X(LINUX_ECONNRESET),
787 		X(LINUX_ENOBUFS),
788 		X(LINUX_EISCONN),
789 		X(LINUX_ENOTCONN),
790 		X(LINUX_ESHUTDOWN),
791 		X(LINUX_ETOOMANYREFS),
792 		X(LINUX_ETIMEDOUT),
793 		X(LINUX_ECONNREFUSED),
794 		X(LINUX_EHOSTDOWN),
795 		X(LINUX_EHOSTUNREACH),
796 		X(LINUX_EALREADY),
797 		X(LINUX_EINPROGRESS),
798 		X(LINUX_ESTALE),
799 		X(LINUX_EUCLEAN),
800 		X(LINUX_ENOTNAM),
801 		X(LINUX_ENAVAIL),
802 		X(LINUX_EISNAM),
803 		X(LINUX_EREMOTEIO),
804 		X(LINUX_EDQUOT),
805 		X(LINUX_ENOMEDIUM),
806 		X(LINUX_EMEDIUMTYPE),
807 		X(LINUX_ECANCELED),
808 		X(LINUX_ENOKEY),
809 		X(LINUX_EKEYEXPIRED),
810 		X(LINUX_EKEYREVOKED),
811 		X(LINUX_EKEYREJECTED),
812 		X(LINUX_EOWNERDEAD),
813 		X(LINUX_ENOTRECOVERABLE),
814 		X(LINUX_ERFKILL),
815 		X(LINUX_EHWPOISON),
816 #undef X0
817 #undef X
818 	};
819 	if ((size_t)linux_errno < N(table) && table[linux_errno] != NULL)
820 		return (table[linux_errno]);
821 	if (linux_errno <= ERANGE)
822 		return (strerror((int)linux_errno));
823 	(void) snprintf(unknown, sizeof(unknown),
824 	    "Unknown error %d", linux_errno);
825 	return (unknown);
826 }
827 
828 void
829 l9p_describe_fcall(union l9p_fcall *fcall, enum l9p_version version,
830     struct sbuf *sb)
831 {
832 	uint64_t mask;
833 	uint8_t type;
834 	int i;
835 
836 	assert(fcall != NULL);
837 	assert(sb != NULL);
838 	assert(version <= L9P_2000L && version >= L9P_INVALID_VERSION);
839 
840 	type = fcall->hdr.type;
841 
842 	if (type < L9P__FIRST || type >= L9P__LAST_PLUS_1 ||
843 	    ftype_names[type - L9P__FIRST] == NULL) {
844 		const char *rr;
845 
846 		/*
847 		 * Can't say for sure that this distinction --
848 		 * an even number is a request, an odd one is
849 		 * a response -- will be maintained forever,
850 		 * but it's good enough for now.
851 		 */
852 		rr = (type & 1) != 0 ? "response" : "request";
853 		sbuf_printf(sb, "<unknown %s %d> tag=%d", rr, type,
854 		    fcall->hdr.tag);
855 	} else {
856 		sbuf_printf(sb, "%s tag=%d", ftype_names[type - L9P__FIRST],
857 		    fcall->hdr.tag);
858 	}
859 
860 	switch (type) {
861 	case L9P_TVERSION:
862 	case L9P_RVERSION:
863 		sbuf_printf(sb, " version=\"%s\" msize=%d", fcall->version.version,
864 		    fcall->version.msize);
865 		return;
866 
867 	case L9P_TAUTH:
868 		l9p_describe_fid(" afid=", fcall->hdr.fid, sb);
869 		sbuf_printf(sb, " uname=\"%s\" aname=\"%s\"",
870 		    fcall->tauth.uname, fcall->tauth.aname);
871 		return;
872 
873 	case L9P_TATTACH:
874 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
875 		l9p_describe_fid(" afid=", fcall->tattach.afid, sb);
876 		sbuf_printf(sb, " uname=\"%s\" aname=\"%s\"",
877 		    fcall->tattach.uname, fcall->tattach.aname);
878 		if (version >= L9P_2000U)
879 			sbuf_printf(sb, " n_uname=%d", fcall->tattach.n_uname);
880 		return;
881 
882 	case L9P_RATTACH:
883 		l9p_describe_qid(" ", &fcall->rattach.qid, sb);
884 		return;
885 
886 	case L9P_RERROR:
887 		sbuf_printf(sb, " ename=\"%s\" errnum=%d", fcall->error.ename,
888 		    fcall->error.errnum);
889 		return;
890 
891 	case L9P_RLERROR:
892 		sbuf_printf(sb, " errnum=%d (%s)", fcall->error.errnum,
893 		    lookup_linux_errno(fcall->error.errnum));
894 		return;
895 
896 	case L9P_TFLUSH:
897 		sbuf_printf(sb, " oldtag=%d", fcall->tflush.oldtag);
898 		return;
899 
900 	case L9P_RFLUSH:
901 		return;
902 
903 	case L9P_TWALK:
904 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
905 		l9p_describe_fid(" newfid=", fcall->twalk.newfid, sb);
906 		if (fcall->twalk.nwname) {
907 			sbuf_cat(sb, " wname=\"");
908 			for (i = 0; i < fcall->twalk.nwname; i++)
909 				sbuf_printf(sb, "%s%s", i == 0 ? "" : "/",
910 				    fcall->twalk.wname[i]);
911 			sbuf_cat(sb, "\"");
912 		}
913 		return;
914 
915 	case L9P_RWALK:
916 		sbuf_printf(sb, " wqid=[");
917 		for (i = 0; i < fcall->rwalk.nwqid; i++)
918 			l9p_describe_qid(i == 0 ? "" : ",",
919 			    &fcall->rwalk.wqid[i], sb);
920 		sbuf_cat(sb, "]");
921 		return;
922 
923 	case L9P_TOPEN:
924 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
925 		l9p_describe_mode(" mode=", fcall->tcreate.mode, sb);
926 		return;
927 
928 	case L9P_ROPEN:
929 		l9p_describe_qid(" qid=", &fcall->ropen.qid, sb);
930 		sbuf_printf(sb, " iounit=%d", fcall->ropen.iounit);
931 		return;
932 
933 	case L9P_TCREATE:
934 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
935 		l9p_describe_name(" name=", fcall->tcreate.name, sb);
936 		l9p_describe_ext_perm(" perm=", fcall->tcreate.perm, sb);
937 		l9p_describe_mode(" mode=", fcall->tcreate.mode, sb);
938 		if (version >= L9P_2000U && fcall->tcreate.extension != NULL)
939 			l9p_describe_name(" extension=",
940 			    fcall->tcreate.extension, sb);
941 		return;
942 
943 	case L9P_RCREATE:
944 		l9p_describe_qid(" qid=", &fcall->rcreate.qid, sb);
945 		sbuf_printf(sb, " iounit=%d", fcall->rcreate.iounit);
946 		return;
947 
948 	case L9P_TREAD:
949 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
950 		sbuf_printf(sb, " offset=%" PRIu64 " count=%" PRIu32,
951 		    fcall->io.offset, fcall->io.count);
952 		return;
953 
954 	case L9P_RREAD:
955 	case L9P_RWRITE:
956 		sbuf_printf(sb, " count=%" PRIu32, fcall->io.count);
957 		return;
958 
959 	case L9P_TWRITE:
960 	case L9P_TREADDIR:
961 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
962 		sbuf_printf(sb, " offset=%" PRIu64 " count=%" PRIu32,
963 		    fcall->io.offset, fcall->io.count);
964 		return;
965 
966 	case L9P_TCLUNK:
967 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
968 		return;
969 
970 	case L9P_RCLUNK:
971 		return;
972 
973 	case L9P_TREMOVE:
974 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
975 		return;
976 
977 	case L9P_RREMOVE:
978 		return;
979 
980 	case L9P_TSTAT:
981 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
982 		return;
983 
984 	case L9P_RSTAT:
985 		l9p_describe_l9stat(" ", &fcall->rstat.stat, version, sb);
986 		return;
987 
988 	case L9P_TWSTAT:
989 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
990 		l9p_describe_l9stat(" ", &fcall->twstat.stat, version, sb);
991 		return;
992 
993 	case L9P_RWSTAT:
994 		return;
995 
996 	case L9P_TSTATFS:
997 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
998 		return;
999 
1000 	case L9P_RSTATFS:
1001 		l9p_describe_statfs(" ", &fcall->rstatfs.statfs, sb);
1002 		return;
1003 
1004 	case L9P_TLOPEN:
1005 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1006 		l9p_describe_lflags(" flags=", fcall->tlcreate.flags, sb);
1007 		return;
1008 
1009 	case L9P_RLOPEN:
1010 		l9p_describe_qid(" qid=", &fcall->rlopen.qid, sb);
1011 		sbuf_printf(sb, " iounit=%d", fcall->rlopen.iounit);
1012 		return;
1013 
1014 	case L9P_TLCREATE:
1015 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1016 		l9p_describe_name(" name=", fcall->tlcreate.name, sb);
1017 		/* confusing: "flags" is open-mode, "mode" is permissions */
1018 		l9p_describe_lflags(" flags=", fcall->tlcreate.flags, sb);
1019 		/* TLCREATE mode/permissions have S_IFREG (0x8000) set */
1020 		l9p_describe_lperm(" mode=", fcall->tlcreate.mode, sb);
1021 		l9p_describe_ugid(" gid=", fcall->tlcreate.gid, sb);
1022 		return;
1023 
1024 	case L9P_RLCREATE:
1025 		l9p_describe_qid(" qid=", &fcall->rlcreate.qid, sb);
1026 		sbuf_printf(sb, " iounit=%d", fcall->rlcreate.iounit);
1027 		return;
1028 
1029 	case L9P_TSYMLINK:
1030 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1031 		l9p_describe_name(" name=", fcall->tsymlink.name, sb);
1032 		l9p_describe_name(" symtgt=", fcall->tsymlink.symtgt, sb);
1033 		l9p_describe_ugid(" gid=", fcall->tsymlink.gid, sb);
1034 		return;
1035 
1036 	case L9P_RSYMLINK:
1037 		l9p_describe_qid(" qid=", &fcall->ropen.qid, sb);
1038 		return;
1039 
1040 	case L9P_TMKNOD:
1041 		l9p_describe_fid(" dfid=", fcall->hdr.fid, sb);
1042 		l9p_describe_name(" name=", fcall->tmknod.name, sb);
1043 		/*
1044 		 * TMKNOD mode/permissions have S_IFBLK/S_IFCHR/S_IFIFO
1045 		 * bits.  The major and minor values are only meaningful
1046 		 * for S_IFBLK and S_IFCHR, but just decode always here.
1047 		 */
1048 		l9p_describe_lperm(" mode=", fcall->tmknod.mode, sb);
1049 		sbuf_printf(sb, " major=%u minor=%u",
1050 		    fcall->tmknod.major, fcall->tmknod.minor);
1051 		l9p_describe_ugid(" gid=", fcall->tmknod.gid, sb);
1052 		return;
1053 
1054 	case L9P_RMKNOD:
1055 		l9p_describe_qid(" qid=", &fcall->rmknod.qid, sb);
1056 		return;
1057 
1058 	case L9P_TRENAME:
1059 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1060 		l9p_describe_fid(" dfid=", fcall->trename.dfid, sb);
1061 		l9p_describe_name(" name=", fcall->trename.name, sb);
1062 		return;
1063 
1064 	case L9P_RRENAME:
1065 		return;
1066 
1067 	case L9P_TREADLINK:
1068 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1069 		return;
1070 
1071 	case L9P_RREADLINK:
1072 		l9p_describe_name(" target=", fcall->rreadlink.target, sb);
1073 		return;
1074 
1075 	case L9P_TGETATTR:
1076 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1077 		l9p_describe_getattr_mask(fcall->tgetattr.request_mask, sb);
1078 		return;
1079 
1080 	case L9P_RGETATTR:
1081 		/* Don't need to decode bits: they're implied by the output */
1082 		mask = fcall->rgetattr.valid;
1083 		sbuf_printf(sb, " valid=0x%016" PRIx64, mask);
1084 		l9p_describe_qid(" qid=", &fcall->rgetattr.qid, sb);
1085 		if (mask & L9PL_GETATTR_MODE)
1086 			l9p_describe_lperm(" mode=", fcall->rgetattr.mode, sb);
1087 		if (mask & L9PL_GETATTR_UID)
1088 			l9p_describe_ugid(" uid=", fcall->rgetattr.uid, sb);
1089 		if (mask & L9PL_GETATTR_GID)
1090 			l9p_describe_ugid(" gid=", fcall->rgetattr.gid, sb);
1091 		if (mask & L9PL_GETATTR_NLINK)
1092 			sbuf_printf(sb, " nlink=%" PRIu64,
1093 			    fcall->rgetattr.nlink);
1094 		if (mask & L9PL_GETATTR_RDEV)
1095 			sbuf_printf(sb, " rdev=0x%" PRIx64,
1096 			    fcall->rgetattr.rdev);
1097 		if (mask & L9PL_GETATTR_SIZE)
1098 			l9p_describe_size(" size=", fcall->rgetattr.size, sb);
1099 		if (mask & L9PL_GETATTR_BLOCKS)
1100 			sbuf_printf(sb, " blksize=%" PRIu64 " blocks=%" PRIu64,
1101 			    fcall->rgetattr.blksize, fcall->rgetattr.blocks);
1102 		if (mask & L9PL_GETATTR_ATIME)
1103 			l9p_describe_time(sb, " atime=",
1104 			    fcall->rgetattr.atime_sec,
1105 			    fcall->rgetattr.atime_nsec);
1106 		if (mask & L9PL_GETATTR_MTIME)
1107 			l9p_describe_time(sb, " mtime=",
1108 			    fcall->rgetattr.mtime_sec,
1109 			    fcall->rgetattr.mtime_nsec);
1110 		if (mask & L9PL_GETATTR_CTIME)
1111 			l9p_describe_time(sb, " ctime=",
1112 			    fcall->rgetattr.ctime_sec,
1113 			    fcall->rgetattr.ctime_nsec);
1114 		if (mask & L9PL_GETATTR_BTIME)
1115 			l9p_describe_time(sb, " btime=",
1116 			    fcall->rgetattr.btime_sec,
1117 			    fcall->rgetattr.btime_nsec);
1118 		if (mask & L9PL_GETATTR_GEN)
1119 			sbuf_printf(sb, " gen=0x%" PRIx64, fcall->rgetattr.gen);
1120 		if (mask & L9PL_GETATTR_DATA_VERSION)
1121 			sbuf_printf(sb, " data_version=0x%" PRIx64,
1122 			    fcall->rgetattr.data_version);
1123 		return;
1124 
1125 	case L9P_TSETATTR:
1126 		/* As with RGETATTR, we'll imply decode via output. */
1127 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1128 		mask = fcall->tsetattr.valid;
1129 		/* NB: tsetattr valid mask is only 32 bits, hence %08x */
1130 		sbuf_printf(sb, " valid=0x%08" PRIx64, mask);
1131 		if (mask & L9PL_SETATTR_MODE)
1132 			l9p_describe_lperm(" mode=", fcall->tsetattr.mode, sb);
1133 		if (mask & L9PL_SETATTR_UID)
1134 			l9p_describe_ugid(" uid=", fcall->tsetattr.uid, sb);
1135 		if (mask & L9PL_SETATTR_GID)
1136 			l9p_describe_ugid(" uid=", fcall->tsetattr.gid, sb);
1137 		if (mask & L9PL_SETATTR_SIZE)
1138 			l9p_describe_size(" size=", fcall->tsetattr.size, sb);
1139 		if (mask & L9PL_SETATTR_ATIME) {
1140 			if (mask & L9PL_SETATTR_ATIME_SET)
1141 				l9p_describe_time(sb, " atime=",
1142 				    fcall->tsetattr.atime_sec,
1143 				    fcall->tsetattr.atime_nsec);
1144 			else
1145 				sbuf_cat(sb, " atime=now");
1146 		}
1147 		if (mask & L9PL_SETATTR_MTIME) {
1148 			if (mask & L9PL_SETATTR_MTIME_SET)
1149 				l9p_describe_time(sb, " mtime=",
1150 				    fcall->tsetattr.mtime_sec,
1151 				    fcall->tsetattr.mtime_nsec);
1152 			else
1153 				sbuf_cat(sb, " mtime=now");
1154 		}
1155 		if (mask & L9PL_SETATTR_CTIME)
1156 			sbuf_cat(sb, " ctime=now");
1157 		return;
1158 
1159 	case L9P_RSETATTR:
1160 		return;
1161 
1162 	case L9P_TXATTRWALK:
1163 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1164 		l9p_describe_fid(" newfid=", fcall->txattrwalk.newfid, sb);
1165 		l9p_describe_name(" name=", fcall->txattrwalk.name, sb);
1166 		return;
1167 
1168 	case L9P_RXATTRWALK:
1169 		l9p_describe_size(" size=", fcall->rxattrwalk.size, sb);
1170 		return;
1171 
1172 	case L9P_TXATTRCREATE:
1173 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1174 		l9p_describe_name(" name=", fcall->txattrcreate.name, sb);
1175 		l9p_describe_size(" size=", fcall->txattrcreate.attr_size, sb);
1176 		sbuf_printf(sb, " flags=%" PRIu32, fcall->txattrcreate.flags);
1177 		return;
1178 
1179 	case L9P_RXATTRCREATE:
1180 		return;
1181 
1182 	case L9P_RREADDIR:
1183 		l9p_describe_readdir(sb, &fcall->io);
1184 		return;
1185 
1186 	case L9P_TFSYNC:
1187 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1188 		return;
1189 
1190 	case L9P_RFSYNC:
1191 		return;
1192 
1193 	case L9P_TLOCK:
1194 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1195 		/* decode better later */
1196 		sbuf_printf(sb, " type=%d flags=0x%" PRIx32
1197 		    " start=%" PRIu64 " length=%" PRIu64
1198 		    " proc_id=0x%" PRIx32 " client_id=\"%s\"",
1199 		    fcall->tlock.type, fcall->tlock.flags,
1200 		    fcall->tlock.start, fcall->tlock.length,
1201 		    fcall->tlock.proc_id, fcall->tlock.client_id);
1202 		return;
1203 
1204 	case L9P_RLOCK:
1205 		sbuf_printf(sb, " status=%d", fcall->rlock.status);
1206 		return;
1207 
1208 	case L9P_TGETLOCK:
1209 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1210 		/* FALLTHROUGH */
1211 
1212 	case L9P_RGETLOCK:
1213 		/* decode better later */
1214 		sbuf_printf(sb, " type=%d "
1215 		    " start=%" PRIu64 " length=%" PRIu64
1216 		    " proc_id=0x%" PRIx32 " client_id=\"%s\"",
1217 		    fcall->getlock.type,
1218 		    fcall->getlock.start, fcall->getlock.length,
1219 		    fcall->getlock.proc_id, fcall->getlock.client_id);
1220 		return;
1221 
1222 	case L9P_TLINK:
1223 		l9p_describe_fid(" dfid=", fcall->tlink.dfid, sb);
1224 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1225 		l9p_describe_name(" name=", fcall->tlink.name, sb);
1226 		return;
1227 
1228 	case L9P_RLINK:
1229 		return;
1230 
1231 	case L9P_TMKDIR:
1232 		l9p_describe_fid(" fid=", fcall->hdr.fid, sb);
1233 		l9p_describe_name(" name=", fcall->tmkdir.name, sb);
1234 		/* TMKDIR mode/permissions have S_IFDIR set */
1235 		l9p_describe_lperm(" mode=", fcall->tmkdir.mode, sb);
1236 		l9p_describe_ugid(" gid=", fcall->tmkdir.gid, sb);
1237 		return;
1238 
1239 	case L9P_RMKDIR:
1240 		l9p_describe_qid(" qid=", &fcall->rmkdir.qid, sb);
1241 		return;
1242 
1243 	case L9P_TRENAMEAT:
1244 		l9p_describe_fid(" olddirfid=", fcall->hdr.fid, sb);
1245 		l9p_describe_name(" oldname=", fcall->trenameat.oldname,
1246 		    sb);
1247 		l9p_describe_fid(" newdirfid=", fcall->trenameat.newdirfid, sb);
1248 		l9p_describe_name(" newname=", fcall->trenameat.newname,
1249 		    sb);
1250 		return;
1251 
1252 	case L9P_RRENAMEAT:
1253 		return;
1254 
1255 	case L9P_TUNLINKAT:
1256 		l9p_describe_fid(" dirfd=", fcall->hdr.fid, sb);
1257 		l9p_describe_name(" name=", fcall->tunlinkat.name, sb);
1258 		l9p_describe_unlinkat_flags(" flags=",
1259 		    fcall->tunlinkat.flags, sb);
1260 		return;
1261 
1262 	case L9P_RUNLINKAT:
1263 		return;
1264 
1265 	default:
1266 		sbuf_printf(sb, " <missing case in %s()>", __func__);
1267 	}
1268 }
1269