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 @@
1Public domain.
README.md+8−0
@@ -0,0 +1,8 @@
1# c9
2
3Low level 9p client and server.
4
5## Examples
6
7Until I have time to write a minimal example you could take a look at
8https://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
12enum
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
21static void
22w08(uint8_t **p, uint8_t x)
23{
24 (*p)[0] = x;
25 *p += 1;
26}
27
28static void
29w16(uint8_t **p, uint16_t x)
30{
31 (*p)[0] = x;
32 (*p)[1] = x>>8;
33 *p += 2;
34}
35
36static void
37w32(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
46static void
47w64(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
60static void
61wcs(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
70static uint8_t
71r08(uint8_t **p)
72{
73 *p += 1;
74 return (*p)[-1];
75}
76
77static uint16_t
78r16(uint8_t **p)
79{
80 *p += 2;
81 return (uint16_t)(*p)[-2]<<0 | (uint16_t)(*p)[-1]<<8;
82}
83
84static uint32_t
85r32(uint8_t **p)
86{
87 return r16(p) | (uint32_t)r16(p)<<16;
88}
89
90static uint64_t
91r64(uint8_t **p)
92{
93 return r32(p) | (uint64_t)r32(p)<<32;
94}
95
96static C9error
97newtag(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
133static int
134freetag(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
153static uint8_t *
154T(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
177static uint8_t *
178R(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
200C9error
201c9parsedir(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;
232error:
233 c->error("c9parsedir: invalid size");
234 return C9Epkt;
235}
236
237C9error
238c9version(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
260C9error
261c9auth(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
280C9error
281c9flush(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
301C9error
302c9attach(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
322C9error
323c9walk(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
354C9error
355c9open(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
368C9error
369c9create(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
389C9error
390c9read(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
404C9error
405c9write(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
420C9error
421c9wrstr(C9ctx *c, C9tag *tag, C9fid fid, const char *s)
422{
423 return c9write(c, tag, fid, 0, s, strlen(s));
424}
425
426C9error
427c9clunk(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
439C9error
440c9remove(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
452C9error
453c9stat(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
465C9error
466c9wstat(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
500C9error
501c9proc(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;
645error:
646 c->error("c9proc: invalid packet (type=%d)", r.type);
647 return C9Epkt;
648}
649
650C9error
651s9version(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
664C9error
665s9auth(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
679C9error
680s9error(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
697C9error
698s9attach(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
712C9error
713s9flush(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
723C9error
724s9walk(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
748C9error
749s9open(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
764C9error
765s9create(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
780C9error
781s9read(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
794C9error
795s9write(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
807C9error
808s9readdir(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
873C9error
874s9clunk(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
884C9error
885s9remove(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
895C9error
896s9stat(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
934C9error
935s9wstat(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
945C9error
946s9proc(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;
1156error:
1157 c->error("s9proc: invalid packet (type=%d)", t.type);
1158 return C9Epkt;
1159}
c9.h+350−0
@@ -0,0 +1,350 @@
1struct C9aux;
2
3typedef struct C9r C9r;
4typedef struct C9t C9t;
5typedef struct C9stat C9stat;
6typedef struct C9ctx C9ctx;
7typedef struct C9qid C9qid;
8typedef enum C9error C9error;
9typedef enum C9mode C9mode;
10typedef enum C9rtype C9rtype;
11typedef enum C9ttype C9ttype;
12typedef enum C9qt C9qt;
13typedef uint32_t C9fid;
14typedef 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. */
23enum C9mode
24{
25 C9read = 0,
26 C9write = 1,
27 C9rdwr = 2,
28 C9exec = 3,
29 C9trunc = 0x10,
30 C9rclose = 0x40,
31};
32
33enum 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. */
61enum
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. */
71enum 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. */
85enum 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. */
103enum 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. */
122enum 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. */
133struct 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 */
147struct 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. */
161struct 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. */
201struct 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
262enum
263{
264 C9tagsbits = sizeof(uint32_t) * 8,
265};
266
267struct 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. */
310extern C9error c9parsedir(C9ctx *c, C9stat *stat, uint8_t **data, uint32_t *size) __attribute__((nonnull(1, 2, 3)));
311
312extern C9error c9version(C9ctx *c, C9tag *tag, uint32_t msize) __attribute__((nonnull(1, 2)));
313extern C9error c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname) __attribute__((nonnull(1, 2)));
314extern C9error c9flush(C9ctx *c, C9tag *tag, C9tag oldtag) __attribute__((nonnull(1, 2)));
315extern C9error c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const char *aname) __attribute__((nonnull(1, 2)));
316extern C9error c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[]) __attribute__((nonnull(1, 2, 5)));
317extern C9error c9open(C9ctx *c, C9tag *tag, C9fid fid, C9mode mode) __attribute__((nonnull(1, 2)));
318extern C9error c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mode mode) __attribute__((nonnull(1, 2, 4)));
319extern C9error c9read(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, uint32_t count) __attribute__((nonnull(1, 2)));
320extern C9error c9write(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, const void *in, uint32_t count) __attribute__((nonnull(1, 2, 5)));
321extern C9error c9wrstr(C9ctx *c, C9tag *tag, C9fid fid, const char *s) __attribute__((nonnull(1, 2, 4)));
322extern C9error c9clunk(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2)));
323extern C9error c9remove(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2)));
324extern C9error c9stat(C9ctx *c, C9tag *tag, C9fid fid) __attribute__((nonnull(1, 2)));
325extern 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 */
332extern C9error c9proc(C9ctx *c) __attribute__((nonnull(1)));
333
334extern C9error s9version(C9ctx *c) __attribute__((nonnull(1)));
335extern C9error s9auth(C9ctx *c, C9tag tag, const C9qid *aqid) __attribute__((nonnull(1, 3)));
336extern C9error s9error(C9ctx *c, C9tag tag, const char *err) __attribute__((nonnull(1)));
337extern C9error s9attach(C9ctx *c, C9tag tag, const C9qid *qid) __attribute__((nonnull(1, 3)));
338extern C9error s9flush(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
339extern C9error s9walk(C9ctx *c, C9tag tag, C9qid *qids[]) __attribute__((nonnull(1, 3)));
340extern C9error s9open(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) __attribute__((nonnull(1, 3)));
341extern C9error s9create(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) __attribute__((nonnull(1, 3)));
342extern C9error s9read(C9ctx *c, C9tag tag, const void *data, uint32_t size) __attribute__((nonnull(1, 3)));
343extern C9error s9readdir(C9ctx *c, C9tag tag, C9stat *st[], int *num, uint64_t *offset, uint32_t size) __attribute__((nonnull(1, 3, 4)));
344extern C9error s9write(C9ctx *c, C9tag tag, uint32_t size) __attribute__((nonnull(1)));
345extern C9error s9clunk(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
346extern C9error s9remove(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
347extern C9error s9stat(C9ctx *c, C9tag tag, const C9stat *s) __attribute__((nonnull(1, 3)));
348extern C9error s9wstat(C9ctx *c, C9tag tag) __attribute__((nonnull(1)));
349
350extern 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