History

Squashed 'c9/' content from commit 24bfa2e

Sigrid Haflínudóttir committed 6 years ago

Commit c19685cNo parent (first commit)4 files · +1518 −0Browse files

Unified · Split

4 files
  1. LICENSE +1 −0
  2. README.md +8 −0
  3. c9.c +1159 −0
  4. c9.h +350 −0
LICENSE+1−0
@@ -0,0 +1 @@
1+Public domain.
README.md+8−0
@@ -0,0 +1,8 @@
1+# c9
2+
3+Low level 9p client and server.
4+
5+## Examples
6+
7+Until I have time to write a minimal example you could take a look at
8+https://github.com/ftrvxmtrx/dawfs/blob/master/c9net.c
c9.c+1159−0
@@ -0,0 +1,1159 @@
1+/*
2+ * This is 9p client and server implementation which aims to be
3+ * correct, small and secure. It's the lowest level implementation.
4+ * It doesn't have much comments, mostly because it doesn't make
5+ * any sense to copy-paste protocol documentation, which
6+ * you can read at http://man.cat-v.org/plan_9/5/, see 'intro'.
7+ */
8+#include <string.h>
9+#include <stdint.h>
10+#include "c9.h"
11+
12+enum
13+{
14+ Svver = 1<<0,
15+};
16+
17+#define safestrlen(s) (s == NULL ? 0 : (uint32_t)strlen(s))
18+#define maxread(c) (c->msize-4-4-1-2)
19+#define maxwrite(c) maxread(c)
20+
21+static void
22+w08(uint8_t **p, uint8_t x)
23+{
24+ (*p)[0] = x;
25+ *p += 1;
26+}
27+
28+static void
29+w16(uint8_t **p, uint16_t x)
30+{
31+ (*p)[0] = x;
32+ (*p)[1] = x>>8;
33+ *p += 2;
34+}
35+
36+static void
37+w32(uint8_t **p, uint32_t x)
38+{
39+ (*p)[0] = x;
40+ (*p)[1] = x>>8;
41+ (*p)[2] = x>>16;
42+ (*p)[3] = x>>24;
43+ *p += 4;
44+}
45+
46+static void
47+w64(uint8_t **p, uint64_t x)
48+{
49+ (*p)[0] = x;
50+ (*p)[1] = x>>8;
51+ (*p)[2] = x>>16;
52+ (*p)[3] = x>>24;
53+ (*p)[4] = x>>32;
54+ (*p)[5] = x>>40;
55+ (*p)[6] = x>>48;
56+ (*p)[7] = x>>56;
57+ *p += 8;
58+}
59+
60+static void
61+wcs(uint8_t **p, const char *s, int len)
62+{
63+ w16(p, len);
64+ if(s != NULL){
65+ memmove(*p, s, len);
66+ *p += len;
67+ }
68+}
69+
70+static uint8_t
71+r08(uint8_t **p)
72+{
73+ *p += 1;
74+ return (*p)[-1];
75+}
76+
77+static uint16_t
78+r16(uint8_t **p)
79+{
80+ *p += 2;
81+ return (uint16_t)(*p)[-2]<<0 | (uint16_t)(*p)[-1]<<8;
82+}
83+
84+static uint32_t
85+r32(uint8_t **p)
86+{
87+ return r16(p) | (uint32_t)r16(p)<<16;
88+}
89+
90+static uint64_t
91+r64(uint8_t **p)
92+{
93+ return r32(p) | (uint64_t)r32(p)<<32;
94+}
95+
96+static C9error
97+newtag(C9ctx *c, C9ttype type, C9tag *tag)
98+{
99+ uint32_t i;
100+
101+ if(type == Tversion){
102+ *tag = 0xffff;
103+ return 0;
104+ }
105+
106+ if(c->lowfreetag < C9maxtags){
107+ uint32_t d = c->lowfreetag / C9tagsbits, m = c->lowfreetag % C9tagsbits;
108+ if((c->tags[d] & 1<<m) != 0){
109+ c->tags[d] &= ~(1<<m);
110+ *tag = c->lowfreetag++;
111+ return 0;
112+ }
113+ }
114+
115+ for(i = 0; i < (int)sizeof(c->tags)/sizeof(c->tags[0]); i++){
116+ uint32_t x, j;
117+ if((x = c->tags[i]) == 0)
118+ continue;
119+ for(j = 0; j < C9tagsbits; j++){
120+ if((x & (1<<j)) != 0){
121+ c->tags[i] &= ~(1<<j);
122+ *tag = i*C9tagsbits + j;
123+ c->lowfreetag = *tag + 1;
124+ return 0;
125+ }
126+ }
127+ }
128+
129+ c->error("newtag: no free tags");
130+ return C9Etag;
131+}
132+
133+static int
134+freetag(C9ctx *c, C9tag tag)
135+{
136+ if(tag != 0xffff){
137+ uint32_t d = tag / C9tagsbits, m = tag % C9tagsbits;
138+ if(tag >= C9maxtags){
139+ c->error("freetag: invalid tag");
140+ return -1;
141+ }
142+ if((c->tags[d] & 1<<m) != 0){
143+ c->error("freetag: double free");
144+ return -1;
145+ }
146+ if(c->lowfreetag > tag)
147+ c->lowfreetag = tag;
148+ c->tags[d] |= 1<<m;
149+ }
150+ return 0;
151+}
152+
153+static uint8_t *
154+T(C9ctx *c, uint32_t size, C9ttype type, C9tag *tag, C9error *err)
155+{
156+ uint8_t *p = NULL;
157+
158+ if(size > c->msize-4-1-2){
159+ c->error("T: invalid size");
160+ *err = C9Esize;
161+ }else if((*err = newtag(c, type, tag)) == 0){
162+ size += 4+1+2;
163+ if((p = c->begin(c, size)) == NULL){
164+ c->error("T: no buffer");
165+ freetag(c, *tag);
166+ *err = C9Ebuf;
167+ }else{
168+ *err = 0;
169+ w32(&p, size);
170+ w08(&p, type);
171+ w16(&p, *tag);
172+ }
173+ }
174+ return p;
175+}
176+
177+static uint8_t *
178+R(C9ctx *c, uint32_t size, C9rtype type, C9tag tag, C9error *err)
179+{
180+ uint8_t *p = NULL;
181+
182+ if(size > c->msize-4-1-2){
183+ c->error("R: invalid size");
184+ *err = C9Esize;
185+ }else{
186+ size += 4+1+2;
187+ if((p = c->begin(c, size)) == NULL){
188+ c->error("R: no buffer");
189+ *err = C9Ebuf;
190+ }else{
191+ *err = 0;
192+ w32(&p, size);
193+ w08(&p, type);
194+ w16(&p, tag);
195+ }
196+ }
197+ return p;
198+}
199+
200+C9error
201+c9parsedir(C9ctx *c, C9stat *stat, uint8_t **t, uint32_t *size)
202+{
203+ uint8_t *b;
204+ uint32_t cnt, sz;
205+
206+ if(*size < 49 || (sz = r16(t)) < 47 || *size < 2+sz)
207+ goto error;
208+ *size -= 2+sz;
209+ *t += 6; /* skip type(2) and dev(4) */
210+ stat->qid.type = r08(t);
211+ stat->qid.version = r32(t);
212+ stat->qid.path = r64(t);
213+ stat->mode = r32(t);
214+ stat->atime = r32(t);
215+ stat->mtime = r32(t);
216+ stat->size = r64(t);
217+ sz -= 39;
218+ if((cnt = r16(t)) > sz-2)
219+ goto error;
220+ stat->name = (char*)*t; b = *t = *t+cnt; sz -= 2+cnt;
221+ if(sz < 2 || (cnt = r16(t)) > sz-2)
222+ goto error;
223+ stat->uid = (char*)*t; *b = 0; b = *t = *t+cnt; sz -= 2+cnt;
224+ if(sz < 2 || (cnt = r16(t)) > sz-2)
225+ goto error;
226+ stat->gid = (char*)*t; *b = 0; b = *t = *t+cnt; sz -= 2+cnt;
227+ if(sz < 2 || (cnt = r16(t)) > sz-2)
228+ goto error;
229+ stat->muid = memmove(*t-1, *t, cnt); *b = stat->muid[cnt] = 0; *t = *t+cnt; sz -= 2+cnt;
230+ *t += sz;
231+ return 0;
232+error:
233+ c->error("c9parsedir: invalid size");
234+ return C9Epkt;
235+}
236+
237+C9error
238+c9version(C9ctx *c, C9tag *tag, uint32_t msize)
239+{
240+ uint8_t *b;
241+ C9error err;
242+
243+ if(msize < C9minmsize){
244+ c->error("c9version: msize too small");
245+ return C9Einit;
246+ }
247+ memset(c->tags, 0xff, sizeof(c->tags));
248+ memset(c->flush, 0xff, sizeof(c->flush));
249+ c->lowfreetag = 0;
250+ c->msize = msize;
251+
252+ if((b = T(c, 4+2+6, Tversion, tag, &err)) != NULL){
253+ w32(&b, msize);
254+ wcs(&b, "9P2000", 6);
255+ err = c->end(c);
256+ }
257+ return err;
258+}
259+
260+C9error
261+c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname)
262+{
263+ uint8_t *b;
264+ uint32_t ulen = safestrlen(uname), alen = safestrlen(aname);
265+ C9error err;
266+
267+ if(ulen > C9maxstr || alen > C9maxstr){
268+ c->error("c9auth: string too long");
269+ return C9Estr;
270+ }
271+ if((b = T(c, 4+2+ulen+2+alen, Tauth, tag, &err)) != NULL){
272+ w32(&b, afid);
273+ wcs(&b, uname, ulen);
274+ wcs(&b, aname, alen);
275+ err = c->end(c);
276+ }
277+ return err;
278+}
279+
280+C9error
281+c9flush(C9ctx *c, C9tag *tag, C9tag oldtag)
282+{
283+ uint8_t *b;
284+ C9error err;
285+ int i;
286+
287+ for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++);
288+ if(i == C9maxflush){
289+ c->error("c9flush: no free flush slots");
290+ return C9Eflush;
291+ }
292+ if((b = T(c, 2, Tflush, tag, &err)) != NULL){
293+ w16(&b, oldtag);
294+ err = c->end(c);
295+ if(err == 0)
296+ c->flush[i] = (uint32_t)oldtag<<16 | *tag;
297+ }
298+ return err;
299+}
300+
301+C9error
302+c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const char *aname)
303+{
304+ uint32_t ulen = safestrlen(uname), alen = safestrlen(aname);
305+ uint8_t *b;
306+ C9error err;
307+
308+ if(ulen > C9maxstr || alen > C9maxstr){
309+ c->error("c9attach: string too long");
310+ return C9Estr;
311+ }
312+ if((b = T(c, 4+4+2+ulen+2+alen, Tattach, tag, &err)) != NULL){
313+ w32(&b, fid);
314+ w32(&b, afid);
315+ wcs(&b, uname, ulen);
316+ wcs(&b, aname, alen);
317+ err = c->end(c);
318+ }
319+ return err;
320+}
321+
322+C9error
323+c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[])
324+{
325+ uint32_t i, j, sz;
326+ uint32_t len[C9maxpathel];
327+ uint8_t *b;
328+ C9error err;
329+
330+ for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){
331+ len[i] = safestrlen(path[i]);
332+ if(len[i] == 0 || len[i] > C9maxstr){
333+ c->error("c9walk: path element too long");
334+ return C9Epath;
335+ }
336+ sz += 2 + len[i];
337+ }
338+ if(path[i] != NULL || i == 0){
339+ c->error("c9walk: invalid elements !(0 < %d <= %d)", i, C9maxpathel);
340+ return C9Epath;
341+ }
342+
343+ if((b = T(c, 4+4+2+sz, Twalk, tag, &err)) != NULL){
344+ w32(&b, fid);
345+ w32(&b, newfid);
346+ w16(&b, i);
347+ for(j = 0; j < i; j++)
348+ wcs(&b, path[j], len[j]);
349+ err = c->end(c);
350+ }
351+ return err;
352+}
353+
354+C9error
355+c9open(C9ctx *c, C9tag *tag, C9fid fid, C9mode mode)
356+{
357+ uint8_t *b;
358+ C9error err;
359+
360+ if((b = T(c, 4+1, Topen, tag, &err)) != NULL){
361+ w32(&b, fid);
362+ w08(&b, mode);
363+ err = c->end(c);
364+ }
365+ return err;
366+}
367+
368+C9error
369+c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mode mode)
370+{
371+ uint32_t nlen = strlen(name);
372+ uint8_t *b;
373+ C9error err;
374+
375+ if(nlen == 0 || nlen > C9maxstr){
376+ c->error("c9create: invalid name");
377+ return C9Epath;
378+ }
379+ if((b = T(c, 4+2+nlen+4+1, Tcreate, tag, &err)) != NULL){
380+ w32(&b, fid);
381+ wcs(&b, name, nlen);
382+ w32(&b, perm);
383+ w08(&b, mode);
384+ err = c->end(c);
385+ }
386+ return err;
387+}
388+
389+C9error
390+c9read(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, uint32_t count)
391+{
392+ uint8_t *b;
393+ C9error err;
394+
395+ if((b = T(c, 4+8+4, Tread, tag, &err)) != NULL){
396+ w32(&b, fid);
397+ w64(&b, offset);
398+ w32(&b, count);
399+ err = c->end(c);
400+ }
401+ return err;
402+}
403+
404+C9error
405+c9write(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, const void *in, uint32_t count)
406+{
407+ uint8_t *b;
408+ C9error err;
409+
410+ if((b = T(c, 4+8+4+count, Twrite, tag, &err)) != NULL){
411+ w32(&b, fid);
412+ w64(&b, offset);
413+ w32(&b, count);
414+ memmove(b, in, count);
415+ err = c->end(c);
416+ }
417+ return err;
418+}
419+
420+C9error
421+c9wrstr(C9ctx *c, C9tag *tag, C9fid fid, const char *s)
422+{
423+ return c9write(c, tag, fid, 0, s, strlen(s));
424+}
425+
426+C9error
427+c9clunk(C9ctx *c, C9tag *tag, C9fid fid)
428+{
429+ uint8_t *b;
430+ C9error err;
431+
432+ if((b = T(c, 4, Tclunk, tag, &err)) != NULL){
433+ w32(&b, fid);
434+ err = c->end(c);
435+ }
436+ return err;
437+}
438+
439+C9error
440+c9remove(C9ctx *c, C9tag *tag, C9fid fid)
441+{
442+ uint8_t *b;
443+ C9error err;
444+
445+ if((b = T(c, 4, Tremove, tag, &err)) != NULL){
446+ w32(&b, fid);
447+ err = c->end(c);
448+ }
449+ return err;
450+}
451+
452+C9error
453+c9stat(C9ctx *c, C9tag *tag, C9fid fid)
454+{
455+ uint8_t *b;
456+ C9error err;
457+
458+ if((b = T(c, 4, Tstat, tag, &err)) != NULL){
459+ w32(&b, fid);
460+ err = c->end(c);
461+ }
462+ return err;
463+}
464+
465+C9error
466+c9wstat(C9ctx *c, C9tag *tag, C9fid fid, const C9stat *s)
467+{
468+ uint32_t nlen = safestrlen(s->name), ulen = safestrlen(s->uid), glen = safestrlen(s->gid);
469+ uint32_t unusedsz = 2+4+13, statsz = unusedsz+4+4+4+8+2+nlen+2+ulen+2+glen+2;
470+ uint8_t *b;
471+ C9error err;
472+
473+ if(nlen == 0 || nlen > C9maxstr){
474+ c->error("c9wstat: invalid name");
475+ return C9Epath;
476+ }
477+ if(ulen > C9maxstr || glen > C9maxstr){
478+ c->error("c9wstat: string too long");
479+ return C9Estr;
480+ }
481+ if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){
482+ w32(&b, fid);
483+ w16(&b, statsz+2);
484+ w16(&b, statsz);
485+ memset(b, 0xff, unusedsz); /* leave type(2), dev(4) and qid(13) unchanged */
486+ b += unusedsz;
487+ w32(&b, s->mode);
488+ w32(&b, s->atime);
489+ w32(&b, s->mtime);
490+ w64(&b, s->size);
491+ wcs(&b, s->name, nlen);
492+ wcs(&b, s->uid, ulen);
493+ wcs(&b, s->gid, glen);
494+ wcs(&b, NULL, 0); /* muid unchanged */
495+ err = c->end(c);
496+ }
497+ return err;
498+}
499+
500+C9error
501+c9proc(C9ctx *c)
502+{
503+ uint32_t i, sz, cnt, msize;
504+ uint8_t *b;
505+ int err;
506+ C9r r;
507+
508+ err = -1;
509+ if((b = c->read(c, 4, &err)) == NULL){
510+ if(err != 0)
511+ c->error("c9proc: short read");
512+ return err == 0 ? 0 : C9Epkt;
513+ }
514+
515+ sz = r32(&b);
516+ if(sz < 7 || sz > c->msize){
517+ c->error("c9proc: invalid packet size !(7 <= %u <= %u)", sz, c->msize);
518+ return C9Epkt;
519+ }
520+ sz -= 4;
521+ err = -1;
522+ if((b = c->read(c, sz, &err)) == NULL){
523+ if(err != 0)
524+ c->error("c9proc: short read");
525+ return err == 0 ? 0 : C9Epkt;
526+ }
527+
528+ r.type = r08(&b);
529+ r.tag = r16(&b);
530+ if(r.type != Rversion){
531+ if(r.tag >= C9maxtags){
532+ c->error("c9proc: invalid tag 0x%x", r.tag);
533+ return C9Epkt;
534+ }
535+ if(freetag(c, r.tag) != 0)
536+ return C9Etag;
537+ }
538+ sz -= 3;
539+ r.numqid = 0;
540+
541+ switch(r.type){
542+ case Rread:
543+ if(sz < 4 || (cnt = r32(&b)) > sz-4)
544+ goto error;
545+ r.read.data = b;
546+ r.read.size = cnt;
547+ c->r(c, &r);
548+ break;
549+
550+ case Rwrite:
551+ if(sz < 4 || (cnt = r32(&b)) > c->msize)
552+ goto error;
553+ r.write.size = cnt;
554+ c->r(c, &r);
555+ break;
556+
557+ case Rwalk:
558+ if(sz < 2+13 || (cnt = r16(&b))*13 > sz-2)
559+ goto error;
560+ if(cnt > C9maxpathel){
561+ c->error("c9proc: Rwalk !(%d <= %d)", cnt, C9maxpathel);
562+ return C9Epath;
563+ }
564+ for(i = 0; i < cnt; i++){
565+ r.qid[i].type = r08(&b);
566+ r.qid[i].version = r32(&b);
567+ r.qid[i].path = r64(&b);
568+ }
569+ r.numqid = cnt;
570+ c->r(c, &r);
571+ break;
572+
573+ case Rstat:
574+ b += 2; sz -= 2;
575+ if((err = c9parsedir(c, &r.stat, &b, &sz)) != 0){
576+ c->error("c9proc");
577+ return err;
578+ }
579+ r.numqid = 1;
580+ c->r(c, &r);
581+ break;
582+
583+ case Rflush:
584+ for(i = 0; i < C9maxflush; i++){
585+ if((c->flush[i] & 0xffff) == r.tag){
586+ freetag(c, c->flush[i]>>16);
587+ c->flush[i] = 0xffffffff;
588+ break;
589+ }
590+ }
591+ /* fallthrough */
592+ case Rclunk:
593+ case Rremove:
594+ case Rwstat:
595+ c->r(c, &r);
596+ break;
597+
598+ case Ropen:
599+ case Rcreate:
600+ if(sz < 17)
601+ goto error;
602+ r.qid[0].type = r08(&b);
603+ r.qid[0].version = r32(&b);
604+ r.qid[0].path = r64(&b);
605+ r.iounit = r32(&b);
606+ r.numqid = 1;
607+ c->r(c, &r);
608+ break;
609+
610+ case Rerror:
611+ if(sz < 2 || (cnt = r16(&b)) > sz-2)
612+ goto error;
613+ r.error = memmove(b-1, b, cnt);
614+ r.error[cnt] = 0;
615+ c->r(c, &r);
616+ break;
617+
618+ case Rauth:
619+ case Rattach:
620+ if(sz < 13)
621+ goto error;
622+ r.qid[0].type = r08(&b);
623+ r.qid[0].version = r32(&b);
624+ r.qid[0].path = r64(&b);
625+ r.numqid = 1;
626+ c->r(c, &r);
627+ break;
628+
629+ case Rversion:
630+ if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2)
631+ goto error;
632+ if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){
633+ c->error("invalid version");
634+ return C9Ever;
635+ }
636+ if(msize < c->msize)
637+ c->msize = msize;
638+ c->r(c, &r);
639+ break;
640+
641+ default:
642+ goto error;
643+ }
644+ return 0;
645+error:
646+ c->error("c9proc: invalid packet (type=%d)", r.type);
647+ return C9Epkt;
648+}
649+
650+C9error
651+s9version(C9ctx *c)
652+{
653+ uint8_t *b;
654+ C9error err;
655+
656+ if((b = R(c, 4+2+6, Rversion, 0xffff, &err)) != NULL){
657+ w32(&b, c->msize);
658+ wcs(&b, "9P2000", 6);
659+ err = c->end(c);
660+ };
661+ return err;
662+}
663+
664+C9error
665+s9auth(C9ctx *c, C9tag tag, const C9qid *aqid)
666+{
667+ uint8_t *b;
668+ C9error err;
669+
670+ if((b = R(c, 13, Rauth, tag, &err)) != NULL){
671+ w08(&b, aqid->type);
672+ w32(&b, aqid->version);
673+ w64(&b, aqid->path);
674+ err = c->end(c);
675+ }
676+ return err;
677+}
678+
679+C9error
680+s9error(C9ctx *c, C9tag tag, const char *ename)
681+{
682+ uint32_t len = safestrlen(ename);
683+ uint8_t *b;
684+ C9error err;
685+
686+ if(len > C9maxstr){
687+ c->error("s9error: invalid ename");
688+ return C9Estr;
689+ }
690+ if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){
691+ wcs(&b, ename, len);
692+ err = c->end(c);
693+ }
694+ return err;
695+}
696+
697+C9error
698+s9attach(C9ctx *c, C9tag tag, const C9qid *qid)
699+{
700+ uint8_t *b;
701+ C9error err;
702+
703+ if((b = R(c, 13, Rattach, tag, &err)) != NULL){
704+ w08(&b, qid->type);
705+ w32(&b, qid->version);
706+ w64(&b, qid->path);
707+ err = c->end(c);
708+ }
709+ return err;
710+}
711+
712+C9error
713+s9flush(C9ctx *c, C9tag tag)
714+{
715+ uint8_t *b;
716+ C9error err;
717+
718+ if((b = R(c, 0, Rflush, tag, &err)) != NULL)
719+ err = c->end(c);
720+ return err;
721+}
722+
723+C9error
724+s9walk(C9ctx *c, C9tag tag, C9qid *qids[])
725+{
726+ uint32_t i, n;
727+ uint8_t *b;
728+ C9error err;
729+
730+ for(n = 0; n < C9maxpathel && qids[n] != NULL; n++);
731+ if(n > C9maxpathel){
732+ c->error("s9walk: invalid elements !(0 <= %d <= %d)", n, C9maxpathel);
733+ return C9Epath;
734+ }
735+
736+ if((b = R(c, 2+n*13, Rwalk, tag, &err)) != NULL){
737+ w16(&b, n);
738+ for(i = 0; i < n; i++){
739+ w08(&b, qids[i]->type);
740+ w32(&b, qids[i]->version);
741+ w64(&b, qids[i]->path);
742+ }
743+ err = c->end(c);
744+ }
745+ return err;
746+}
747+
748+C9error
749+s9open(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit)
750+{
751+ uint8_t *b;
752+ C9error err;
753+
754+ if((b = R(c, 13+4, Ropen, tag, &err)) != NULL){
755+ w08(&b, qid->type);
756+ w32(&b, qid->version);
757+ w64(&b, qid->path);
758+ w32(&b, iounit);
759+ err = c->end(c);
760+ }
761+ return err;
762+}
763+
764+C9error
765+s9create(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit)
766+{
767+ uint8_t *b;
768+ C9error err;
769+
770+ if((b = R(c, 13+4, Rcreate, tag, &err)) != NULL){
771+ w08(&b, qid->type);
772+ w32(&b, qid->version);
773+ w64(&b, qid->path);
774+ w32(&b, iounit);
775+ err = c->end(c);
776+ }
777+ return err;
778+}
779+
780+C9error
781+s9read(C9ctx *c, C9tag tag, const void *data, uint32_t size)
782+{
783+ uint8_t *b;
784+ C9error err;
785+
786+ if((b = R(c, 4+size, Rread, tag, &err)) != NULL){
787+ w32(&b, size);
788+ memmove(b, data, size);
789+ err = c->end(c);
790+ }
791+ return err;
792+}
793+
794+C9error
795+s9write(C9ctx *c, C9tag tag, uint32_t size)
796+{
797+ uint8_t *b;
798+ C9error err;
799+
800+ if((b = R(c, 4, Rwrite, tag, &err)) != NULL){
801+ w32(&b, size);
802+ err = c->end(c);
803+ }
804+ return err;
805+}
806+
807+C9error
808+s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_t size)
809+{
810+ uint8_t *b;
811+ const C9stat *s;
812+ uint32_t nlen, ulen, glen, mulen, m, n;
813+ C9error err;
814+ int i;
815+
816+ if(size > c->msize-4-1-2)
817+ size = c->msize-4-1-2;
818+
819+ m = 0;
820+ for(i = 0; i < *num; i++){
821+ s = st[i];
822+ nlen = safestrlen(s->name);
823+ ulen = safestrlen(s->uid);
824+ glen = safestrlen(s->gid);
825+ mulen = safestrlen(s->muid);
826+
827+ if(nlen == 0 || nlen > C9maxstr){
828+ c->error("s9readdir: invalid name");
829+ return C9Epath;
830+ }
831+ if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){
832+ c->error("s9readdir: string too long");
833+ return C9Estr;
834+ }
835+
836+ n = 2 + 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen;
837+ if(4+m+n > size)
838+ break;
839+ m += n;
840+ }
841+
842+ if((b = R(c, 4+m, Rread, tag, &err)) != NULL){
843+ *num = i;
844+ w32(&b, m);
845+ for(i = 0; i < *num; i++){
846+ s = st[i];
847+ nlen = safestrlen(s->name);
848+ ulen = safestrlen(s->uid);
849+ glen = safestrlen(s->gid);
850+ mulen = safestrlen(s->muid);
851+ w16(&b, 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen);
852+ w16(&b, 0xffff); /* type */
853+ w32(&b, 0xffffffff); /* dev */
854+ w08(&b, s->qid.type);
855+ w32(&b, s->qid.version);
856+ w64(&b, s->qid.path);
857+ w32(&b, s->mode);
858+ w32(&b, s->atime);
859+ w32(&b, s->mtime);
860+ w64(&b, s->size);
861+ wcs(&b, s->name, nlen);
862+ wcs(&b, s->uid, ulen);
863+ wcs(&b, s->gid, glen);
864+ wcs(&b, s->muid, mulen);
865+ }
866+ err = c->end(c);
867+ if(err == 0)
868+ *offset += m;
869+ }
870+ return err;
871+}
872+
873+C9error
874+s9clunk(C9ctx *c, C9tag tag)
875+{
876+ uint8_t *b;
877+ C9error err;
878+
879+ if((b = R(c, 0, Rclunk, tag, &err)) != NULL)
880+ err = c->end(c);
881+ return err;
882+}
883+
884+C9error
885+s9remove(C9ctx *c, C9tag tag)
886+{
887+ uint8_t *b;
888+ C9error err;
889+
890+ if((b = R(c, 0, Rremove, tag, &err)) != NULL)
891+ err = c->end(c);
892+ return err;
893+}
894+
895+C9error
896+s9stat(C9ctx *c, C9tag tag, const C9stat *s)
897+{
898+ uint32_t nlen = safestrlen(s->name), ulen = safestrlen(s->uid);
899+ uint32_t glen = safestrlen(s->gid), mulen = safestrlen(s->name);
900+ uint32_t statsz = 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen;
901+ uint8_t *b;
902+ C9error err;
903+
904+ if(nlen == 0 || nlen > C9maxstr){
905+ c->error("s9stat: invalid name");
906+ return C9Epath;
907+ }
908+ if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){
909+ c->error("s9stat: string too long");
910+ return C9Estr;
911+ }
912+
913+ if((b = R(c, 2+2+statsz, Rstat, tag, &err)) != NULL){
914+ w16(&b, statsz+2);
915+ w16(&b, statsz);
916+ w16(&b, 0xffff); /* type */
917+ w32(&b, 0xffffffff); /* dev */
918+ w08(&b, s->qid.type);
919+ w32(&b, s->qid.version);
920+ w64(&b, s->qid.path);
921+ w32(&b, s->mode);
922+ w32(&b, s->atime);
923+ w32(&b, s->mtime);
924+ w64(&b, s->size);
925+ wcs(&b, s->name, nlen);
926+ wcs(&b, s->uid, ulen);
927+ wcs(&b, s->gid, glen);
928+ wcs(&b, s->muid, mulen);
929+ err = c->end(c);
930+ }
931+ return err;
932+}
933+
934+C9error
935+s9wstat(C9ctx *c, C9tag tag)
936+{
937+ uint8_t *b;
938+ C9error err;
939+
940+ if((b = R(c, 0, Rwstat, tag, &err)) != NULL)
941+ err = c->end(c);
942+ return err;
943+}
944+
945+C9error
946+s9proc(C9ctx *c)
947+{
948+ uint32_t i, sz, cnt, n, msize;
949+ int readerr;
950+ uint8_t *b;
951+ C9error err;
952+ C9t t;
953+
954+ readerr = -1;
955+ if((b = c->read(c, 4, &readerr)) == NULL){
956+ if(readerr != 0)
957+ c->error("s9proc: short read");
958+ return readerr == 0 ? 0 : C9Epkt;
959+ }
960+
961+ sz = r32(&b);
962+ if(sz < 7 || sz > c->msize){
963+ c->error("s9proc: invalid packet size !(7 <= %u <= %u)", sz, c->msize);
964+ return C9Epkt;
965+ }
966+ sz -= 4;
967+ readerr = -1;
968+ if((b = c->read(c, sz, &readerr)) == NULL){
969+ if(readerr != 0)
970+ c->error("s9proc: short read");
971+ return readerr == 0 ? 0 : C9Epkt;
972+ }
973+
974+ t.type = r08(&b);
975+ t.tag = r16(&b);
976+ sz -= 3;
977+
978+ if((c->svflags & Svver) == 0 && t.type != Tversion){
979+ c->error("s9proc: expected Tversion, got %d", t.type);
980+ return C9Epkt;
981+ }
982+
983+ switch(t.type){
984+ case Tread:
985+ if(sz < 4+8+4)
986+ goto error;
987+ t.fid = r32(&b);
988+ t.read.offset = r64(&b);
989+ t.read.size = r32(&b);
990+ if(t.read.size > maxread(c))
991+ t.read.size = maxread(c);
992+ c->t(c, &t);
993+ break;
994+
995+ case Twrite:
996+ if(sz < 4+8+4)
997+ goto error;
998+ t.fid = r32(&b);
999+ t.write.offset = r64(&b);
1000+ if((t.write.size = r32(&b)) < sz-4-8-4)
1001+ goto error;
1002+ if(t.write.size > maxwrite(c))
1003+ t.write.size = maxwrite(c);
1004+ t.write.data = b;
1005+ c->t(c, &t);
1006+ break;
1007+
1008+ case Tclunk:
1009+ case Tstat:
1010+ case Tremove:
1011+ if(sz < 4)
1012+ goto error;
1013+ t.fid = r32(&b);
1014+ c->t(c, &t);
1015+ break;
1016+
1017+ case Twalk:
1018+ if(sz < 4+4+2)
1019+ goto error;
1020+ t.fid = r32(&b);
1021+ t.walk.newfid = r32(&b);
1022+ if((n = r16(&b)) > 16){
1023+ c->error("s9proc: Twalk !(%d <= 16)", n);
1024+ return C9Epath;
1025+ }
1026+ sz -= 4+4+2;
1027+ if(n > 0){
1028+ for(i = 0; i < n; i++){
1029+ if(sz < 2 || (cnt = r16(&b)) > sz-2)
1030+ goto error;
1031+ if(cnt < 1){
1032+ c->error("s9proc: Twalk invalid element [%d]", i);
1033+ return C9Epath;
1034+ }
1035+ b[-2] = 0;
1036+ t.walk.wname[i] = (char*)b;
1037+ b += cnt;
1038+ sz -= 2 + cnt;
1039+ }
1040+ memmove(t.walk.wname[i-1]-1, t.walk.wname[i-1], (char*)b - t.walk.wname[i-1]);
1041+ t.walk.wname[i-1]--;
1042+ b[-1] = 0;
1043+ }else
1044+ i = 0;
1045+ t.walk.wname[i] = NULL;
1046+ c->t(c, &t);
1047+ break;
1048+
1049+ case Topen:
1050+ if(sz < 4+1)
1051+ goto error;
1052+ t.fid = r32(&b);
1053+ t.open.mode = r08(&b);
1054+ c->t(c, &t);
1055+ break;
1056+
1057+ case Twstat:
1058+ if(sz < 4+2)
1059+ goto error;
1060+ t.fid = r32(&b);
1061+ if((cnt = r16(&b)) > sz-4)
1062+ goto error;
1063+ if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){
1064+ c->error("s9proc");
1065+ return err;
1066+ }
1067+ c->t(c, &t);
1068+ break;
1069+
1070+ case Tcreate:
1071+ if(sz < 4+2+4+1)
1072+ goto error;
1073+ t.fid = r32(&b);
1074+ if((cnt = r16(&b)) < 1 || cnt > sz-4-2-4-1)
1075+ goto error;
1076+ t.create.name = (char*)b;
1077+ b += cnt;
1078+ t.create.perm = r32(&b);
1079+ t.create.mode = r08(&b);
1080+ t.create.name[cnt] = 0;
1081+ c->t(c, &t);
1082+ break;
1083+
1084+ case Tflush:
1085+ if(sz < 2)
1086+ goto error;
1087+ t.flush.oldtag = r16(&b);
1088+ c->t(c, &t);
1089+ break;
1090+
1091+ case Tversion:
1092+ if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2)
1093+ goto error;
1094+ if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){
1095+ if((b = R(c, 4+2+7, Rversion, 0xffff, &err)) != NULL){
1096+ w32(&b, 0);
1097+ wcs(&b, "unknown", 7);
1098+ err = c->end(c);
1099+ c->error("s9proc: invalid version");
1100+ }
1101+ return C9Ever;
1102+ }
1103+ if(msize < c->msize)
1104+ c->msize = msize;
1105+ c->svflags |= Svver;
1106+ c->t(c, &t);
1107+ break;
1108+
1109+ case Tattach:
1110+ if(sz < 4+4+2+2)
1111+ goto error;
1112+ t.fid = r32(&b);
1113+ t.attach.afid = r32(&b);
1114+ cnt = r16(&b);
1115+ sz -= 4+4+2;
1116+ if(cnt+2 > sz)
1117+ goto error;
1118+ t.attach.uname = (char*)b;
1119+ b += cnt;
1120+ cnt = r16(&b);
1121+ b[-2] = 0;
1122+ sz -= cnt+2;
1123+ if(cnt > sz)
1124+ goto error;
1125+ memmove(b-1, b, cnt);
1126+ t.attach.aname = (char*)b-1;
1127+ t.attach.aname[cnt] = 0;
1128+ c->t(c, &t);
1129+ break;
1130+
1131+ case Tauth:
1132+ if(sz < 4+2+2)
1133+ goto error;
1134+ t.auth.afid = r32(&b);
1135+ cnt = r16(&b);
1136+ sz -= 4+2;
1137+ if(cnt+2 > sz)
1138+ goto error;
1139+ t.auth.uname = (char*)b;
1140+ b += cnt;
1141+ cnt = r16(&b);
1142+ b[-2] = 0;
1143+ sz -= cnt+2;
1144+ if(cnt > sz)
1145+ goto error;
1146+ memmove(b-1, b, cnt);
1147+ t.auth.aname = (char*)b-1;
1148+ t.auth.aname[cnt] = 0;
1149+ c->t(c, &t);
1150+ break;
1151+
1152+ default:
1153+ goto error;
1154+ }
1155+ return 0;
1156+error:
1157+ c->error("s9proc: invalid packet (type=%d)", t.type);
1158+ return C9Epkt;
1159+}
c9.h+350−0
@@ -0,0 +1,350 @@
1+struct C9aux;
2+
3+typedef struct C9r C9r;
4+typedef struct C9t C9t;
5+typedef struct C9stat C9stat;
6+typedef struct C9ctx C9ctx;
7+typedef struct C9qid C9qid;
8+typedef enum C9error C9error;
9+typedef enum C9mode C9mode;
10+typedef enum C9rtype C9rtype;
11+typedef enum C9ttype C9ttype;
12+typedef enum C9qt C9qt;
13+typedef uint32_t C9fid;
14+typedef uint16_t C9tag;
15+
16+/* Stat field is not changed if it's set to this value when calling c9wstat. */
17+#define C9nochange (~0)
18+
19+/* Special fid used with auth/attach to basically avoid authentication. */
20+#define C9nofid ((C9fid)~0)
21+
22+/* C9modes for opening a file. */
23+enum C9mode
24+{
25+ C9read = 0,
26+ C9write = 1,
27+ C9rdwr = 2,
28+ C9exec = 3,
29+ C9trunc = 0x10,
30+ C9rclose = 0x40,
31+};
32+
33+enum C9perm
34+{
35+ /* User/owner. */
36+ C9permur = 1<<8, /* Readable. */
37+ C9permuw = 1<<7, /* Writable. */
38+ C9permux = 1<<6, /* Executable. */
39+
40+ /* Group. */
41+ C9permgr = 1<<5,
42+ C9permgw = 1<<4,
43+ C9permgx = 1<<3,
44+
45+ /* Other. */
46+ C9permor = 1<<2,
47+ C9permow = 1<<1,
48+ C9permox = 1<<0,
49+};
50+
51+/* Directory. */
52+#define C9permdir 0x80000000
53+
54+/* Bitmask of stat.mode. */
55+#define C9stdir 0x80000000
56+#define C9stappend 0x40000000
57+#define C9stexcl 0x20000000
58+#define C9sttmp 0x04000000
59+
60+/* Limits. */
61+enum
62+{
63+ C9maxtags = 64, /* Maximal number of outstanding requests. [1-65535] */
64+ C9maxflush = 8, /* Maximal number of outstanding flushes. [1-65535] */
65+ C9maxstr = 0xffff, /* Maximal string length. [1-65535] */
66+ C9minmsize = 4096, /* Minimal sane msize. [4096-...] */
67+ C9maxpathel = 16, /* Maximal number of elements in a path. Do not change. */
68+};
69+
70+/* Errors. */
71+enum C9error
72+{
73+ C9Einit = -1, /* Initialization failed. */
74+ C9Ever = -2, /* Protocol version doesn't match. */
75+ C9Epkt = -3, /* Incoming packet error. */
76+ C9Etag = -4, /* No free tags or bad tag. */
77+ C9Ebuf = -5, /* No buffer space enough for a message. */
78+ C9Epath = -6, /* Path is too long or just invalid. */
79+ C9Eflush = -7, /* Limit of outstanding flushes reached. */
80+ C9Esize = -8, /* Can't fit data in one message. */
81+ C9Estr = -9 /* Bad string. */
82+};
83+
84+/* Request types. */
85+enum C9ttype
86+{
87+ Tversion = 100,
88+ Tauth = 102,
89+ Tattach = 104,
90+ Tflush = 108,
91+ Twalk = 110,
92+ Topen = 112,
93+ Tcreate = 114,
94+ Tread = 116,
95+ Twrite = 118,
96+ Tclunk = 120,
97+ Tremove = 122,
98+ Tstat = 124,
99+ Twstat = 126
100+};
101+
102+/* Response types. */
103+enum C9rtype
104+{
105+ Rversion = 101,
106+ Rauth = 103,
107+ Rattach = 105,
108+ Rerror = 107,
109+ Rflush = 109,
110+ Rwalk = 111,
111+ Ropen = 113,
112+ Rcreate = 115,
113+ Rread = 117,
114+ Rwrite = 119,
115+ Rclunk = 121,
116+ Rremove = 123,
117+ Rstat = 125,
118+ Rwstat = 127
119+};
120+
121+/* Unique file id type. */
122+enum C9qt
123+{
124+ C9qtdir = 1<<7,
125+ C9qtappend = 1<<6,
126+ C9qtexcl = 1<<5,
127+ C9qtauth = 1<<3,
128+ C9qttmp = 1<<2,
129+ C9qtfile = 0
130+};
131+
132+/* Unique file id. */
133+struct C9qid
134+{
135+ uint64_t path;
136+ uint32_t version;
137+ C9qt type;
138+};
139+
140+/*
141+ * File stats. Version and muid are ignored on wstat. Dmdir bit
142+ * change in mode won't work on wstat. Set any integer field to
143+ * C9nochange to keep it unchanged on wstat. Set any string to NULL to
144+ * keep it unchanged. Strings can be empty (""), but never NULL after
145+ * stat call.
146+ */
147+struct C9stat
148+{
149+ uint64_t size; /* Size of the file (in bytes). */
150+ char *name; /* Name of the file. */
151+ char *uid; /* Owner of the file. */
152+ char *gid; /* Group of the file. */
153+ char *muid; /* The user who modified the file last. */
154+ C9qid qid; /* Same as qid[0]. */
155+ uint32_t mode; /* Permissions. See C9st* and C9perm. */
156+ uint32_t atime; /* Last access time. */
157+ uint32_t mtime; /* Last modification time. */
158+};
159+
160+/* Response data. */
161+struct C9r
162+{
163+ union
164+ {
165+ char *error;
166+
167+ struct
168+ {
169+ uint8_t *data;
170+ uint32_t size;
171+ }read;
172+
173+ struct
174+ {
175+ uint32_t size;
176+ }write;
177+
178+ /* File stats (only valid if type is Rstat). */
179+ C9stat stat;
180+
181+ /*
182+ * Qid(s). qid[0] is valid for auth/attach/create/stat/open.
183+ * More ids may be a result of a walk, see numqid.
184+ */
185+ C9qid qid[C9maxpathel];
186+ };
187+ C9rtype type; /* Response type. */
188+
189+ /*
190+ * If not zero, is the maximum number of bytes that are guaranteed
191+ * to be read or written atomically, without breaking into multiple
192+ * messages.
193+ */
194+ uint32_t iounit;
195+
196+ int numqid; /* Number of valid unique ids in qid array. */
197+ C9tag tag; /* Tag number. */
198+};
199+
200+/* Request data. */
201+struct C9t
202+{
203+ C9ttype type;
204+ C9tag tag;
205+ union
206+ {
207+ struct
208+ {
209+ char *uname;
210+ char *aname;
211+ C9fid afid;
212+ }attach;
213+
214+ struct
215+ {
216+ char *uname;
217+ char *aname;
218+ C9fid afid;
219+ }auth;
220+
221+ struct
222+ {
223+ char *name;
224+ uint32_t perm;
225+ C9mode mode;
226+ }create;
227+
228+ struct
229+ {
230+ C9tag oldtag;
231+ }flush;
232+
233+ struct
234+ {
235+ C9mode mode;
236+ }open;
237+
238+ struct
239+ {
240+ uint64_t offset;
241+ uint32_t size;
242+ }read;
243+
244+ struct
245+ {
246+ char *wname[C9maxpathel+1]; /* wname[16] is always NULL */
247+ C9fid newfid;
248+ }walk;
249+
250+ struct
251+ {
252+ uint64_t offset;
253+ uint8_t *data;
254+ uint32_t size;
255+ }write;
256+
257+ C9stat wstat;
258+ };
259+ C9fid fid;
260+};
261+
262+enum
263+{
264+ C9tagsbits = sizeof(uint32_t) * 8,
265+};
266+
267+struct C9ctx
268+{
269+ /*
270+ * Should return a pointer to the data (exactly 'size' bytes) read.
271+ * Set 'err' to non-zero and return NULL in case of error.
272+ * 'err' set to zero (no error) should be used to return from c9process
273+ * early (timeout on read to do non-blocking operations, for example).
274+ */
275+ uint8_t *(*read)(C9ctx *ctx, uint32_t size, int *err) __attribute__((nonnull(1, 3)));
276+
277+ /* Should return a buffer to store 'size' bytes. Nil means no memory. */
278+ uint8_t *(*begin)(C9ctx *ctx, uint32_t size) __attribute__((nonnull(1)));
279+
280+ /*
281+ * Marks the end of a message. Callback may decide if any accumulated
282+ * messages should be sent to the server/client.
283+ */
284+ int (*end)(C9ctx *ctx) __attribute__((nonnull(1)));
285+
286+ /* Callback called every time a new R-message is received. */
287+ void (*r)(C9ctx *ctx, C9r *r) __attribute__((nonnull(1, 2)));
288+
289+ /* Callback called every time a new T-message is received. */
290+ void (*t)(C9ctx *ctx, C9t *t) __attribute__((nonnull(1, 2)));
291+
292+ /* Callback for error messages. */
293+ void (*error)(const char *fmt, ...) __attribute__((nonnull(1)));
294+
295+ /* Auxiliary data, can be used by any of above callbacks. */
296+ struct C9aux *aux;
297+
298+ /* private stuff */
299+ uint32_t msize;
300+ uint32_t flush[C9maxflush];
301+ uint32_t tags[C9maxtags/C9tagsbits];
302+ union
303+ {
304+ C9tag lowfreetag;
305+ uint16_t svflags;
306+ };
307+};
308+
309+/* Parse one directory entry. */
310+extern C9error c9parsedir(C9ctx *c, C9stat *stat, uint8_t **data, uint32_t *size) __attribute__((nonnull(1, 2, 3)));
311+
312+extern C9error c9version(C9ctx *c, C9tag *tag, uint32_t msize) __attribute__((nonnull(1, 2)));
313+extern C9error c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname) __attribute__((nonnull(1, 2)));
314+extern C9error c9flush(C9ctx *c, C9tag *tag, C9tag oldtag) __attribute__((nonnull(1, 2)));
315+extern C9error c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const char *aname) __attribute__((nonnull(1, 2)));
316+extern C9error c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[]) __attribute__((nonnull(1, 2, 5)));
317+extern C9error c9open(C9ctx *c, C9tag *tag, C9fid fid, C9mode mode) __attribute__((nonnull(1, 2)));
318+extern C9error c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mode mode) __attribute__((nonnull(1, 2, 4)));
319+extern C9error c9read(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, uint32_t count) __attribute__((nonnull(1, 2)));
320+extern C9error c9write(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, const void *in, uint32_t count) __attribute__((nonnull(1, 2, 5)));
321+extern C9error c9wrstr(C9ctx *c, C9tag *tag, C9fid fid, const char *s) __attribute__((nonnull(1, 2, 4)));
322+extern C9error c9clunk(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2)));
323+extern C9error c9remove(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2)));
324+extern C9error c9stat(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2)));
325+extern C9error c9wstat(C9ctx *c, C9tag *tag, C9fid fid, const C9stat *s) __attribute__((nonnull(1, 2, 4)));
326+
327+/*
328+ * Wait until a response comes and process it. If the function returns
329+ * any error, context must be treated as 'broken' and no subsequent calls
330+ * should be made without reinitialization (c9version).
331+ */
332+extern C9error c9proc(C9ctx *c) __attribute__((nonnull(1)));
333+
334+extern C9error s9version(C9ctx *c) __attribute__((nonnull(1)));
335+extern C9error s9auth(C9ctx *c, C9tag tag, const C9qid *aqid) __attribute__((nonnull(1, 3)));
336+extern C9error s9error(C9ctx *c, C9tag tag, const char *err) __attribute__((nonnull(1)));
337+extern C9error s9attach(C9ctx *c, C9tag tag, const C9qid *qid) __attribute__((nonnull(1, 3)));
338+extern C9error s9flush(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
339+extern C9error s9walk(C9ctx *c, C9tag tag, C9qid *qids[]) __attribute__((nonnull(1, 3)));
340+extern C9error s9open(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) __attribute__((nonnull(1, 3)));
341+extern C9error s9create(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) __attribute__((nonnull(1, 3)));
342+extern C9error s9read(C9ctx *c, C9tag tag, const void *data, uint32_t size) __attribute__((nonnull(1, 3)));
343+extern C9error s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_t size) __attribute__((nonnull(1, 3, 4)));
344+extern C9error s9write(C9ctx *c, C9tag tag, uint32_t size) __attribute__((nonnull(1)));
345+extern C9error s9clunk(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
346+extern C9error s9remove(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
347+extern C9error s9stat(C9ctx *c, C9tag tag, const C9stat *s) __attribute__((nonnull(1, 3)));
348+extern C9error s9wstat(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
349+
350+extern C9error s9proc(C9ctx *c) __attribute__((nonnull(1)));

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t