ninenano

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,
101
102	/**
103	 * Minimum size of the machine-independent directory entry,
104	 * stat. This includes the leading 16-bit count. See stat(5) for
105	 * more information.
106	 */
107	_9P_MINSTSIZ = 3 * BIT16SZ + BIT32SZ + _9P_QIDSIZ
108		+ 3 * BIT32SZ + BIT64SZ + 4 * BIT16SZ,
109
110	/**
111	 * Maximum length of a version string in a R-message. The
112	 * longest valid version string is the 7 characters `unknown`.
113	 * In addition to that we need to reserve one-byte for the
114	 * nullbyte.
115	 */
116	_9P_VERLEN = 8,
117
118	/**
119	 * Used in the ::_9pattach function to identify the root
120	 * directory of the desired file tree.
121	 */
122	_9P_ROOTFID = 1,
123
124	/**
125	 * From walk(5):
126	 *   To simplify the implementation of the servers, a maximum of
127	 *   sixteen name elements or qids may be packed in a single message.
128	 */
129	_9P_MAXWEL = 16,
130
131	/**
132	 * Path seperator used to split a path into nwnames.
133	 */
134	_9P_PATHSEP = '/',
135
136	/**
137	 * Minimum msize we are able to handle. Only the Rwrite and
138	 * Rread message support sending more than one message for a
139	 * request. Therefore all other message types have to fit within
140	 * this limit.
141	 */
142	_9P_MINSIZE = 64,
143};
144
145/**
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), 0x20000000
150 *   (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. */
165
166/**
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. */
176
177/* From open(5):
178 *   The mode field determines the type of I/O: 0 (called OREAD in
179 *   <libc.h>), 1 (OWRITE), 2 (ORDWR), and 3 (OEXEC) mean read access,
180 *   write access, read and write access, and execute access, to be
181 *   checked against the per- missions for the file. In addition, if
182 *   mode has the OTRUNC (0x10) bit set, the file is to be truncated
183 *   [...]
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 */
192
193/**
194 * Valid values for the type field of a 9P message. This has been copied
195 * from the Plan 9 sourcetree, the file can be found at the location
196 * `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;
229
230/**
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;
238
239/**
240 * Struct representing a qid.
241 *
242 *  From intro(5):
243 *   The thirteen-byte qid fields hold a one-byte type, specifying
244 *   whether the file is a directory, append-only file, etc., and two
245 *   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;
255
256	/**
257	 * From intro(5):
258	 *   The version is a version number for a file; typically, it
259	 *   is incremented every time the file is modified.
260	 */
261	uint32_t vers;
262
263	/**
264	 * From intro(5):
265	 *   The path is an integer unique among all files in the
266	 *   hierarchy. If a file is deleted and recreated with the same
267	 *   name in the same directory, the old and new path components
268	 *   of the qids should be different.
269	 */
270	uint64_t path;
271} _9pqid;
272
273/**
274 * Struct representing an open fid.
275 *
276 * From intro(5):
277 *   Most T-messages contain a fid, a 32-bit unsigned integer that the
278 *   client uses to identify a ``current file'' on the server. Fids are
279 *   somewhat like file descriptors in a user process, [...] all files
280 *   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;
287
288	/**
289	 * The qid associated with this fid.
290	 */
291	_9pqid qid;
292
293	/**
294	 * Offset in file associated with this fid.
295	 */
296	uint64_t off;
297
298	/**
299	 * iounit for this file as returned by open(5).
300	 */
301	uint32_t iounit;
302} _9pfid;
303
304/**
305 * Struct representing a 9P package. For the sake of simplicity it only
306 * has length, tag and type fields since those are the only fields
307 * present in all 9P messages. Message specific parameters have to be
308 * 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-endian
314	 * byte order.
315	 */
316	uint8_t *buf;
317
318	/**
319	 * Length of the message specific parameters. The length of the
320	 * total message as specified by intro(5) can be calculated by
321	 * adding the length of the header (7 bytes) to the value of
322	 * this field.
323	 */
324	uint32_t len;
325
326	/**
327	 * Type of the message.
328	 */
329	_9ptype type; /* XXX Use uint8_t instead? */
330
331	/**
332	 * Unique message tag of this particular message.
333	 */
334	uint16_t tag;
335} _9ppkt;
336
337/**
338 * Function used for receiving data that should be parsed as a 9P
339 * 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);
348
349/**
350 * Connection context for a 9P connection.
351 */
352typedef struct {
353	/**
354	 * Global static buffer used for storing message specific
355	 * parameters.  Message indepented parameters are stored on the
356	 * stack using `_9ppkt`.
357	 */
358	uint8_t buffer[_9P_MSIZE];
359
360	/**
361	 * Function used to receive R-messages.
362	 */
363	iofunc read;
364
365	/**
366	 * Function used to send T-messages.
367	 */
368	iofunc write;
369
370	/**
371	 * From version(5):
372	 *   The client suggests a maximum message size, msize, that is
373	 *   the maximum length, in bytes, it will ever generate or
374	 *   expect to receive in a single 9P message.
375	 *
376	 * The msize we are suggestion to the server is defined by the
377	 * macro ::_9P_MSIZE for the unlikly event that the server
378	 * choosen an msize smaller than the one we are suggesting we
379	 * are storing the msize actually used for the communication in
380	 * this variable.
381	 *
382	 * It is declared with the initial value ::_9P_MSIZE to make it
383	 * possible to use this variable as an argument to the read
384	 * function even before a session is established.
385	 */
386	uint32_t msize;
387
388	/**
389	 * As with file descriptors, we need to store currently open
390	 * fids somehow. This is done in this static buffer. This buffer
391	 * should only be accessed using the ::fidtbl function it should
392	 * never be modified directly.
393	 *
394	 * This buffer is not static because it is used in the ::fidtbl
395	 * function from `util.c`.
396	 */
397	_9pfid fids[_9P_MAXFIDS];
398} _9pctx;
399
400void _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*);
411
412#endif