Client implementation of the 9P protocol for constrained devices
git clone https://git.8pit.net/ninenano.git
1#ifndef NINEP_H 2#define NINEP_H 3 4#include <stdint.h> 5#include <stddef.h> 6 7#include <sys/stat.h> 8#include <sys/types.h> 9 10/** 11 * 9P version implemented by this library. 12 * 13 * From version(5): 14 * Currently, the only defined version is the 6 characters `9P2000`. 15 */ 16#define _9P_VERSION "9P2000" 17 18/** 19 * From version(5): 20 * The client suggests a maximum message size, msize, that is the 21 * maximum length, in bytes, it will ever generate or expect to 22 * receive in a single 9P message. 23 */ 24#ifndef _9P_MSIZE 25 #define _9P_MSIZE 1024 26#endif 27 28/** 29 * From intro(5): 30 * Most T-messages contain a fid, a 32-bit unsigned integer that the 31 * client uses to identify a ``current file'' on the server. Fids are 32 * somewhat like file descriptors in a user process, [...] all files 33 * being manipulated by the operating system - are identified by fids. 34 * 35 * From intro(5): 36 * Replies (R-messages) to auth, attach, walk, open, and create 37 * requests convey a qid field back to the client. The qid represents 38 * the server's unique identification for the file being accessed: two 39 * files on the same server hierarchy are the same if and only if 40 * their qids are the same. 41 * 42 * We need to map fids to qids, we do this using a primitve hash table 43 * implementation. The amount of maximum entries in this hash table can 44 * be tweaked by defining this macro. 45 */ 46#ifndef _9P_MAXFIDS 47 #define _9P_MAXFIDS 16 48#endif 49 50/** 51 * From intro(5): 52 * An exception is the tag NOTAG, defined as (ushort)~0 in <fcall.h>: 53 * the client can use it, when establishing a connection, to override 54 * tag matching in version messages. 55 */ 56#define _9P_NOTAG (uint16_t)~0 57 58/** 59 * From attach(5): 60 * If the client does not wish to authenticate the connection, or 61 * knows that authentication is not required, the afid field in the 62 * attach message should be set to NOFID, defined as (u32int)~0 in 63 * <fcall.h>. 64 */ 65#define _9P_NOFID (uint32_t)~0 66 67/** 68 * Enum defining sizes of 8..64 bit fields in bytes. 69 */ 70enum { 71 BIT8SZ = 1, /**< Size of 8 bit field in bytes. */ 72 BIT16SZ = 2, /**< Size of 16 bit field in bytes. */ 73 BIT32SZ = 4, /**< Size of 32 bit field in bytes. */ 74 BIT64SZ = 8, /**< Size of 64 bit field in bytes. */ 75}; 76 77/** 78 * Enum defining various global variables. 79 */ 80enum { 81 /** 82 * Size of the 9P packet header. Actually intro(5) never mention a 83 * header but the first 7 byte of every valid 9P message are always the 84 * same so I am going to take the liberty of refering to those 7 bytes 85 * as `header` in the comments. 86 */ 87 _9P_HEADSIZ = BIT32SZ + 1 + BIT16SZ, 88 89 /** 90 * Size of a qid consisting of one one-byte field, one four-byte 91 * field and one eight-byte field. 92 */ 93 _9P_QIDSIZ = BIT8SZ + BIT32SZ + BIT64SZ, 94 95 /** 96 * Ample room for Twrite/Rread header (iounit). This has been 97 * copied from the Plan 9 sourcetree, the file can be found at 98 * the location `sys/include/fcall.h`. 99 */100 _9P_IOHDRSIZ = 24,101102 /**103 * Minimum size of the machine-independent directory entry,104 * stat. This includes the leading 16-bit count. See stat(5) for105 * more information.106 */107 _9P_MINSTSIZ = 3 * BIT16SZ + BIT32SZ + _9P_QIDSIZ108 + 3 * BIT32SZ + BIT64SZ + 4 * BIT16SZ,109110 /**111 * Maximum length of a version string in a R-message. The112 * longest valid version string is the 7 characters `unknown`.113 * In addition to that we need to reserve one-byte for the114 * nullbyte.115 */116 _9P_VERLEN = 8,117118 /**119 * Used in the ::_9pattach function to identify the root120 * directory of the desired file tree.121 */122 _9P_ROOTFID = 1,123124 /**125 * From walk(5):126 * To simplify the implementation of the servers, a maximum of127 * sixteen name elements or qids may be packed in a single message.128 */129 _9P_MAXWEL = 16,130131 /**132 * Path seperator used to split a path into nwnames.133 */134 _9P_PATHSEP = '/',135136 /**137 * Minimum msize we are able to handle. Only the Rwrite and138 * Rread message support sending more than one message for a139 * request. Therefore all other message types have to fit within140 * this limit.141 */142 _9P_MINSIZE = 64,143};144145/**146 * From stat(5):147 * The mode contains permission bits as described in intro(5)148 * and the following: 0x80000000 (DMDIR, this file is a direc-149 * tory), 0x40000000 (DMAPPEND, append only), 0x20000000150 * (DMEXCL, exclusive use), 0x04000000 (DMTMP, temporary);151 * these are echoed in Qid.type.152 *153 * These definition have been copied from the Plan 9 sourcetree.154 * The file can be found at the location `sys/include/libc.h`.155 */156#define DMDIR 0x80000000 /**< Mode bit for directories. */157#define DMAPPEND 0x40000000 /**< Mode bit for append only files. */158#define DMEXCL 0x20000000 /**< Mode bit for exclusive use files. */159#define DMMOUNT 0x10000000 /**< Mode bit for mounted channel. */160#define DMAUTH 0x08000000 /**< Mode bit for authentication file. */161#define DMTMP 0x04000000 /**< Mode bit for non-backed-up files. */162#define DMREAD 0x4 /**< Mode bit for read permission. */163#define DMWRITE 0x2 /**< Mode bit for write permission. */164#define DMEXEC 0x1 /**< Mode bit for execute permission. */165166/**167 * Same bits as defined above for Qid.type.168 */169#define QTDIR 0x80 /* Type bit for directories. */170#define QTAPPEND 0x40 /* Type bit for append only files. */171#define QTEXCL 0x20 /* Type bit for exclusive use files. */172#define QTMOUNT 0x10 /* Type bit for mounted channel. */173#define QTAUTH 0x08 /* Type bit for authentication file. */174#define QTTMP 0x04 /* Type bit for not-backed-up file. */175#define QTFILE 0x00 /* Plain file. */176177/* From open(5):178 * The mode field determines the type of I/O: 0 (called OREAD in179 * <libc.h>), 1 (OWRITE), 2 (ORDWR), and 3 (OEXEC) mean read access,180 * write access, read and write access, and execute access, to be181 * checked against the per- missions for the file. In addition, if182 * mode has the OTRUNC (0x10) bit set, the file is to be truncated183 * [...]184 *185 * These definition have been copied from the Plan 9 sourcetree.186 * The file can be found at the location `sys/include/libc.h`.187 */188#define OREAD 0 /**< open for read */189#define OWRITE 1 /**< write */190#define ORDWR 2 /**< read and write */191#define OTRUNC 16 /**< or'ed in (except for exec), truncate file first */192193/**194 * Valid values for the type field of a 9P message. This has been copied195 * from the Plan 9 sourcetree, the file can be found at the location196 * `sys/include/fcall.h`.197 */198typedef enum {199 Tversion = 100,200 Rversion,201 Tauth = 102,202 Rauth,203 Tattach = 104,204 Rattach,205 Terror = 106, /* illegal */206 Rerror,207 Tflush = 108,208 Rflush,209 Twalk = 110,210 Rwalk,211 Topen = 112,212 Ropen,213 Tcreate = 114,214 Rcreate,215 Tread = 116,216 Rread,217 Twrite = 118,218 Rwrite,219 Tclunk = 120,220 Rclunk,221 Tremove = 122,222 Rremove,223 Tstat = 124,224 Rstat,225 Twstat = 126,226 Rwstat,227 Tmax,228} _9ptype;229230/**231 * Enum defining operating which should be performed on the fid table.232 */233typedef enum {234 ADD, /**< Add the given fid to the table. */235 GET, /**< Get the given fid from the table. */236 DEL, /**< Delete the given fid from the table. */237} _9pfidop;238239/**240 * Struct representing a qid.241 *242 * From intro(5):243 * The thirteen-byte qid fields hold a one-byte type, specifying244 * whether the file is a directory, append-only file, etc., and two245 * unsigned integers: first the four-byte qid version, then the eight-246 * byte qid path.247 */248typedef struct {249 /**250 * From intro(5):251 * [...] Whether the file is a directory, append-only file,252 * etc. [...]253 */254 uint8_t type;255256 /**257 * From intro(5):258 * The version is a version number for a file; typically, it259 * is incremented every time the file is modified.260 */261 uint32_t vers;262263 /**264 * From intro(5):265 * The path is an integer unique among all files in the266 * hierarchy. If a file is deleted and recreated with the same267 * name in the same directory, the old and new path components268 * of the qids should be different.269 */270 uint64_t path;271} _9pqid;272273/**274 * Struct representing an open fid.275 *276 * From intro(5):277 * Most T-messages contain a fid, a 32-bit unsigned integer that the278 * client uses to identify a ``current file'' on the server. Fids are279 * somewhat like file descriptors in a user process, [...] all files280 * being manipulated by the operating system - are identified by fids.281 */282typedef struct {283 /**284 * The 32-bit unsigned integer representation of this fid.285 */286 uint32_t fid;287288 /**289 * The qid associated with this fid.290 */291 _9pqid qid;292293 /**294 * Offset in file associated with this fid.295 */296 uint64_t off;297298 /**299 * iounit for this file as returned by open(5).300 */301 uint32_t iounit;302} _9pfid;303304/**305 * Struct representing a 9P package. For the sake of simplicity it only306 * has length, tag and type fields since those are the only fields307 * present in all 9P messages. Message specific parameters have to be308 * parsed manually.309 */310typedef struct {311 /**312 * Pointer to the beginning of the message specific parameters.313 * Values in this buffer have to be encoded in little-endian314 * byte order.315 */316 uint8_t *buf;317318 /**319 * Length of the message specific parameters. The length of the320 * total message as specified by intro(5) can be calculated by321 * adding the length of the header (7 bytes) to the value of322 * this field.323 */324 uint32_t len;325326 /**327 * Type of the message.328 */329 _9ptype type; /* XXX Use uint8_t instead? */330331 /**332 * Unique message tag of this particular message.333 */334 uint16_t tag;335} _9ppkt;336337/**338 * Function used for receiving data that should be parsed as a 9P339 * R-messages and for sending T-messages to the server.340 *341 * @param buf Pointer where the data should be written to / read from.342 * @param bufsiz Maximum space available at @p data.343 * @return The number of bytes read on success.344 * @return `0`, if no read data is available, but everything is in order.345 * @return A negative errno value on error.346 */347typedef ssize_t (*iofunc)(void *buf, size_t bufsiz);348349/**350 * Connection context for a 9P connection.351 */352typedef struct {353 /**354 * Global static buffer used for storing message specific355 * parameters. Message indepented parameters are stored on the356 * stack using `_9ppkt`.357 */358 uint8_t buffer[_9P_MSIZE];359360 /**361 * Function used to receive R-messages.362 */363 iofunc read;364365 /**366 * Function used to send T-messages.367 */368 iofunc write;369370 /**371 * From version(5):372 * The client suggests a maximum message size, msize, that is373 * the maximum length, in bytes, it will ever generate or374 * expect to receive in a single 9P message.375 *376 * The msize we are suggestion to the server is defined by the377 * macro ::_9P_MSIZE for the unlikly event that the server378 * choosen an msize smaller than the one we are suggesting we379 * are storing the msize actually used for the communication in380 * this variable.381 *382 * It is declared with the initial value ::_9P_MSIZE to make it383 * possible to use this variable as an argument to the read384 * function even before a session is established.385 */386 uint32_t msize;387388 /**389 * As with file descriptors, we need to store currently open390 * fids somehow. This is done in this static buffer. This buffer391 * should only be accessed using the ::fidtbl function it should392 * never be modified directly.393 *394 * This buffer is not static because it is used in the ::fidtbl395 * function from `util.c`.396 */397 _9pfid fids[_9P_MAXFIDS];398} _9pctx;399400void _9pinit(_9pctx*, iofunc, iofunc);401int _9pversion(_9pctx*);402int _9pattach(_9pctx*, _9pfid**, char*, char*);403int _9pclunk(_9pctx*, _9pfid*);404int _9pstat(_9pctx*, _9pfid*, struct stat*);405int _9pwalk(_9pctx*, _9pfid**, char*);406int _9popen(_9pctx*, _9pfid*, uint8_t);407int _9pcreate(_9pctx*, _9pfid*, char*, uint32_t, uint8_t);408ssize_t _9pread(_9pctx*, _9pfid*, char*, size_t);409ssize_t _9pwrite(_9pctx*, _9pfid*, char*, size_t);410int _9premove(_9pctx*, _9pfid*);411412#endif