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;101102 hash = i = fid % _9P_MAXFIDS;103 if (op == ADD)104 fid = 0;105106 do {107 if ((ret = &fids[i])->fid == fid)108 break;109110 i = (i + 1) % _9P_MAXFIDS;111 } while (i != hash);112113 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 }120121 return ret;122}123124/**125 * Finds a new **unique** fid for the fid table. Insert it into the fid126 * table and returns a pointer to the new fid table entry.127 *128 * @param fids Pointer to fid table which should be modified. The table129 * 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;138139 for (i = 1; i <= _9P_MAXFIDS; i++) {140 fid = randu32();141 if (!fid) fid++;142143 /* 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;148149 r->fid = fid;150 return r;151 }152153 return NULL;154}155156/**@}*/157158/**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 used163 * by the current CPU (the host system) to the one used by the protocol164 * (little-endian).165 *166 * These functions take two arguments: An unsigned integer and a pointer167 * to a packet buffer to which the resulting integer in the protocol168 * representation should be written. The position in the packet buffer169 * is advanced after writting the integer to it.170 *171 * @{172 */173174void175htop8(uint8_t val, _9ppkt *pkt)176{177 *pkt->buf = val;178 advbuf(pkt, BIT8SZ);179}180181void182htop16(uint16_t val, _9ppkt *pkt)183{184 val = _9p_swap(val, s);185 bufcpy(pkt, &val, BIT16SZ);186}187188void189htop32(uint32_t val, _9ppkt *pkt)190{191 val = _9p_swap(val, l);192 bufcpy(pkt, &val, BIT32SZ);193}194195void196htop64(uint64_t val, _9ppkt *pkt)197{198 val = _9p_swap(val, ll);199 bufcpy(pkt, &val, BIT64SZ);200}201202/**@}*/203204/**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 used209 * 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 read212 * a certain amount of bytes from the buffer pointed to by the `buf`213 * field in the given `_9ppkt`. After reading the integer the position214 * in the buffer is advanced by the amount of bytes read. Besides the215 * length of the 9P packet is updated.216 *217 * @{218 */219220void221ptoh8(uint8_t *dest, _9ppkt *pkt)222{223 *dest = *pkt->buf;224 advbuf(pkt, BIT8SZ);225}226227void228ptoh16(uint16_t *dest, _9ppkt *pkt)229{230 memcpy(dest, pkt->buf, BIT16SZ);231 *dest = _9p_swap(*dest, s);232 advbuf(pkt, BIT16SZ);233}234235void236ptoh32(uint32_t *dest, _9ppkt *pkt)237{238 memcpy(dest, pkt->buf, BIT32SZ);239 *dest = _9p_swap(*dest, l);240 advbuf(pkt, BIT32SZ);241}242243void244ptoh64(uint64_t *dest, _9ppkt *pkt)245{246 memcpy(dest, pkt->buf, BIT64SZ);247 *dest = _9p_swap(*dest, ll);248 advbuf(pkt, BIT64SZ);249}250251/**@}*/252253/**254 * @defgroup strings Functions for converting strings from host \255 * representation to protocol representation and vice versa.256 *257 * @{258 */259260/**261 * Does the same thing as ::pstring except for the fact that this262 * function takes a string length parameter and therefore also works on263 * 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 should268 * be written.269 * @return `0` on success.270 * @return `-1` on failure.271 */272int273pnstring(char *str, size_t len, _9ppkt *pkt)274{275 if (len + BIT16SZ > pkt->len)276 return -1;277278 htop16((uint16_t)len, pkt);279 if (len) bufcpy(pkt, str, len);280281 return 0;282}283284/**285 * From intro(5):286 * The notation string[s] (using a literal s character) is shorthand287 * for s[2] followed by s bytes of UTF-8 text.288 *289 * Converts the null-terminated string in the given buffer to a string290 * as defined in intro(5) prefixed with a two byte size field.291 *292 * Besides the position of the given packet buffer is advanced. This293 * function might fail if the string length exceeds the amount of bytes294 * 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 should298 * be written.299 * @return `0` on success.300 * @return `-1` on failure.301 */302int303pstring(char *str, _9ppkt *pkt)304{305 size_t len;306307 len = (str) ? strlen(str) : 0;308 return pnstring(str, len, pkt);309}310311/**312 * This function converts a string as represented by the protocol to a313 * string as represented by the host.314 *315 * From intro(5):316 * The notation string[s] (using a literal s character) is shorthand317 * 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 by320 * the `buf` field of the given 9P packet. We use those two bytes to321 * determine the length of the string. If this length exceeds the packet322 * length or the given parameter `n` an error is returned. Otherwise we323 * 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 of326 * 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 string329 * 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 */335int336hstring(char *dest, uint16_t n, _9ppkt *pkt)337{338 uint16_t siz;339340 if (pkt->len < BIT16SZ)341 return -1;342 ptoh16(&siz, pkt);343344 if (pkt->len < siz || siz >= n)345 return -1;346347 memcpy(dest, pkt->buf, siz);348 dest[siz] = '\0';349350 advbuf(pkt, siz);351 return 0;352}353354/**@}*/355356/**357 * @defgroup qids Functions for converting qids.358 *359 * @{360 */361362/**363 * From intro(5):364 * The thirteen-byte qid fields hold a one-byte type, specifying365 * whether the file is a directory, append-only file, etc., and two366 * 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 ::_9pqid370 * 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 */377int378hqid(_9pqid *dest, _9ppkt *pkt)379{380 if (pkt->len < _9P_QIDSIZ)381 return -1;382383 ptoh8(&dest->type, pkt);384 ptoh32(&dest->vers, pkt);385 ptoh64(&dest->path, pkt);386387 return 0;388}389390/**@}*/