ninenano

Client implementation of the 9P protocol for constrained devices

git clone https://git.8pit.net/ninenano.git

  1#include <assert.h>
  2#include <string.h>
  3#include <stdint.h>
  4
  5#include <sys/types.h>
  6
  7#include "9p.h"
  8#include "9util.h"
  9#include "byteorder.h"
 10
 11/**
 12 * From intro(5):
 13 *   Each message consists of a sequence of bytes. Two-, four-, and
 14 *   eight-byte fields hold unsigned integers represented in
 15 *   little-endian order (least significant byte first).
 16 *
 17 * Since we want to write an endian-agnostic implementation we define a
 18 * macro called `_9p_swap` which is similar to `_byteorder_swap` but
 19 * doesn't swap the byte order on little endian plattforms.
 20 */
 21#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 22  #define _9p_swap(V, T) (V)
 23#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
 24  #define _9p_swap(V, T) (byteorder_swap##T((V)))
 25#else
 26  #error "Byte order is neither little nor big!"
 27#endif
 28
 29/**
 30 * @defgroup pktBuf Functions for packet buffers.
 31 *
 32 * @{
 33 */
 34
 35/**
 36 * Advances the position in the packet buffer. The macro takes care of
 37 * decrementing the length field of the packet as well.
 38 *
 39 * @param pkt Pointer to a packet in which the buffer position should be
 40 * 	advanced.
 41 * @param off Offset which should be added to the buffer position.
 42 */
 43void
 44advbuf(_9ppkt *pkt, size_t off)
 45{
 46	assert(pkt->len - off <= pkt->len);
 47
 48	pkt->buf += off;
 49	pkt->len -= off;
 50}
 51
 52/**
 53 * Copies n bytes from the given memory area to a packet buffer and
 54 * afterwards advances the position in the packet buffer.
 55 *
 56 * @param pkt Pointer to a packet to which the data should be copied.
 57 * @param src Pointer to a buffer from which the data should be read.
 58 * @param n Amount of bytse which should be copied.
 59 */
 60void
 61bufcpy(_9ppkt *pkt, void *src, size_t n)
 62{
 63	memcpy(pkt->buf, src, n);
 64	advbuf(pkt, n);
 65}
 66
 67/**@}*/
 68
 69/**
 70 * @defgroup fidTbl Functions for the fid table.
 71 *
 72 * @{
 73 */
 74
 75/**
 76 * This function can be used to add, get and delete fids. It has one
 77 * unexpected caveat: The add operation does set the `fid` field, it
 78 * simply returns the next free fid. Thus the callers has to set the
 79 * `fid` field on the struct pointer manually.
 80 *
 81 * @pre fid != 0
 82 *
 83 * @param fids Pointer to fid table which should be modified. The table
 84 *   size must be equal to ::_9P_MAXFIDS.
 85 * @param fid A 32-bit unsigned integer that the client uses to identify
 86 *   a `current file` on the server.
 87 * @param op Operating which should be performed for the given fid on
 88 *   the fid table.
 89 * @return On success a pointer to a fid in the fid table is returned,
 90 *   on failure a NULL pointer is returned instead.
 91 */
 92_9pfid*
 93fidtbl(_9pfid *fids, uint32_t fid, _9pfidop op)
 94{
 95	_9pfid *ret;
 96	size_t i, hash;
 97
 98	/* A value of 0 is used to indicate an unused table entry. */
 99	if (!fid)
100		return NULL;
101
102	hash = i = fid % _9P_MAXFIDS;
103	if (op == ADD)
104		fid = 0;
105
106	do {
107		if ((ret = &fids[i])->fid == fid)
108			break;
109
110		i = (i + 1) % _9P_MAXFIDS;
111	} while (i != hash);
112
113	if (ret->fid != fid)
114		return NULL;
115	if (op == DEL) {
116		if (ret->fid == _9P_ROOTFID)
117			return NULL;
118		ret->fid = 0;
119	}
120
121	return ret;
122}
123
124/**
125 * Finds a new **unique** fid for the fid table. Insert it into the fid
126 * table and returns a pointer to the new fid table entry.
127 *
128 * @param fids Pointer to fid table which should be modified. The table
129 *   size must be equal to ::_9P_MAXFIDS.
130 * @return Pointer to fid table entry or NULL if the fid table is full.
131 */
132_9pfid*
133newfid(_9pfid *fids)
134{
135	_9pfid *r;
136	size_t i;
137	uint32_t fid;
138
139	for (i = 1; i <= _9P_MAXFIDS; i++) {
140		fid = randu32();
141		if (!fid) fid++;
142
143		/* TODO room for optimization don't call fidtbl twice. */
144		if (fidtbl(fids, fid, GET))
145			continue;
146		if (!(r = fidtbl(fids, fid, ADD)))
147			break;
148
149		r->fid = fid;
150		return r;
151	}
152
153	return NULL;
154}
155
156/**@}*/
157
158/**
159 * @defgroup htop Functions for converting from host byte order to the \
160 *   byte order used by the 9P protocol.
161 *
162 * Functions for converting integers encoded using the byte order used
163 * by the current CPU (the host system) to the one used by the protocol
164 * (little-endian).
165 *
166 * These functions take two arguments: An unsigned integer and a pointer
167 * to a packet buffer to which the resulting integer in the protocol
168 * representation should be written. The position in the packet buffer
169 * is advanced after writting the integer to it.
170 *
171 * @{
172 */
173
174void
175htop8(uint8_t val, _9ppkt *pkt)
176{
177	*pkt->buf = val;
178	advbuf(pkt, BIT8SZ);
179}
180
181void
182htop16(uint16_t val, _9ppkt *pkt)
183{
184	val = _9p_swap(val, s);
185	bufcpy(pkt, &val, BIT16SZ);
186}
187
188void
189htop32(uint32_t val, _9ppkt *pkt)
190{
191	val = _9p_swap(val, l);
192	bufcpy(pkt, &val, BIT32SZ);
193}
194
195void
196htop64(uint64_t val, _9ppkt *pkt)
197{
198	val = _9p_swap(val, ll);
199	bufcpy(pkt, &val, BIT64SZ);
200}
201
202/**@}*/
203
204/**
205 * @defgroup ptoh Functions for converting from the byte order used by \
206 *   the 9P protocol to the byte order used by the host.
207 *
208 * Functions for converting integers encoded using the byte order used
209 * by the 9P protocol to the one used by the CPU (the host system).
210 *
211 * To receive the integer which should be converted those functions read
212 * a certain amount of bytes from the buffer pointed to by the `buf`
213 * field in the given `_9ppkt`. After reading the integer the position
214 * in the buffer is advanced by the amount of bytes read. Besides the
215 * length of the 9P packet is updated.
216 *
217 * @{
218 */
219
220void
221ptoh8(uint8_t *dest, _9ppkt *pkt)
222{
223	*dest = *pkt->buf;
224	advbuf(pkt, BIT8SZ);
225}
226
227void
228ptoh16(uint16_t *dest, _9ppkt *pkt)
229{
230	memcpy(dest, pkt->buf, BIT16SZ);
231	*dest = _9p_swap(*dest, s);
232	advbuf(pkt, BIT16SZ);
233}
234
235void
236ptoh32(uint32_t *dest, _9ppkt *pkt)
237{
238	memcpy(dest, pkt->buf, BIT32SZ);
239	*dest = _9p_swap(*dest, l);
240	advbuf(pkt, BIT32SZ);
241}
242
243void
244ptoh64(uint64_t *dest, _9ppkt *pkt)
245{
246	memcpy(dest, pkt->buf, BIT64SZ);
247	*dest = _9p_swap(*dest, ll);
248	advbuf(pkt, BIT64SZ);
249}
250
251/**@}*/
252
253/**
254 * @defgroup strings Functions for converting strings from host \
255 *   representation to protocol representation and vice versa.
256 *
257 * @{
258 */
259
260/**
261 * Does the same thing as ::pstring except for the fact that this
262 * function takes a string length parameter and therefore also works on
263 * strings which are not null-terminated.
264 *
265 * @param str Pointer to a buffer containing a string.
266 * @param len Length of the string contained in the buffer.
267 * @param pkt Pointer to a packet to which the resulting string should
268 * 	be written.
269 * @return `0` on success.
270 * @return `-1` on failure.
271 */
272int
273pnstring(char *str, size_t len, _9ppkt *pkt)
274{
275	if (len + BIT16SZ > pkt->len)
276		return -1;
277
278	htop16((uint16_t)len, pkt);
279	if (len) bufcpy(pkt, str, len);
280
281	return 0;
282}
283
284/**
285 * From intro(5):
286 *   The notation string[s] (using a literal s character) is shorthand
287 *   for s[2] followed by s bytes of UTF-8 text.
288 *
289 * Converts the null-terminated string in the given buffer to a string
290 * as defined in intro(5) prefixed with a two byte size field.
291 *
292 * Besides the position of the given packet buffer is advanced. This
293 * function might fail if the string length exceeds the amount of bytes
294 * available in the packet buffer.
295 *
296 * @param str Pointer to the null-terminated string.
297 * @param pkt Pointer to a packet to which the resulting string should
298 * 	be written.
299 * @return `0` on success.
300 * @return `-1` on failure.
301 */
302int
303pstring(char *str, _9ppkt *pkt)
304{
305	size_t len;
306
307	len = (str) ? strlen(str) : 0;
308	return pnstring(str, len, pkt);
309}
310
311/**
312 * This function converts a string as represented by the protocol to a
313 * string as represented by the host.
314 *
315 * From intro(5):
316 *   The notation string[s] (using a literal s character) is shorthand
317 *   for s[2] followed by s bytes of UTF-8 text.
318 *
319 * We thus read the first two bytes of the memory location pointed to by
320 * the `buf` field of the given 9P packet. We use those two bytes to
321 * determine the length of the string. If this length exceeds the packet
322 * length or the given parameter `n` an error is returned. Otherwise we
323 * copy the bytes after the length field to the given destination address.
324 *
325 * The parameter `n` is a `uint16_t` value because the maximum length of
326 * a 9P string is `2^16` since the length field consists of 2 bytes.
327 *
328 * @param dest Pointer to memory location to store string in. The string
329 *   will always be null-terminated unless an error has occured.
330 * @param n Size of the given buffer.
331 * @param pkt 9P packet to read string from.
332 * @return `0` on success.
333 * @return `-1` on failure.
334 */
335int
336hstring(char *dest, uint16_t n, _9ppkt *pkt)
337{
338	uint16_t siz;
339
340	if (pkt->len < BIT16SZ)
341		return -1;
342	ptoh16(&siz, pkt);
343
344	if (pkt->len < siz || siz >= n)
345		return -1;
346
347	memcpy(dest, pkt->buf, siz);
348	dest[siz] = '\0';
349
350	advbuf(pkt, siz);
351	return 0;
352}
353
354/**@}*/
355
356/**
357 * @defgroup qids Functions for converting qids.
358 *
359 * @{
360 */
361
362/**
363 * From intro(5):
364 *   The thirteen-byte qid fields hold a one-byte type, specifying
365 *   whether the file is a directory, append-only file, etc., and two
366 *   unsigned integers: first the four-byte qid version, then the eight-
367 *   byte qid path.
368 *
369 * This function converts such a qid representation to a ::_9pqid
370 * struct.
371 *
372 * @param dest Pointer to an allocated ::_9pqid struct.
373 * @param pkt 9P packet to read qid from.
374 * @return `0` on success.
375 * @return `-1` on failure.
376 */
377int
378hqid(_9pqid *dest, _9ppkt *pkt)
379{
380	if (pkt->len < _9P_QIDSIZ)
381		return -1;
382
383	ptoh8(&dest->type, pkt);
384	ptoh32(&dest->vers, pkt);
385	ptoh64(&dest->path, pkt);
386
387	return 0;
388}
389
390/**@}*/