xref: /illumos-gate/usr/src/lib/lib9p/common/pack.c (revision aa693e99)
1*aa693e99SJason King /*
2*aa693e99SJason King  * Copyright 2016 Jakub Klama <jceel@FreeBSD.org>
3*aa693e99SJason King  * All rights reserved
4*aa693e99SJason King  *
5*aa693e99SJason King  * Redistribution and use in source and binary forms, with or without
6*aa693e99SJason King  * modification, are permitted providing that the following conditions
7*aa693e99SJason King  * are met:
8*aa693e99SJason King  * 1. Redistributions of source code must retain the above copyright
9*aa693e99SJason King  *    notice, this list of conditions and the following disclaimer.
10*aa693e99SJason King  * 2. Redistributions in binary form must reproduce the above copyright
11*aa693e99SJason King  *    notice, this list of conditions and the following disclaimer in the
12*aa693e99SJason King  *    documentation and/or other materials provided with the distribution.
13*aa693e99SJason King  *
24*aa693e99SJason King  * POSSIBILITY OF SUCH DAMAGE.
25*aa693e99SJason King  *
26*aa693e99SJason King  */
27*aa693e99SJason King 
28*aa693e99SJason King /*
29*aa693e99SJason King  * Based on libixp code: ©2007-2010 Kris Maglione <maglione.k at Gmail>
30*aa693e99SJason King  */
31*aa693e99SJason King 
32*aa693e99SJason King #include <stdlib.h>
33*aa693e99SJason King #include <string.h>
34*aa693e99SJason King #include <assert.h>
35*aa693e99SJason King #include <sys/types.h>
36*aa693e99SJason King #include <sys/param.h>
37*aa693e99SJason King #ifdef __APPLE__
38*aa693e99SJason King # include "apple_endian.h"
39*aa693e99SJason King #elif __illumos__
40*aa693e99SJason King # include "illumos_endian.h"
41*aa693e99SJason King # include <sys/sysmacros.h>
42*aa693e99SJason King #else
43*aa693e99SJason King # include <sys/endian.h>
44*aa693e99SJason King #endif
45*aa693e99SJason King #include <sys/uio.h>
46*aa693e99SJason King #include "lib9p.h"
47*aa693e99SJason King #include "lib9p_impl.h"
48*aa693e99SJason King #include "log.h"
49*aa693e99SJason King 
50*aa693e99SJason King #define N(ary)          (sizeof(ary) / sizeof(*ary))
51*aa693e99SJason King #define STRING_SIZE(s)  (L9P_WORD + (s != NULL ? (uint16_t)strlen(s) : 0))
52*aa693e99SJason King #define QID_SIZE        (L9P_BYTE + L9P_DWORD + L9P_QWORD)
53*aa693e99SJason King 
54*aa693e99SJason King static ssize_t l9p_iov_io(struct l9p_message *, void *, size_t);
55*aa693e99SJason King static inline ssize_t l9p_pu8(struct l9p_message *, uint8_t *);
56*aa693e99SJason King static inline ssize_t l9p_pu16(struct l9p_message *, uint16_t *);
57*aa693e99SJason King static inline ssize_t l9p_pu32(struct l9p_message *, uint32_t *);
58*aa693e99SJason King static inline ssize_t l9p_pu64(struct l9p_message *, uint64_t *);
59*aa693e99SJason King static ssize_t l9p_pustring(struct l9p_message *, char **s);
60*aa693e99SJason King static ssize_t l9p_pustrings(struct l9p_message *, uint16_t *, char **, size_t);
61*aa693e99SJason King static ssize_t l9p_puqid(struct l9p_message *, struct l9p_qid *);
62*aa693e99SJason King static ssize_t l9p_puqids(struct l9p_message *, uint16_t *, struct l9p_qid *q);
63*aa693e99SJason King 
64*aa693e99SJason King /*
65*aa693e99SJason King  * Transfer data from incoming request, or to outgoing response,
66*aa693e99SJason King  * using msg to track position and direction within request/response.
67*aa693e99SJason King  *
68*aa693e99SJason King  * Returns the number of bytes actually transferred (which is always
69*aa693e99SJason King  * just len itself, converted to signed), or -1 if we ran out of space.
70*aa693e99SJason King  *
71*aa693e99SJason King  * Note that if we return -1, subsequent l9p_iov_io() calls with
72*aa693e99SJason King  * the same (and not-reset) msg and len > 0 will also return -1.
73*aa693e99SJason King  * This means most users can just check the *last* call for failure.
74*aa693e99SJason King  */
75*aa693e99SJason King static ssize_t
l9p_iov_io(struct l9p_message * msg,void * buffer,size_t len)76*aa693e99SJason King l9p_iov_io(struct l9p_message *msg, void *buffer, size_t len)
77*aa693e99SJason King {
78*aa693e99SJason King 	size_t done = 0;
79*aa693e99SJason King 	size_t left = len;
80*aa693e99SJason King 
81*aa693e99SJason King 	assert(msg != NULL);
82*aa693e99SJason King 
83*aa693e99SJason King 	if (len == 0)
84*aa693e99SJason King 		return (0);
85*aa693e99SJason King 
86*aa693e99SJason King 	if (msg->lm_cursor_iov >= msg->lm_niov)
87*aa693e99SJason King 		return (-1);
88*aa693e99SJason King 
89*aa693e99SJason King 	assert(buffer != NULL);
90*aa693e99SJason King 
91*aa693e99SJason King 	while (left > 0) {
92*aa693e99SJason King 		size_t idx = msg->lm_cursor_iov;
93*aa693e99SJason King 		size_t space = msg->lm_iov[idx].iov_len - msg->lm_cursor_offset;
94*aa693e99SJason King 		size_t towrite = MIN(space, left);
95*aa693e99SJason King 
96*aa693e99SJason King 		if (msg->lm_mode == L9P_PACK) {
97*aa693e99SJason King 			memcpy((char *)msg->lm_iov[idx].iov_base +
98*aa693e99SJason King 			    msg->lm_cursor_offset, (char *)buffer + done,
99*aa693e99SJason King 			    towrite);
100*aa693e99SJason King 		}
101*aa693e99SJason King 
102*aa693e99SJason King 		if (msg->lm_mode == L9P_UNPACK) {
103*aa693e99SJason King 			memcpy((char *)buffer + done,
104*aa693e99SJason King 			    (char *)msg->lm_iov[idx].iov_base +
105*aa693e99SJason King 			    msg->lm_cursor_offset, towrite);
106*aa693e99SJason King 		}
107*aa693e99SJason King 
108*aa693e99SJason King 		msg->lm_cursor_offset += towrite;
109*aa693e99SJason King 
110*aa693e99SJason King 		done += towrite;
111*aa693e99SJason King 		left -= towrite;
112*aa693e99SJason King 
113*aa693e99SJason King 		if (space - towrite == 0) {
114*aa693e99SJason King 			/* Advance to next iov */
115*aa693e99SJason King 			msg->lm_cursor_iov++;
116*aa693e99SJason King 			msg->lm_cursor_offset = 0;
117*aa693e99SJason King 
118*aa693e99SJason King 			if (msg->lm_cursor_iov >= msg->lm_niov && left > 0)
119*aa693e99SJason King 				return (-1);
120*aa693e99SJason King 		}
121*aa693e99SJason King 	}
122*aa693e99SJason King 
123*aa693e99SJason King 	msg->lm_size += done;
124*aa693e99SJason King 	return ((ssize_t)done);
125*aa693e99SJason King }
126*aa693e99SJason King 
127*aa693e99SJason King /*
128*aa693e99SJason King  * Pack or unpack a byte (8 bits).
129*aa693e99SJason King  *
130*aa693e99SJason King  * Returns 1 (success, 1 byte) or -1 (error).
131*aa693e99SJason King  */
132*aa693e99SJason King static inline ssize_t
l9p_pu8(struct l9p_message * msg,uint8_t * val)133*aa693e99SJason King l9p_pu8(struct l9p_message *msg, uint8_t *val)
134*aa693e99SJason King {
135*aa693e99SJason King 
136*aa693e99SJason King 	return (l9p_iov_io(msg, val, sizeof (uint8_t)));
137*aa693e99SJason King }
138*aa693e99SJason King 
139*aa693e99SJason King /*
140*aa693e99SJason King  * Pack or unpack 16-bit value.
141*aa693e99SJason King  *
142*aa693e99SJason King  * Returns 2 or -1.
143*aa693e99SJason King  */
144*aa693e99SJason King static inline ssize_t
l9p_pu16(struct l9p_message * msg,uint16_t * val)145*aa693e99SJason King l9p_pu16(struct l9p_message *msg, uint16_t *val)
146*aa693e99SJason King {
147*aa693e99SJason King #if _BYTE_ORDER != _LITTLE_ENDIAN
148*aa693e99SJason King 	/*
149*aa693e99SJason King 	 * The ifdefs are annoying, but there is no need
150*aa693e99SJason King 	 * for all of this foolery on little-endian hosts,
151*aa693e99SJason King 	 * and I don't expect the compiler to optimize it
152*aa693e99SJason King 	 * all away.
153*aa693e99SJason King 	 */
154*aa693e99SJason King 	uint16_t copy;
155*aa693e99SJason King 	ssize_t ret;
156*aa693e99SJason King 
157*aa693e99SJason King 	if (msg->lm_mode == L9P_PACK) {
158*aa693e99SJason King 		copy = htole16(*val);
159*aa693e99SJason King 		return (l9p_iov_io(msg, &copy, sizeof (uint16_t)));
160*aa693e99SJason King 	}
161*aa693e99SJason King 	ret = l9p_iov_io(msg, val, sizeof (uint16_t));
162*aa693e99SJason King 	*val = le16toh(*val);
163*aa693e99SJason King 	return (ret);
164*aa693e99SJason King #else
165*aa693e99SJason King 	return (l9p_iov_io(msg, val, sizeof (uint16_t)));
166*aa693e99SJason King #endif
167*aa693e99SJason King }
168*aa693e99SJason King 
169*aa693e99SJason King /*
170*aa693e99SJason King  * Pack or unpack 32-bit value.
171*aa693e99SJason King  *
172*aa693e99SJason King  * Returns 4 or -1.
173*aa693e99SJason King  */
174*aa693e99SJason King static inline ssize_t
l9p_pu32(struct l9p_message * msg,uint32_t * val)175*aa693e99SJason King l9p_pu32(struct l9p_message *msg, uint32_t *val)
176*aa693e99SJason King {
177*aa693e99SJason King #if _BYTE_ORDER != _LITTLE_ENDIAN
178*aa693e99SJason King 	uint32_t copy;
179*aa693e99SJason King 	ssize_t ret;
180*aa693e99SJason King 
181*aa693e99SJason King 	if (msg->lm_mode == L9P_PACK) {
182*aa693e99SJason King 		copy = htole32(*val);
183*aa693e99SJason King 		return (l9p_iov_io(msg, &copy, sizeof (uint32_t)));
184*aa693e99SJason King 	}
185*aa693e99SJason King 	ret = l9p_iov_io(msg, val, sizeof (uint32_t));
186*aa693e99SJason King 	*val = le32toh(*val);
187*aa693e99SJason King 	return (ret);
188*aa693e99SJason King #else
189*aa693e99SJason King 	return (l9p_iov_io(msg, val, sizeof (uint32_t)));
190*aa693e99SJason King #endif
191*aa693e99SJason King }
192*aa693e99SJason King 
193*aa693e99SJason King /*
194*aa693e99SJason King  * Pack or unpack 64-bit value.
195*aa693e99SJason King  *
196*aa693e99SJason King  * Returns 8 or -1.
197*aa693e99SJason King  */
198*aa693e99SJason King static inline ssize_t
l9p_pu64(struct l9p_message * msg,uint64_t * val)199*aa693e99SJason King l9p_pu64(struct l9p_message *msg, uint64_t *val)
200*aa693e99SJason King {
201*aa693e99SJason King #if _BYTE_ORDER != _LITTLE_ENDIAN
202*aa693e99SJason King 	uint64_t copy;
203*aa693e99SJason King 	ssize_t ret;
204*aa693e99SJason King 
205*aa693e99SJason King 	if (msg->lm_mode == L9P_PACK) {
206*aa693e99SJason King 		copy = htole64(*val);
207*aa693e99SJason King 		return (l9p_iov_io(msg, &copy, sizeof (uint64_t)));
208*aa693e99SJason King 	}
209*aa693e99SJason King 	ret = l9p_iov_io(msg, val, sizeof (uint32_t));
210*aa693e99SJason King 	*val = le64toh(*val);
211*aa693e99SJason King 	return (ret);
212*aa693e99SJason King #else
213*aa693e99SJason King 	return (l9p_iov_io(msg, val, sizeof (uint64_t)));
214*aa693e99SJason King #endif
215*aa693e99SJason King }
216*aa693e99SJason King 
217*aa693e99SJason King /*
218*aa693e99SJason King  * Pack or unpack a string, encoded as 2-byte length followed by
219*aa693e99SJason King  * string bytes.  The returned length is 2 greater than the
220*aa693e99SJason King  * length of the string itself.
221*aa693e99SJason King  *
222*aa693e99SJason King  * When unpacking, this allocates a new string (NUL-terminated).
223*aa693e99SJason King  *
224*aa693e99SJason King  * Return -1 on error (not space, or failed to allocate string,
225*aa693e99SJason King  * or illegal string).
226*aa693e99SJason King  *
227*aa693e99SJason King  * Note that pustring (and hence pustrings) can return an error
228*aa693e99SJason King  * even when l9p_iov_io succeeds.
229*aa693e99SJason King  */
230*aa693e99SJason King static ssize_t
l9p_pustring(struct l9p_message * msg,char ** s)231*aa693e99SJason King l9p_pustring(struct l9p_message *msg, char **s)
232*aa693e99SJason King {
233*aa693e99SJason King 	uint16_t len;
234*aa693e99SJason King 
235*aa693e99SJason King 	if (msg->lm_mode == L9P_PACK)
236*aa693e99SJason King 		len = *s != NULL ? (uint16_t)strlen(*s) : 0;
237*aa693e99SJason King 
238*aa693e99SJason King 	if (l9p_pu16(msg, &len) < 0)
239*aa693e99SJason King 		return (-1);
240*aa693e99SJason King 
241*aa693e99SJason King 	if (msg->lm_mode == L9P_UNPACK) {
242*aa693e99SJason King 		*s = l9p_calloc(1, len + 1);
243*aa693e99SJason King 		if (*s == NULL)
244*aa693e99SJason King 			return (-1);
245*aa693e99SJason King 	}
246*aa693e99SJason King 
247*aa693e99SJason King 	if (l9p_iov_io(msg, *s, len) < 0)
248*aa693e99SJason King 		return (-1);
249*aa693e99SJason King 
250*aa693e99SJason King 	if (msg->lm_mode == L9P_UNPACK) {
251*aa693e99SJason King 		/*
252*aa693e99SJason King 		 * An embedded NUL byte in a string is illegal.
253*aa693e99SJason King 		 * We don't necessarily have to check (we'll just
254*aa693e99SJason King 		 * treat it as a shorter string), but checking
255*aa693e99SJason King 		 * seems like a good idea.
256*aa693e99SJason King 		 */
257*aa693e99SJason King 		if (memchr(*s, '\0', len) != NULL)
258*aa693e99SJason King 			return (-1);
259*aa693e99SJason King 	}
260*aa693e99SJason King 
261*aa693e99SJason King 	return ((ssize_t)len + 2);
262*aa693e99SJason King }
263*aa693e99SJason King 
264*aa693e99SJason King /*
265*aa693e99SJason King  * Pack or unpack a number (*num) of strings (but at most max of
266*aa693e99SJason King  * them).
267*aa693e99SJason King  *
268*aa693e99SJason King  * Returns the number of bytes transferred, including the packed
269*aa693e99SJason King  * number of strings.  If packing and the packed number of strings
270*aa693e99SJason King  * was reduced, the original *num value is unchanged; only the
271*aa693e99SJason King  * wire-format number is reduced.  If unpacking and the input
272*aa693e99SJason King  * number of strings exceeds the max, the incoming *num is reduced
273*aa693e99SJason King  * to lim, if needed.  (NOTE ASYMMETRY HERE!)
274*aa693e99SJason King  *
275*aa693e99SJason King  * Returns -1 on error.
276*aa693e99SJason King  */
277*aa693e99SJason King static ssize_t
l9p_pustrings(struct l9p_message * msg,uint16_t * num,char ** strings,size_t max)278*aa693e99SJason King l9p_pustrings(struct l9p_message *msg, uint16_t *num, char **strings,
279*aa693e99SJason King     size_t max)
280*aa693e99SJason King {
281*aa693e99SJason King 	size_t i, lim;
282*aa693e99SJason King 	ssize_t r, ret;
283*aa693e99SJason King 	uint16_t adjusted;
284*aa693e99SJason King 
285*aa693e99SJason King 	if (msg->lm_mode == L9P_PACK) {
286*aa693e99SJason King 		lim = *num;
287*aa693e99SJason King 		if (lim > max)
288*aa693e99SJason King 			lim = max;
289*aa693e99SJason King 		adjusted = (uint16_t)lim;
290*aa693e99SJason King 		r = l9p_pu16(msg, &adjusted);
291*aa693e99SJason King 	} else {
292*aa693e99SJason King 		r = l9p_pu16(msg, num);
293*aa693e99SJason King 		lim = *num;
294*aa693e99SJason King 		if (lim > max)
295*aa693e99SJason King 			*num = (uint16_t)(lim = max);
296*aa693e99SJason King 	}
297*aa693e99SJason King 	if (r < 0)
298*aa693e99SJason King 		return (-1);
299*aa693e99SJason King 
300*aa693e99SJason King 	for (i = 0; i < lim; i++) {
301*aa693e99SJason King 		ret = l9p_pustring(msg, &strings[i]);
302*aa693e99SJason King 		if (ret < 1)
303*aa693e99SJason King 			return (-1);
304*aa693e99SJason King 
305*aa693e99SJason King 		r += ret;
306*aa693e99SJason King 	}
307*aa693e99SJason King 
308*aa693e99SJason King 	return (r);
309*aa693e99SJason King }
310*aa693e99SJason King 
311*aa693e99SJason King /*
312*aa693e99SJason King  * Pack or unpack a qid.
313*aa693e99SJason King  *
314*aa693e99SJason King  * Returns 13 (success) or -1 (error).
315*aa693e99SJason King  */
316*aa693e99SJason King static ssize_t
l9p_puqid(struct l9p_message * msg,struct l9p_qid * qid)317*aa693e99SJason King l9p_puqid(struct l9p_message *msg, struct l9p_qid *qid)
318*aa693e99SJason King {
319*aa693e99SJason King 	ssize_t r;
320*aa693e99SJason King 	uint8_t type;
321*aa693e99SJason King 
322*aa693e99SJason King 	if (msg->lm_mode == L9P_PACK) {
323*aa693e99SJason King 		type = qid->type;
324*aa693e99SJason King 		r = l9p_pu8(msg, &type);
325*aa693e99SJason King 	} else {
326*aa693e99SJason King 		r = l9p_pu8(msg, &type);
327*aa693e99SJason King 		qid->type = type;
328*aa693e99SJason King 	}
329*aa693e99SJason King 	if (r > 0)
330*aa693e99SJason King 		r = l9p_pu32(msg, &qid->version);
331*aa693e99SJason King 	if (r > 0)
332*aa693e99SJason King 		r = l9p_pu64(msg, &qid->path);
333*aa693e99SJason King 
334*aa693e99SJason King 	return (r > 0 ? QID_SIZE : r);
335*aa693e99SJason King }
336*aa693e99SJason King 
337*aa693e99SJason King /*
338*aa693e99SJason King  * Pack or unpack *num qids.
339*aa693e99SJason King  *
340*aa693e99SJason King  * Returns 2 + 13 * *num (after possibly setting *num), or -1 on error.
341*aa693e99SJason King  */
342*aa693e99SJason King static ssize_t
l9p_puqids(struct l9p_message * msg,uint16_t * num,struct l9p_qid * qids)343*aa693e99SJason King l9p_puqids(struct l9p_message *msg, uint16_t *num, struct l9p_qid *qids)
344*aa693e99SJason King {
345*aa693e99SJason King 	size_t i, lim;
346*aa693e99SJason King 	ssize_t ret, r;
347*aa693e99SJason King 
348*aa693e99SJason King 	r = l9p_pu16(msg, num);
349*aa693e99SJason King 	if (r > 0) {
350*aa693e99SJason King 		for (i = 0, lim = *num; i < lim; i++) {
351*aa693e99SJason King 			ret = l9p_puqid(msg, &qids[i]);
352*aa693e99SJason King 			if (ret < 0)
353*aa693e99SJason King 				return (-1);
354*aa693e99SJason King 			r += ret;
355*aa693e99SJason King 		}
356*aa693e99SJason King 	}
357*aa693e99SJason King 	return (r);
358*aa693e99SJason King }
359*aa693e99SJason King 
360*aa693e99SJason King /*
361*aa693e99SJason King  * Pack or unpack a l9p_stat.
362*aa693e99SJason King  *
363*aa693e99SJason King  * These have variable size, and the size further depends on
364*aa693e99SJason King  * the protocol version.
365*aa693e99SJason King  *
366*aa693e99SJason King  * Returns the number of bytes packed/unpacked, or -1 on error.
367*aa693e99SJason King  */
368*aa693e99SJason King ssize_t
l9p_pustat(struct l9p_message * msg,struct l9p_stat * stat,enum l9p_version version)369*aa693e99SJason King l9p_pustat(struct l9p_message *msg, struct l9p_stat *stat,
370*aa693e99SJason King     enum l9p_version version)
371*aa693e99SJason King {
372*aa693e99SJason King 	ssize_t r = 0;
373*aa693e99SJason King 	uint16_t size;
374*aa693e99SJason King 
375*aa693e99SJason King 	/* The on-wire size field excludes the size of the size field. */
376*aa693e99SJason King 	if (msg->lm_mode == L9P_PACK)
377*aa693e99SJason King 		size = l9p_sizeof_stat(stat, version) - 2;
378*aa693e99SJason King 
379*aa693e99SJason King 	r += l9p_pu16(msg, &size);
380*aa693e99SJason King 	r += l9p_pu16(msg, &stat->type);
381*aa693e99SJason King 	r += l9p_pu32(msg, &stat->dev);
382*aa693e99SJason King 	r += l9p_puqid(msg, &stat->qid);
383*aa693e99SJason King 	r += l9p_pu32(msg, &stat->mode);
384*aa693e99SJason King 	r += l9p_pu32(msg, &stat->atime);
385*aa693e99SJason King 	r += l9p_pu32(msg, &stat->mtime);
386*aa693e99SJason King 	r += l9p_pu64(msg, &stat->length);
387*aa693e99SJason King 	r += l9p_pustring(msg, &stat->name);
388*aa693e99SJason King 	r += l9p_pustring(msg, &stat->uid);
389*aa693e99SJason King 	r += l9p_pustring(msg, &stat->gid);
390*aa693e99SJason King 	r += l9p_pustring(msg, &stat->muid);
391*aa693e99SJason King 
392*aa693e99SJason King 	if (version >= L9P_2000U) {
393*aa693e99SJason King 		r += l9p_pustring(msg, &stat->extension);
394*aa693e99SJason King 		r += l9p_pu32(msg, &stat->n_uid);
395*aa693e99SJason King 		r += l9p_pu32(msg, &stat->n_gid);
396*aa693e99SJason King 		r += l9p_pu32(msg, &stat->n_muid);
397*aa693e99SJason King 	}
398*aa693e99SJason King 
399*aa693e99SJason King 	if (r < size + 2)
400*aa693e99SJason King 		return (-1);
401*aa693e99SJason King 
402*aa693e99SJason King 	return (r);
403*aa693e99SJason King }
404*aa693e99SJason King 
405*aa693e99SJason King /*
406*aa693e99SJason King  * Pack or unpack a variable-length dirent.
407*aa693e99SJason King  *
408*aa693e99SJason King  * If unpacking, the name field is malloc()ed and the caller must
409*aa693e99SJason King  * free it.
410*aa693e99SJason King  *
411*aa693e99SJason King  * Returns the wire-format length, or -1 if we ran out of room.
412*aa693e99SJason King  */
413*aa693e99SJason King ssize_t
l9p_pudirent(struct l9p_message * msg,struct l9p_dirent * de)414*aa693e99SJason King l9p_pudirent(struct l9p_message *msg, struct l9p_dirent *de)
415*aa693e99SJason King {
416*aa693e99SJason King 	ssize_t r, s;
417*aa693e99SJason King 
418*aa693e99SJason King 	r = l9p_puqid(msg, &de->qid);
419*aa693e99SJason King 	r += l9p_pu64(msg, &de->offset);
420*aa693e99SJason King 	r += l9p_pu8(msg, &de->type);
421*aa693e99SJason King 	s = l9p_pustring(msg, &de->name);
422*aa693e99SJason King 	if (r < QID_SIZE + 8 + 1 || s < 0)
423*aa693e99SJason King 		return (-1);
424*aa693e99SJason King 	return (r + s);
425*aa693e99SJason King }
426*aa693e99SJason King 
427*aa693e99SJason King /*
428*aa693e99SJason King  * Pack or unpack a request or response (fcall).
429*aa693e99SJason King  *
430*aa693e99SJason King  * Returns 0 on success, -1 on error.  (It's up to the caller
431*aa693e99SJason King  * to call l9p_freefcall on our failure.)
432*aa693e99SJason King  */
433*aa693e99SJason King int
l9p_pufcall(struct l9p_message * msg,union l9p_fcall * fcall,enum l9p_version version)434*aa693e99SJason King l9p_pufcall(struct l9p_message *msg, union l9p_fcall *fcall,
435*aa693e99SJason King     enum l9p_version version)
436*aa693e99SJason King {
437*aa693e99SJason King 	uint32_t length = 0;
438*aa693e99SJason King 	ssize_t r;
439*aa693e99SJason King 
440*aa693e99SJason King 	/*
441*aa693e99SJason King 	 * Get overall length, type, and tag, which should appear
442*aa693e99SJason King 	 * in all messages.  If not even that works, abort immediately.
443*aa693e99SJason King 	 */
444*aa693e99SJason King 	l9p_pu32(msg, &length);
445*aa693e99SJason King 	l9p_pu8(msg, &fcall->hdr.type);
446*aa693e99SJason King 	r = l9p_pu16(msg, &fcall->hdr.tag);
447*aa693e99SJason King 	if (r < 0)
448*aa693e99SJason King 		return (-1);
449*aa693e99SJason King 
450*aa693e99SJason King 	/*
451*aa693e99SJason King 	 * Decode remainder of message.	 When unpacking, this may
452*aa693e99SJason King 	 * allocate memory, even if we fail during the decode.
453*aa693e99SJason King 	 * Note that the initial fcall is zeroed out, though, so
454*aa693e99SJason King 	 * we can just freefcall() to release whatever might have
455*aa693e99SJason King 	 * gotten allocated, if the unpack fails due to a short
456*aa693e99SJason King 	 * packet.
457*aa693e99SJason King 	 */
458*aa693e99SJason King 	switch (fcall->hdr.type) {
459*aa693e99SJason King 	case L9P_TVERSION:
460*aa693e99SJason King 	case L9P_RVERSION:
461*aa693e99SJason King 		l9p_pu32(msg, &fcall->version.msize);
462*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->version.version);
463*aa693e99SJason King 		break;
464*aa693e99SJason King 
465*aa693e99SJason King 	case L9P_TAUTH:
466*aa693e99SJason King 		l9p_pu32(msg, &fcall->tauth.afid);
467*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tauth.uname);
468*aa693e99SJason King 		if (r < 0)
469*aa693e99SJason King 			break;
470*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tauth.aname);
471*aa693e99SJason King 		if (r < 0)
472*aa693e99SJason King 			break;
473*aa693e99SJason King 		if (version >= L9P_2000U)
474*aa693e99SJason King 			r = l9p_pu32(msg, &fcall->tauth.n_uname);
475*aa693e99SJason King 		break;
476*aa693e99SJason King 
477*aa693e99SJason King 	case L9P_RAUTH:
478*aa693e99SJason King 		r = l9p_puqid(msg, &fcall->rauth.aqid);
479*aa693e99SJason King 		break;
480*aa693e99SJason King 
481*aa693e99SJason King 	case L9P_TATTACH:
482*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
483*aa693e99SJason King 		l9p_pu32(msg, &fcall->tattach.afid);
484*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tattach.uname);
485*aa693e99SJason King 		if (r < 0)
486*aa693e99SJason King 			break;
487*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tattach.aname);
488*aa693e99SJason King 		if (r < 0)
489*aa693e99SJason King 			break;
490*aa693e99SJason King 		if (version >= L9P_2000U)
491*aa693e99SJason King 			r = l9p_pu32(msg, &fcall->tattach.n_uname);
492*aa693e99SJason King 		break;
493*aa693e99SJason King 
494*aa693e99SJason King 	case L9P_RATTACH:
495*aa693e99SJason King 		r = l9p_puqid(msg, &fcall->rattach.qid);
496*aa693e99SJason King 		break;
497*aa693e99SJason King 
498*aa693e99SJason King 	case L9P_RERROR:
499*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->error.ename);
500*aa693e99SJason King 		if (r < 0)
501*aa693e99SJason King 			break;
502*aa693e99SJason King 		if (version >= L9P_2000U)
503*aa693e99SJason King 			r = l9p_pu32(msg, &fcall->error.errnum);
504*aa693e99SJason King 		break;
505*aa693e99SJason King 
506*aa693e99SJason King 	case L9P_RLERROR:
507*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->error.errnum);
508*aa693e99SJason King 		break;
509*aa693e99SJason King 
510*aa693e99SJason King 	case L9P_TFLUSH:
511*aa693e99SJason King 		r = l9p_pu16(msg, &fcall->tflush.oldtag);
512*aa693e99SJason King 		break;
513*aa693e99SJason King 
514*aa693e99SJason King 	case L9P_RFLUSH:
515*aa693e99SJason King 		break;
516*aa693e99SJason King 
517*aa693e99SJason King 	case L9P_TWALK:
518*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
519*aa693e99SJason King 		l9p_pu32(msg, &fcall->twalk.newfid);
520*aa693e99SJason King 		r = l9p_pustrings(msg, &fcall->twalk.nwname,
521*aa693e99SJason King 		    fcall->twalk.wname, N(fcall->twalk.wname));
522*aa693e99SJason King 		break;
523*aa693e99SJason King 
524*aa693e99SJason King 	case L9P_RWALK:
525*aa693e99SJason King 		r = l9p_puqids(msg, &fcall->rwalk.nwqid, fcall->rwalk.wqid);
526*aa693e99SJason King 		break;
527*aa693e99SJason King 
528*aa693e99SJason King 	case L9P_TOPEN:
529*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
530*aa693e99SJason King 		r = l9p_pu8(msg, &fcall->topen.mode);
531*aa693e99SJason King 		break;
532*aa693e99SJason King 
533*aa693e99SJason King 	case L9P_ROPEN:
534*aa693e99SJason King 		l9p_puqid(msg, &fcall->ropen.qid);
535*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->ropen.iounit);
536*aa693e99SJason King 		break;
537*aa693e99SJason King 
538*aa693e99SJason King 	case L9P_TCREATE:
539*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
540*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tcreate.name);
541*aa693e99SJason King 		if (r < 0)
542*aa693e99SJason King 			break;
543*aa693e99SJason King 		l9p_pu32(msg, &fcall->tcreate.perm);
544*aa693e99SJason King 		r = l9p_pu8(msg, &fcall->tcreate.mode);
545*aa693e99SJason King 		if (version >= L9P_2000U)
546*aa693e99SJason King 			r = l9p_pustring(msg, &fcall->tcreate.extension);
547*aa693e99SJason King 		break;
548*aa693e99SJason King 
549*aa693e99SJason King 	case L9P_RCREATE:
550*aa693e99SJason King 		l9p_puqid(msg, &fcall->rcreate.qid);
551*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->rcreate.iounit);
552*aa693e99SJason King 		break;
553*aa693e99SJason King 
554*aa693e99SJason King 	case L9P_TREAD:
555*aa693e99SJason King 	case L9P_TREADDIR:
556*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
557*aa693e99SJason King 		l9p_pu64(msg, &fcall->io.offset);
558*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->io.count);
559*aa693e99SJason King 		break;
560*aa693e99SJason King 
561*aa693e99SJason King 	case L9P_RREAD:
562*aa693e99SJason King 	case L9P_RREADDIR:
563*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->io.count);
564*aa693e99SJason King 		break;
565*aa693e99SJason King 
566*aa693e99SJason King 	case L9P_TWRITE:
567*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
568*aa693e99SJason King 		l9p_pu64(msg, &fcall->io.offset);
569*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->io.count);
570*aa693e99SJason King 		break;
571*aa693e99SJason King 
572*aa693e99SJason King 	case L9P_RWRITE:
573*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->io.count);
574*aa693e99SJason King 		break;
575*aa693e99SJason King 
576*aa693e99SJason King 	case L9P_TCLUNK:
577*aa693e99SJason King 	case L9P_TSTAT:
578*aa693e99SJason King 	case L9P_TREMOVE:
579*aa693e99SJason King 	case L9P_TSTATFS:
580*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->hdr.fid);
581*aa693e99SJason King 		break;
582*aa693e99SJason King 
583*aa693e99SJason King 	case L9P_RCLUNK:
584*aa693e99SJason King 	case L9P_RREMOVE:
585*aa693e99SJason King 		break;
586*aa693e99SJason King 
587*aa693e99SJason King 	case L9P_RSTAT:
588*aa693e99SJason King 	{
589*aa693e99SJason King 		uint16_t size = l9p_sizeof_stat(&fcall->rstat.stat,
590*aa693e99SJason King 		    version);
591*aa693e99SJason King 		l9p_pu16(msg, &size);
592*aa693e99SJason King 		r = l9p_pustat(msg, &fcall->rstat.stat, version);
593*aa693e99SJason King 	}
594*aa693e99SJason King 		break;
595*aa693e99SJason King 
596*aa693e99SJason King 	case L9P_TWSTAT:
597*aa693e99SJason King 	{
598*aa693e99SJason King 		uint16_t size;
599*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
600*aa693e99SJason King 		l9p_pu16(msg, &size);
601*aa693e99SJason King 		r = l9p_pustat(msg, &fcall->twstat.stat, version);
602*aa693e99SJason King 	}
603*aa693e99SJason King 		break;
604*aa693e99SJason King 
605*aa693e99SJason King 	case L9P_RWSTAT:
606*aa693e99SJason King 		break;
607*aa693e99SJason King 
608*aa693e99SJason King 	case L9P_RSTATFS:
609*aa693e99SJason King 		l9p_pu32(msg, &fcall->rstatfs.statfs.type);
610*aa693e99SJason King 		l9p_pu32(msg, &fcall->rstatfs.statfs.bsize);
611*aa693e99SJason King 		l9p_pu64(msg, &fcall->rstatfs.statfs.blocks);
612*aa693e99SJason King 		l9p_pu64(msg, &fcall->rstatfs.statfs.bfree);
613*aa693e99SJason King 		l9p_pu64(msg, &fcall->rstatfs.statfs.bavail);
614*aa693e99SJason King 		l9p_pu64(msg, &fcall->rstatfs.statfs.files);
615*aa693e99SJason King 		l9p_pu64(msg, &fcall->rstatfs.statfs.ffree);
616*aa693e99SJason King 		l9p_pu64(msg, &fcall->rstatfs.statfs.fsid);
617*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->rstatfs.statfs.namelen);
618*aa693e99SJason King 		break;
619*aa693e99SJason King 
620*aa693e99SJason King 	case L9P_TLOPEN:
621*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
622*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->tlopen.flags);
623*aa693e99SJason King 		break;
624*aa693e99SJason King 
625*aa693e99SJason King 	case L9P_RLOPEN:
626*aa693e99SJason King 		l9p_puqid(msg, &fcall->rlopen.qid);
627*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->rlopen.iounit);
628*aa693e99SJason King 		break;
629*aa693e99SJason King 
630*aa693e99SJason King 	case L9P_TLCREATE:
631*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
632*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tlcreate.name);
633*aa693e99SJason King 		if (r < 0)
634*aa693e99SJason King 			break;
635*aa693e99SJason King 		l9p_pu32(msg, &fcall->tlcreate.flags);
636*aa693e99SJason King 		l9p_pu32(msg, &fcall->tlcreate.mode);
637*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->tlcreate.gid);
638*aa693e99SJason King 		break;
639*aa693e99SJason King 
640*aa693e99SJason King 	case L9P_RLCREATE:
641*aa693e99SJason King 		l9p_puqid(msg, &fcall->rlcreate.qid);
642*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->rlcreate.iounit);
643*aa693e99SJason King 		break;
644*aa693e99SJason King 
645*aa693e99SJason King 	case L9P_TSYMLINK:
646*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
647*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tsymlink.name);
648*aa693e99SJason King 		if (r < 0)
649*aa693e99SJason King 			break;
650*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tsymlink.symtgt);
651*aa693e99SJason King 		if (r < 0)
652*aa693e99SJason King 			break;
653*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->tlcreate.gid);
654*aa693e99SJason King 		break;
655*aa693e99SJason King 
656*aa693e99SJason King 	case L9P_RSYMLINK:
657*aa693e99SJason King 		r = l9p_puqid(msg, &fcall->rsymlink.qid);
658*aa693e99SJason King 		break;
659*aa693e99SJason King 
660*aa693e99SJason King 	case L9P_TMKNOD:
661*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
662*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->tmknod.name);
663*aa693e99SJason King 		if (r < 0)
664*aa693e99SJason King 			break;
665*aa693e99SJason King 		l9p_pu32(msg, &fcall->tmknod.mode);
666*aa693e99SJason King 		l9p_pu32(msg, &fcall->tmknod.major);
667*aa693e99SJason King 		l9p_pu32(msg, &fcall->tmknod.minor);
668*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->tmknod.gid);
669*aa693e99SJason King 		break;
670*aa693e99SJason King 
671*aa693e99SJason King 	case L9P_RMKNOD:
672*aa693e99SJason King 		r = l9p_puqid(msg, &fcall->rmknod.qid);
673*aa693e99SJason King 		break;
674*aa693e99SJason King 
675*aa693e99SJason King 	case L9P_TRENAME:
676*aa693e99SJason King 		l9p_pu32(msg, &fcall->hdr.fid);
677*aa693e99SJason King 		l9p_pu32(msg, &fcall->trename.dfid);
678*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->trename.name);
679*aa693e99SJason King 		break;
680*aa693e99SJason King 
681*aa693e99SJason King 	case L9P_RRENAME:
682*aa693e99SJason King 		break;
683*aa693e99SJason King 
684*aa693e99SJason King 	case L9P_TREADLINK:
685*aa693e99SJason King 		r = l9p_pu32(msg, &fcall->hdr.fid);
686*aa693e99SJason King 		break;
687*aa693e99SJason King 
688*aa693e99SJason King 	case L9P_RREADLINK:
689*aa693e99SJason King 		r = l9p_pustring(msg, &fcall->rreadlink.target);
690*aa693e99SJason King 		break;
691*aa693e99SJason King 
692*aa693e99SJason King 	case L9P_TGETATTR: