ninenano

Client implementation of the 9P protocol for constrained devices

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

  1#include <assert.h>
  2#include <errno.h>
  3#include <string.h>
  4#include <fcntl.h>
  5#include <unistd.h>
  6#include <inttypes.h>
  7
  8#include <sys/stat.h>
  9#include <sys/types.h>
 10
 11#include "9p.h"
 12#include "9util.h"
 13
 14#define ENABLE_DEBUG (0)
 15#include "debug.h"
 16
 17/**
 18 * @defgroup _9putil Static utility functions.
 19 *
 20 * @{
 21 */
 22
 23/**
 24 * Initializes the members of a packet buffer. The length field is set
 25 * to the amount of bytes still available in the buffer and should be
 26 * decremented when writing to the buffer.
 27 *
 28 * @param ctx 9P connection context.
 29 * @param pkt Pointer to a packet for which the members should be
 30 * 	initialized.
 31 * @param type Type which should be used for this packet.
 32 */
 33static void
 34newpkt(_9pctx *ctx, _9ppkt *pkt, _9ptype type)
 35{
 36	pkt->buf = ctx->buffer + _9P_HEADSIZ;
 37	pkt->len = ctx->msize - _9P_HEADSIZ;
 38	pkt->type = type;
 39}
 40
 41/**
 42 * Parses the header (the first 7 bytes) of a 9P message contained in
 43 * the given packet buffer. The result is written to the len, type and
 44 * tag members of the given packet buffer. Besides the position in the
 45 * packet buffer is advanced.
 46 *
 47 * @param pkt Packet buffer from which data should be read. Besides this
 48 *   packet buffer is also used to store the result.
 49 * @return `0` on success.
 50 * @return `-EBADMSG` if the buffer content isn't a valid 9P message.
 51 */
 52static int
 53_9pheader(_9ppkt *pkt)
 54{
 55	uint8_t type;
 56	uint32_t len;
 57
 58	/* From intro(5):
 59	 *   Each 9P message begins with a four-byte size field
 60	 *   specifying the length in bytes of the complete message
 61	 *   including the four bytes of the size field itself.
 62	 */
 63	if (pkt->len < BIT32SZ)
 64		return -EBADMSG;
 65	ptoh32(&len, pkt);
 66
 67	DEBUG("Length of the 9P message: %"PRIu32"\n", len);
 68	if (len > pkt->len + BIT32SZ || len < _9P_HEADSIZ)
 69		return -EBADMSG;
 70	pkt->len = len - BIT32SZ;
 71
 72	/* From intro(5):
 73	 *   The next byte is the message type, one of the constants in
 74	 *   the enumeration in the include file <fcall.h>.
 75	 */
 76	ptoh8(&type, pkt);
 77
 78	DEBUG("Type of 9P message: %"PRIu8"\n", type);
 79	if (type < Tversion || type >= Tmax)
 80		return -EBADMSG;
 81	pkt->type = (_9ptype)type;
 82
 83	/* From intro(5):
 84	 *   The next two bytes are an identifying tag, described below.
 85	 */
 86	ptoh16(&pkt->tag, pkt);
 87	DEBUG("Tag of 9P message: %"PRIu16"\n", pkt->tag);
 88
 89	return 0;
 90}
 91
 92/**
 93 * Performs a 9P request, meaning this function sends a T-message to the
 94 * server, reads the R-message from the client and stores it in a memory
 95 * location provided by the caller.
 96 *
 97 * Keep in mind that the same ::_9ppkt is being used for the T- and
 98 * R-message, thus after calling this method you shouldn't can't access
 99 * the R-message values anymore.
100 *
101 * The length field of the given packet should be equal to the amount of
102 * space (in bytes) still available in the packet buffer.
103 *
104 * @param ctx 9P connection context.
105 * @param p Pointer to the memory location containing the T-message. The
106 *   caller doesn't need to initialize the `tag` field of this message.
107 *   Besides this pointer is also used to store the R-message send by
108 *   the server as a reply.
109 * @return `0` on success, on error a negative errno is returned.
110 */
111static int
112do9p(_9pctx *ctx, _9ppkt *p)
113{
114	ssize_t ret;
115	_9ptype ttype;
116	uint32_t reallen;
117	uint16_t ttag;
118
119	DEBUG("Sending message of type %d to server\n", p->type);
120
121	/* From version(5):
122	 *   The tag should be NOTAG (value (ushort)~0) for a version message.
123	 */
124	p->tag = (p->type == Tversion) ?
125		_9P_NOTAG : (uint16_t)randu32();
126
127	assert(ctx->msize > p->len);
128	reallen = ctx->msize - p->len;
129
130	p->buf = ctx->buffer; /* Reset buffer position. */
131	p->len = _9P_HEADSIZ;
132
133	/* Build the "header", meaning: size[4] type[1] tag[2]
134	 * Every 9P message needs those first 7 bytes. */
135	htop32(reallen, p);
136	htop8(p->type, p);
137	htop16(p->tag, p);
138
139	DEBUG("Sending %"PRIu32" bytes to server...\n", reallen);
140	if ((ret = ctx->write(ctx->buffer, reallen)) < 0)
141		return (int)ret;
142
143	/* Values will be overwritten by _9pheader. */
144	ttype = p->type;
145	ttag = p->tag;
146
147	DEBUG("Reading from server...\n");
148	if ((ret = ctx->read(ctx->buffer, ctx->msize)) < 0)
149		return (int)ret;
150
151	/* Maximum length of a 9P message is 2^32. */
152	if ((size_t)ret > UINT32_MAX)
153		return -EMSGSIZE;
154
155	p->len = (uint32_t)ret;
156	p->buf = ctx->buffer;
157
158	DEBUG("Read %zu bytes from server, parsing them...\n", ret);
159	if ((ret = _9pheader(p)))
160		return (int)ret;
161
162	if (p->tag != ttag) {
163		DEBUG("Tag mismatch (%"PRIu8" vs. %"PRIu8")\n", p->tag, ttag);
164		return -EBADMSG;
165	}
166
167	if (p->type != ttype + 1) {
168		DEBUG("Unexpected value in type field: %d\n", p->type);
169		return -EBADMSG;
170	}
171
172	return 0;
173}
174
175/**
176 * Frees all resources allocated for a given fid on both the client and
177 * the server. Optionally the file associated with the fid can also be
178 * removed from the server.
179 *
180 * @pre t == Tclunk || t == Tremove
181 *
182 * @param ctx 9P connection context.
183 * @param f Pointer to fid on which the operation should be performed.
184 * @param t Type of the operation which should be performed.
185 *
186 * @return `0` on success, on error a negative errno is returned.
187 */
188static int
189fidrem(_9pctx *ctx, _9pfid *f, _9ptype t)
190{
191	int r;
192	_9ppkt pkt;
193
194	assert(t == Tclunk || t == Tremove);
195
196	/* From intro(5):
197	 *   size[4] Tclunk|Tremove tag[2] fid[4]
198	 */
199	newpkt(ctx, &pkt, t);
200	htop32(f->fid, &pkt);
201
202	if ((r = do9p(ctx, &pkt)))
203		return r;
204
205	/* From intro(5):
206	 *   size[4] Rclunk|Rremove tag[2]
207	 *
208	 * These first seven bytes are already parsed by do9p.
209	 * Therefore we don't need to parse anything here.
210	 */
211
212	/* fid wasn't allocated by us so don't use `fid->fid = 0` here */
213	if (!fidtbl(ctx->fids, f->fid, DEL))
214		return -EBADF;
215
216	return 0;
217}
218
219/**
220 * Parses the body of a 9P Ropen or Rcreate message. The body of those
221 * two messages consists of a qid and an iounit. The values of both
222 * fields are stored in the given fid.
223 *
224 * @param ctx 9P connection context.
225 * @param f Pointer to the fid associated with the new file.
226 * @param pkt Pointer to the packet from which the body should be read.
227 * @return `0` on success, on error a negative errno is returned.
228 */
229static int
230newfile(_9pctx *ctx, _9pfid *f, _9ppkt *pkt)
231{
232	if (hqid(&f->qid, pkt) || pkt->len < BIT32SZ)
233		return -EBADMSG;
234	ptoh32(&f->iounit, pkt);
235
236	/* From open(5):
237	 *   The iounit field returned by open and create may be zero.
238	 *   If it is not, it is the maximum number of bytes that are
239	 *   guaranteed to be read from or written to the file without
240	 *   breaking the I/O transfer into multiple 9P messages
241	 */
242	if (!f->iounit)
243		f->iounit = ctx->msize - _9P_IOHDRSIZ;
244
245	f->off = 0;
246	return 0;
247}
248
249/**
250 * Only a maximum of bytes can be transfered atomically. If the amonut
251 * of bytes we want to write to a file (or read from it) exceed this
252 * limit we need to send multiple R-messages to the server. This
253 * function takes care of doing this.
254 *
255 * @pre t == Twrite || t == Tread
256 *
257 * @param ctx 9P connection context.
258 * @param f Pointer to fid on which a read or write operation should be
259 * 	performed.
260 * @param buf Pointer to a buffer from which data should be written to
261 * 	or written from.
262 * @param count Amount of bytes that should be written or read from the
263 * 	file.
264 * @param t Type of the operation which should be performed.
265 */
266static ssize_t
267ioloop(_9pctx *ctx, _9pfid *f, char *buf, size_t count, _9ptype t)
268{
269	int r;
270	size_t n;
271	uint32_t pcnt, ocnt;
272	_9ppkt pkt;
273
274	assert(t == Twrite || t == Tread);
275
276	n = 0;
277	while (n < count) {
278		/* From intro(5):
279		 *   size[4] Tread tag[2] fid[4] offset[8] count[4]
280		 *   size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
281		 */
282		newpkt(ctx, &pkt, t);
283		htop32(f->fid, &pkt);
284		htop64(f->off, &pkt);
285
286		assert(n <= count);
287		if (count - n <= UINT32_MAX)
288			pcnt = (uint32_t)(count - n);
289		else
290			pcnt = UINT32_MAX;
291
292		if (pcnt > f->iounit)
293			pcnt = f->iounit;
294		htop32(pcnt, &pkt);
295
296		if (t == Twrite) {
297			if (pcnt > pkt.len - BIT32SZ)
298				pcnt = pkt.len - BIT32SZ;
299			if (!pcnt) return -EOVERFLOW;
300			bufcpy(&pkt, &buf[n], pcnt);
301		}
302
303		DEBUG("Sending %s with offset %"PRIu64" and count %"PRIu32"\n",
304			(t == Tread) ? "Tread" : "Twrite", f->off, pcnt);
305
306		if ((r = do9p(ctx, &pkt)))
307			return r;
308
309		/* From intro(5):
310		 *   size[4] Rread tag[2] count[4] data[count]
311		 *   size[4] Rwrite tag[2] count[4]
312		 */
313		ocnt = pcnt;
314		if (pkt.len < BIT32SZ)
315			return -EBADMSG;
316		ptoh32(&pcnt, &pkt);
317
318		/* From open(5):
319		 *   If the offset field is greater than or equal to the
320		 *   number of bytes in the file, a count of zero will
321		 *   be returned.
322		 */
323		if (!pcnt)
324			return 0; /* EOF */
325
326		if (pcnt > count)
327			return -EBADMSG;
328
329		if (t == Tread) {
330			if (pkt.len < pcnt)
331				return -EBADMSG;
332			memcpy(&buf[n], pkt.buf, pcnt);
333		}
334
335		n += pcnt;
336		f->off += pcnt;
337
338		if (pcnt < ocnt)
339			break;
340	}
341
342	return (ssize_t)n;
343}
344
345/**@}*/
346
347/**
348 * Initializes a 9P connection context.
349 *
350 * @param read Function used for receiving data from the server.
351 * @param write Function used for sending data to the server.
352 * @param ctx 9P connection context which should be initialized.
353 */
354void
355_9pinit(_9pctx *ctx, iofunc read, iofunc write)
356{
357	initrand();
358	memset(ctx->fids, 0, _9P_MAXFIDS * sizeof(_9pfid));
359
360	ctx->msize = _9P_MSIZE;
361	ctx->write = write;
362	ctx->read = read;
363}
364
365/**
366 * From version(5):
367 *   The version request negotiates the protocol version and message
368 *   size to be used on the connection and initializes the connection
369 *   for I/O. Tversion must be the first message sent on the 9P
370 *   connection, and the client cannot issue any further requests until
371 *   it has received the Rversion reply.
372 *
373 * The version parameter is always set to the value of `_9P_VERSION`,
374 * the msize parameter on the other hand is always set to the value of
375 * `_9P_MSIZE`. The msize choosen by the server is stored in the msize
376 * member of the given connection context.
377 *
378 * @param ctx 9P connection context.
379 *
380 * @return `0` on success.
381 * @return `-EBADMSG` if the 9P message was invalid.
382 * @return `-EMSGSIZE` if the server msize was greater than `_9P_MSIZE`.
383 * @return `-ENOPROTOOPT` server implements a different version of the
384 *   9P network protocol.
385 */
386int
387_9pversion(_9pctx *ctx)
388{
389	int r;
390	char ver[_9P_VERLEN];
391	_9ppkt pkt;
392
393	/* From intro(5):
394	 *   size[4] Tversion tag[2] msize[4] version[s]
395	 */
396	newpkt(ctx, &pkt, Tversion);
397	htop32(_9P_MSIZE, &pkt);
398	if (pstring(_9P_VERSION, &pkt))
399		return -EOVERFLOW;
400
401	if ((r = do9p(ctx, &pkt)))
402		return r;
403
404	/* From intro(5):
405	 *   size[4] Rversion tag[2] msize[4] version[s]
406	 *
407	 * Also according to version(5) the version field in the
408	 * R-message must be a string of the form `9Pnnnn` thus it has
409	 * to be at least 4 bytes long plus 2 bytes for the size tag and
410	 * 4 bytes for the msize field.
411	 */
412	if (pkt.len <= 10)
413		return -EBADMSG;
414	ptoh32(&ctx->msize, &pkt);
415
416	DEBUG("Msize of Rversion message: %"PRIu32"\n", ctx->msize);
417
418	/* From version(5):
419	 *   The server responds with its own maximum, msize, which must
420	 *   be less than or equal to the client's value.
421	 */
422	if (ctx->msize > _9P_MSIZE) {
423		DEBUG("Servers msize is too large (%"PRIu32")\n", ctx->msize);
424		return -EMSGSIZE;
425	} else if (ctx->msize < _9P_MINSIZE) {
426		DEBUG("Servers msize is too small (%"PRIu32")\n", ctx->msize);
427		return -EOVERFLOW;
428	}
429
430	if (hstring(ver, _9P_VERLEN, &pkt))
431		return -EBADMSG;
432	DEBUG("Version string reported by server: %s\n", ver);
433
434	/* From version(5):
435	 *   If the server does not understand the client's version
436	 *   string, it should respond with an Rversion message (not
437	 *   Rerror) with the version string the 7 characters `unknown`.
438         */
439	if (!strcmp(ver, "unknown"))
440		return -ENOPROTOOPT;
441
442	return 0;
443}
444
445/**
446 * From attach(5):
447 *   The attach message serves as a fresh introduction from a user on
448 *   the client machine to the server. As a result of the attach
449 *   transaction, the client will have a connection to the root
450 *   directory of the desired file tree, represented by fid.
451 *
452 * The afid parameter is always set to the value of `_9P_NOFID` since
453 * authentication is not supported currently.
454 *
455 * @param ctx 9P connection context.
456 * @param dest Pointer to a pointer which should be set to the address
457 *   of the corresponding entry in the fid table.
458 * @param uname User identification.
459 * @param aname File tree to access.
460 * @return `0` on success, on error a negative errno is returned.
461 */
462int
463_9pattach(_9pctx *ctx, _9pfid **dest, char *uname, char *aname)
464{
465	int r;
466	_9pfid *fid;
467	_9ppkt pkt;
468
469	/* From intro(5):
470	 *   size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
471	 */
472	newpkt(ctx, &pkt, Tattach);
473	htop32(_9P_ROOTFID, &pkt);
474	htop32(_9P_NOFID, &pkt);
475	if (pstring(uname, &pkt) || pstring(aname, &pkt))
476		return -EOVERFLOW;
477
478	if ((r = do9p(ctx, &pkt)))
479		return r;
480
481	/* From intro(5):
482	 *   size[4] Rattach tag[2] qid[13]
483	 */
484	if (!(fid = fidtbl(ctx->fids, _9P_ROOTFID, ADD)))
485		return -ENFILE;
486	fid->fid = _9P_ROOTFID;
487
488	if (hqid(&fid->qid, &pkt)) {
489		fid->fid = 0; /* mark fid as free. */
490		return -EBADMSG;
491	}
492
493	*dest = fid;
494	return 0;
495}
496
497/**
498 * From clunk(5):
499 *   The clunk request informs the file server that the current file
500 *   represented by fid is no longer needed by the client. The actual
501 *   file is not removed on the server unless the fid had been opened
502 *   with ORCLOSE.
503 *
504 * @param ctx 9P connection context.
505 * @param f Pointer to a fid which should be closed.
506 * @return `0` on success, on error a negative errno is returned.
507 */
508int
509_9pclunk(_9pctx *ctx, _9pfid *f)
510{
511	return fidrem(ctx, f, Tclunk);
512}
513
514/**
515 * From intro(5):
516 *   The stat transaction retrieves information about the file. The stat
517 *   field in the reply includes the file's name, access permissions
518 *   (read, write and execute for owner, group and public), access and
519 *   modification times, and owner and group identifications (see
520 *   stat(2)).
521 *
522 * Retrieves information about the file associated with the given fid.
523 *
524 * @param ctx 9P connection context.
525 * @param fid Fid of the file to retrieve information for.
526 * @param buf Pointer to stat struct to fill.
527 * @return `0` on success, on error a negative errno is returned.
528 */
529int
530_9pstat(_9pctx *ctx, _9pfid *fid, struct stat *buf)
531{
532	int r;
533	uint32_t mode;
534	_9ppkt pkt;
535
536	/* From intro(5):
537	 *   size[4] Tstat tag[2] fid[4]
538	 */
539	newpkt(ctx, &pkt, Tstat);
540	htop32(fid->fid, &pkt);
541
542	if ((r = do9p(ctx, &pkt)))
543		return r;
544
545	/* From intro(5):
546	 *   size[4] Rstat tag[2] stat[n]
547	 *
548	 * See stat(5) for the definition of stat[n].
549	 */
550	if (pkt.len < _9P_MINSTSIZ)
551		return -EBADMSG;
552
553	/* Skip: n[2], size[2], type[2] and dev[4]. */
554	advbuf(&pkt, 3 * BIT16SZ + BIT32SZ);
555
556	/* store qid informations in given fid. */
557	if (hqid(&fid->qid, &pkt))
558		return -EBADMSG;
559
560	/* store the other information in the stat struct. */
561	ptoh32(&mode, &pkt);
562	buf->st_mode = (mode & DMDIR) ? S_IFDIR : S_IFREG;
563	ptoh32((uint32_t*)&buf->st_atime, &pkt);
564	ptoh32((uint32_t*)&buf->st_mtime, &pkt);
565	buf->st_ctime = buf->st_mtime;
566	ptoh64((uint64_t*)&buf->st_size, &pkt);
567
568	/* information for stat struct we cannot extract from the reply. */
569	buf->st_dev = buf->st_ino = buf->st_rdev = 0;
570	buf->st_nlink = 1;
571	buf->st_uid = buf->st_gid = 0;
572	buf->st_blksize = ctx->msize - _9P_IOHDRSIZ;
573	buf->st_blocks = buf->st_size / buf->st_blksize + 1;
574
575	/* name, uid, gid and muid are ignored. */
576
577	return 0;
578}
579
580/* TODO insert _9pwstat implementation here. */
581
582/**
583 * From intro(5):
584 *   A walk message causes the server to change the current file
585 *   associated with a fid to be a file in the directory that is the old
586 *   current file, or one of its subdirectories. Walk returns a new fid
587 *   that refers to the resulting file. Usually, a client maintains a
588 *   fid for the root, and navigates by walks from the root fid.
589 *
590 * This function alsways walks frmom the root fid and returns a fid for
591 * the last element of the given path.
592 *
593 * @attention The 9P protocol specifies that only a a maximum of sixteen
594 * name elements or qids may be packed in a single message.  For name
595 * elements or qids that exceeds this limit multiple Twalk/Rwalk message
596 * need to be send. Since `VFS_NAME_MAX` defaults to `31` it is very
597 * unlikely that this limit will be reached. Therefore this function
598 * doesn't support sending walk messages that exceed this limit.
599 *
600 * @param ctx 9P connection context.
601 * @param dest Pointer to a pointer which should be set to the address
602 *   of the corresponding entry in the fid table.
603 * @param path Path which should be walked.
604 * @return `0` on success, on error a negative errno is returned.
605 */
606int
607_9pwalk(_9pctx *ctx, _9pfid **dest, char *path)
608{
609	int r;
610	char *cur, *sep;
611	size_t n, i, len, elen;
612	uint8_t *nwname;
613	uint16_t nwqid;
614	_9pfid *fid;
615	_9ppkt pkt;
616
617	if (*path == '\0' || !strcmp(path, "/"))
618		len = 0;
619	else
620		len = strlen(path);
621
622	if (!(fid = newfid(ctx->fids)))
623		return -ENFILE;
624
625	/* From intro(5):
626	 *   size[4] Twalk tag[2] fid[4] newfid[4] nwname[2]
627	 *   nwname*(wname[s])
628	 */
629	newpkt(ctx, &pkt, Twalk);
630	htop32(_9P_ROOTFID, &pkt);
631	htop32(fid->fid, &pkt);
632
633	/* leave space for nwname[2]. */
634	nwname = pkt.buf;
635	advbuf(&pkt, BIT16SZ);
636
637	/* generate nwname*(wname[s]) */
638	for (n = i = 0; i < len; n++, i += elen + 1) {
639		if (n >= _9P_MAXWEL) {
640			r = -ENAMETOOLONG;
641			goto err;
642		}
643
644		cur = &path[i];
645		if (!(sep = strchr(cur, _9P_PATHSEP)))
646			sep = &path[len];
647
648		assert(sep - cur >= 0);
649		elen = (size_t)(sep - cur);
650
651		if (pnstring(cur, elen, &pkt)) {
652			r = -EOVERFLOW;
653			goto err;
654		}
655	}
656
657	DEBUG("Constructed Twalk with %zu elements\n", n);
658
659	pkt.buf = nwname;
660	pkt.len += BIT16SZ;
661	assert(n <= UINT16_MAX);
662	htop16((uint16_t)n, &pkt);
663
664	if ((r = do9p(ctx, &pkt)))
665		goto err;
666
667	/* From intro(5):
668	 *   size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
669	 */
670	if (pkt.len < BIT16SZ) {
671		r = -EBADMSG;
672		goto err;
673	}
674	ptoh16(&nwqid, &pkt);
675
676	/**
677	 * From walk(5):
678	 *   nwqid is therefore either nwname or the index of the first
679	 *   elementwise walk that failed.
680	 */
681	DEBUG("nwqid: %"PRIu16"\n", nwqid);
682	if (nwqid != n || nwqid > pkt.len || nwqid > _9P_MAXWEL) { /* XXX */
683		r = -EBADMSG;
684		goto err;
685	}
686
687	/* Skip to the last qid. */
688	if (nwqid) {
689		advbuf(&pkt, (nwqid - 1) * _9P_QIDSIZ);
690		if (hqid(&fid->qid, &pkt)) {
691			r = -EBADMSG;
692			goto err;
693		}
694	}
695
696	*dest = fid;
697	return 0;
698
699err:
700	fid->fid = 0; /* mark fid as free. */
701	return r;
702}
703
704/**
705 * From open(5):
706 *   The open request asks the file server to check permissions and
707 *   prepare a fid for I/O with subsequent read and write messages.
708 *
709 * @param ctx 9P connection context.
710 * @param f Fid which should be opened for I/O.
711 * @param flags Flags used for opening the fid. Supported flags are:
712 * 	OREAD, OWRITE, ORDWR and OTRUNC.
713 * @return `0` on success, on error a negative errno is returned.
714 */
715int
716_9popen(_9pctx *ctx, _9pfid *f, uint8_t flags)
717{
718	int r;
719	_9ppkt pkt;
720
721	/* From intro(5):
722	 *   size[4] Topen tag[2] fid[4] mode[1]
723	 */
724	newpkt(ctx, &pkt, Topen);
725	htop32(f->fid, &pkt);
726	htop8(flags, &pkt);
727
728	if ((r = do9p(ctx, &pkt)))
729		return r;
730
731	/* From intro(5):
732	 *   size[4] Ropen tag[2] qid[13] iounit[4]
733	 */
734	if ((r = newfile(ctx, f, &pkt)))
735		return r;
736
737	return 0;
738}
739
740/**
741 * From open(5):
742 *   The create request asks the file server to create a new file with the name
743 *   supplied, in the directory (dir) represented by fid, and requires write
744 *   permission in the directory
745 *
746 * After creation the file will be opened automatically. And the given
747 * fid will no longer represent the directory, instead it now represents
748 * the newly created file.
749 *
750 * @param ctx 9P connection context.
751 * @param f Pointer to the fid associated with the directory in which a
752 * 	new file should be created.
753 * @param name Name which should be used for the newly created file.
754 * @param perm Permissions with which the new file should be created.
755 * @param flags Flags which should be used for opening the file
756 *   afterwards. See ::_9popen.
757 */
758int
759_9pcreate(_9pctx *ctx, _9pfid *f, char *name, uint32_t perm, uint8_t flags)
760{
761	int r;
762	_9ppkt pkt;
763
764	/* From intro(5):
765	 *   size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
766	 */
767	newpkt(ctx, &pkt, Tcreate);
768	htop32(f->fid, &pkt);
769	if (pstring(name, &pkt) || BIT32SZ + BIT8SZ > pkt.len)
770		return -EOVERFLOW;
771	htop32(perm, &pkt);
772	htop8(flags, &pkt);
773
774	if ((r = do9p(ctx, &pkt)))
775		return r;
776
777	/* From intro(5):
778	 *   size[4] Rcreate tag[2] qid[13] iounit[4]
779	 */
780	if ((r = newfile(ctx, f, &pkt)))
781		return r;
782
783	return 0;
784}
785
786/**
787 * From read(5):
788 *   The read request asks for count bytes of data from the file
789 *   identified by fid, which must be opened for reading, start-
790 *   ing offset bytes after the beginning of the file.
791 *
792 * @param ctx 9P connection context.
793 * @param f Fid from which data should be read.
794 * @param dest Pointer to a buffer to which the received data should be
795 * 	written.
796 * @param count Amount of data that should be read.
797 * @return The number of bytes read on success or a negative errno on
798 * 	error.
799 */
800ssize_t
801_9pread(_9pctx *ctx, _9pfid *f, char *dest, size_t count)
802{
803	return ioloop(ctx, f, dest, count, Tread);
804}
805
806/**
807 * From intro(5):
808 *   The write request asks that count bytes of data be recorded in the
809 *   file identified by fid, which must be opened for writing, starting
810 *   offset bytes after the beginning of the file.
811 *
812 * @param ctx 9P connection context.
813 * @param f Pointer to the fid to which data should be written.
814 * @param src Pointer to a buffer containing the data which should be
815 * 	written to the file.
816 * @param count Amount of bytes which should be written to the file
817 * @return The number of bytes read on success or a negative errno on
818 * 	error.
819 */
820ssize_t
821_9pwrite(_9pctx *ctx, _9pfid *f, char *src, size_t count)
822{
823	return ioloop(ctx, f, src, count, Twrite);
824}
825
826/**
827 * From remove(5):
828 *   The remove request asks the file server both to remove the file
829 *   represented by fid and to clunk the fid, even if the remove fails.
830 *
831 * @param ctx 9P connection context.
832 * @param f Pointer to the fid which should be removed.
833 * @return `0` on success, on error a negative errno is returned.
834 */
835int
836_9premove(_9pctx *ctx, _9pfid *f)
837{
838	return fidrem(ctx, f, Tremove);
839}