ref: b5801e9a8cf3df44ebaf8809ee69e177a2e62788
dir: /c9.c/
/* * This is 9p client and server implementation which aims to be * correct, small and secure. It's the lowest level implementation. * It doesn't have much comments, mostly because it doesn't make * any sense to copy-paste protocol documentation, which * you can read at http://man.cat-v.org/plan_9/5/, see 'intro'. */ #include <string.h> #include <stdint.h> #include "c9.h" enum { Svver = 1<<0, }; #define safestrlen(s) (s == NULL ? 0 : (uint32_t)strlen(s)) #define maxread(c) (c->msize-4-4-1-2) #define maxwrite(c) maxread(c) static void w08(uint8_t **p, uint8_t x) { (*p)[0] = x; *p += 1; } static void w16(uint8_t **p, uint16_t x) { (*p)[0] = x; (*p)[1] = x>>8; *p += 2; } static void w32(uint8_t **p, uint32_t x) { (*p)[0] = x; (*p)[1] = x>>8; (*p)[2] = x>>16; (*p)[3] = x>>24; *p += 4; } static void w64(uint8_t **p, uint64_t x) { (*p)[0] = x; (*p)[1] = x>>8; (*p)[2] = x>>16; (*p)[3] = x>>24; (*p)[4] = x>>32; (*p)[5] = x>>40; (*p)[6] = x>>48; (*p)[7] = x>>56; *p += 8; } static void wcs(uint8_t **p, const char *s, int len) { w16(p, len); if(s != NULL){ memmove(*p, s, len); *p += len; } } static uint8_t r08(uint8_t **p) { *p += 1; return (*p)[-1]; } static uint16_t r16(uint8_t **p) { *p += 2; return (uint16_t)(*p)[-2]<<0 | (uint16_t)(*p)[-1]<<8; } static uint32_t r32(uint8_t **p) { return r16(p) | (uint32_t)r16(p)<<16; } static uint64_t r64(uint8_t **p) { return r32(p) | (uint64_t)r32(p)<<32; } static C9error newtag(C9ctx *c, C9ttype type, C9tag *tag) { uint32_t i; if(type == Tversion){ *tag = 0xffff; return 0; } if(c->lowfreetag < C9maxtags){ uint32_t d = c->lowfreetag / C9tagbits, m = c->lowfreetag % C9tagbits; if((c->tags[d] & 1<<m) != 0){ c->tags[d] &= ~(1<<m); *tag = c->lowfreetag++; return 0; } } for(i = 0; i < (int)sizeof(c->tags)/sizeof(c->tags[0]); i++){ uint32_t x, j; for(j = 0, x = c->tags[i]; (x & 1) != 0 && j < C9tagbits; j++); if(j < C9tagbits){ c->tags[i] &= ~(1<<j); *tag = i*C9tagbits + j; return 0; } } c->error("newtag: no free tags"); return C9Etag; } static int freetag(C9ctx *c, C9tag tag) { if(tag != 0xffff){ uint32_t d = tag / C9tagbits, m = tag % C9tagbits; if(tag >= C9maxtags){ c->error("freetag: invalid tag"); return -1; } if((c->tags[d] & 1<<m) != 0){ c->error("freetag: double free"); return -1; } if(c->lowfreetag > tag) c->lowfreetag = tag; c->tags[d] |= 1<<m; } return 0; } static uint8_t * T(C9ctx *c, uint32_t size, C9ttype type, C9tag *tag, C9error *err) { uint8_t *p = NULL; if(size > c->msize-4-1-2){ c->error("T: invalid size"); *err = C9Esize; }else if((*err = newtag(c, type, tag)) == 0){ size += 4+1+2; if((p = c->begin(c, size)) == NULL){ c->error("T: no buffer"); freetag(c, *tag); *err = C9Ebuf; }else{ *err = 0; w32(&p, size); w08(&p, type); w16(&p, *tag); } } return p; } static uint8_t * R(C9ctx *c, uint32_t size, C9rtype type, C9tag tag, C9error *err) { uint8_t *p = NULL; if(size > c->msize-4-1-2){ c->error("R: invalid size"); *err = C9Esize; }else{ size += 4+1+2; if((p = c->begin(c, size)) == NULL){ c->error("R: no buffer"); *err = C9Ebuf; }else{ *err = 0; w32(&p, size); w08(&p, type); w16(&p, tag); } } return p; } C9error c9parsedir(C9ctx *c, C9stat *stat, uint8_t **t, uint32_t *size) { uint8_t *b; uint32_t cnt, sz; if(*size < 49 || (sz = r16(t)) < 47 || *size < 2+sz) goto error; *size -= 2+sz; *t += 6; /* skip type(2) and dev(4) */ stat->qid.type = r08(t); stat->qid.version = r32(t); stat->qid.path = r64(t); stat->mode = r32(t); stat->atime = r32(t); stat->mtime = r32(t); stat->size = r64(t); sz -= 39; if((cnt = r16(t)) > sz-2) goto error; stat->name = (char*)*t; b = *t = *t+cnt; sz -= 2+cnt; if(sz < 2 || (cnt = r16(t)) > sz-2) goto error; stat->uid = (char*)*t; *b = 0; b = *t = *t+cnt; sz -= 2+cnt; if(sz < 2 || (cnt = r16(t)) > sz-2) goto error; stat->gid = (char*)*t; *b = 0; b = *t = *t+cnt; sz -= 2+cnt; if(sz < 2 || (cnt = r16(t)) > sz-2) goto error; stat->muid = memmove(*t-1, *t, cnt); *b = stat->muid[cnt] = 0; *t = *t+cnt; sz -= 2+cnt; *t += sz; return 0; error: c->error("c9parsedir: invalid size"); return C9Epkt; } C9error c9version(C9ctx *c, C9tag *tag, uint32_t msize) { uint8_t *b; C9error err; if(msize < C9minmsize){ c->error("c9version: msize too small"); return C9Einit; } memset(c->tags, 0xff, sizeof(c->tags)); memset(c->flush, 0xff, sizeof(c->flush)); c->lowfreetag = 0; c->msize = msize; if((b = T(c, 4+2+6, Tversion, tag, &err)) != NULL){ w32(&b, msize); wcs(&b, "9P2000", 6); err = c->end(c); } return err; } C9error c9auth(C9ctx *c, C9tag *tag, C9fid afid, const char *uname, const char *aname) { uint8_t *b; uint32_t ulen = safestrlen(uname), alen = safestrlen(aname); C9error err; if(ulen > C9maxstr || alen > C9maxstr){ c->error("c9auth: string too long"); return C9Estr; } if((b = T(c, 4+2+ulen+2+alen, Tauth, tag, &err)) != NULL){ w32(&b, afid); wcs(&b, uname, ulen); wcs(&b, aname, alen); err = c->end(c); } return err; } C9error c9flush(C9ctx *c, C9tag *tag, C9tag oldtag) { uint8_t *b; C9error err; int i; for(i = 0; i < C9maxflush && c->flush[i] != (uint32_t)~0; i++); if(i == C9maxflush){ c->error("c9flush: no free flush slots"); return C9Eflush; } if((b = T(c, 2, Tflush, tag, &err)) != NULL){ w16(&b, oldtag); err = c->end(c); if(err == 0) c->flush[i] = (uint32_t)oldtag<<16 | *tag; } return err; } C9error c9attach(C9ctx *c, C9tag *tag, C9fid fid, C9fid afid, const char *uname, const char *aname) { uint32_t ulen = safestrlen(uname), alen = safestrlen(aname); uint8_t *b; C9error err; if(ulen > C9maxstr || alen > C9maxstr){ c->error("c9attach: string too long"); return C9Estr; } if((b = T(c, 4+4+2+ulen+2+alen, Tattach, tag, &err)) != NULL){ w32(&b, fid); w32(&b, afid); wcs(&b, uname, ulen); wcs(&b, aname, alen); err = c->end(c); } return err; } C9error c9walk(C9ctx *c, C9tag *tag, C9fid fid, C9fid newfid, const char *path[]) { uint32_t i, j, sz; uint32_t len[C9maxpathel]; uint8_t *b; C9error err; for(sz = i = 0; i < (int)sizeof(len)/sizeof(len[0]) && path[i] != NULL; i++){ len[i] = safestrlen(path[i]); if(len[i] == 0 || len[i] > C9maxstr){ c->error("c9walk: path element too long"); return C9Epath; } sz += 2 + len[i]; } if(path[i] != NULL || i == 0){ c->error("c9walk: invalid elements !(0 < %d <= %d)", i, C9maxpathel); return C9Epath; } if((b = T(c, 4+4+2+sz, Twalk, tag, &err)) != NULL){ w32(&b, fid); w32(&b, newfid); w16(&b, i); for(j = 0; j < i; j++) wcs(&b, path[j], len[j]); err = c->end(c); } return err; } C9error c9open(C9ctx *c, C9tag *tag, C9fid fid, C9mode mode) { uint8_t *b; C9error err; if((b = T(c, 4+1, Topen, tag, &err)) != NULL){ w32(&b, fid); w08(&b, mode); err = c->end(c); } return err; } C9error c9create(C9ctx *c, C9tag *tag, C9fid fid, const char *name, uint32_t perm, C9mode mode) { uint32_t nlen = safestrlen(name); uint8_t *b; C9error err; if(nlen == 0 || nlen > C9maxstr){ c->error("c9create: invalid name"); return C9Epath; } if((b = T(c, 4+2+nlen+4+1, Tcreate, tag, &err)) != NULL){ w32(&b, fid); wcs(&b, name, nlen); w32(&b, perm); w08(&b, mode); err = c->end(c); } return err; } C9error c9read(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, uint32_t count) { uint8_t *b; C9error err; if((b = T(c, 4+8+4, Tread, tag, &err)) != NULL){ w32(&b, fid); w64(&b, offset); w32(&b, count); err = c->end(c); } return err; } C9error c9write(C9ctx *c, C9tag *tag, C9fid fid, uint64_t offset, const void *in, uint32_t count) { uint8_t *b; C9error err; if((b = T(c, 4+8+4+count, Twrite, tag, &err)) != NULL){ w32(&b, fid); w64(&b, offset); w32(&b, count); memmove(b, in, count); err = c->end(c); } return err; } C9error c9wrstr(C9ctx *c, C9tag *tag, C9fid fid, const char *s) { return c9write(c, tag, fid, 0, s, strlen(s)); } C9error c9clunk(C9ctx *c, C9tag *tag, C9fid fid) { uint8_t *b; C9error err; if((b = T(c, 4, Tclunk, tag, &err)) != NULL){ w32(&b, fid); err = c->end(c); } return err; } C9error c9remove(C9ctx *c, C9tag *tag, C9fid fid) { uint8_t *b; C9error err; if((b = T(c, 4, Tremove, tag, &err)) != NULL){ w32(&b, fid); err = c->end(c); } return err; } C9error c9stat(C9ctx *c, C9tag *tag, C9fid fid) { uint8_t *b; C9error err; if((b = T(c, 4, Tstat, tag, &err)) != NULL){ w32(&b, fid); err = c->end(c); } return err; } C9error c9wstat(C9ctx *c, C9tag *tag, C9fid fid, const C9stat *s) { uint32_t nlen = safestrlen(s->name), ulen = safestrlen(s->uid), glen = safestrlen(s->gid); uint32_t unusedsz = 2+4+13, statsz = unusedsz+4+4+4+8+2+nlen+2+ulen+2+glen+2; uint8_t *b; C9error err; if(nlen == 0 || nlen > C9maxstr){ c->error("c9wstat: invalid name"); return C9Epath; } if(ulen > C9maxstr || glen > C9maxstr){ c->error("c9wstat: string too long"); return C9Estr; } if((b = T(c, 4+2+2+statsz, Twstat, tag, &err)) != NULL){ w32(&b, fid); w16(&b, statsz+2); w16(&b, statsz); memset(b, 0xff, unusedsz); /* leave type(2), dev(4) and qid(13) unchanged */ b += unusedsz; w32(&b, s->mode); w32(&b, s->atime); w32(&b, s->mtime); w64(&b, s->size); wcs(&b, s->name, nlen); wcs(&b, s->uid, ulen); wcs(&b, s->gid, glen); wcs(&b, NULL, 0); /* muid unchanged */ err = c->end(c); } return err; } C9error c9proc(C9ctx *c) { uint32_t i, sz, cnt, msize; uint8_t *b; int err; C9r r; err = -1; if((b = c->read(c, 4, &err)) == NULL){ if(err != 0) c->error("c9proc: short read"); return err == 0 ? 0 : C9Epkt; } sz = r32(&b); if(sz < 7 || sz > c->msize){ c->error("c9proc: invalid packet size !(7 <= %u <= %u)", sz, c->msize); return C9Epkt; } sz -= 4; err = -1; if((b = c->read(c, sz, &err)) == NULL){ if(err != 0) c->error("c9proc: short read"); return err == 0 ? 0 : C9Epkt; } r.type = r08(&b); r.tag = r16(&b); if(r.type != Rversion){ if(r.tag >= C9maxtags){ c->error("c9proc: invalid tag 0x%x", r.tag); return C9Epkt; } if(freetag(c, r.tag) != 0) return C9Etag; } sz -= 3; r.numqid = 0; switch(r.type){ case Rread: if(sz < 4 || (cnt = r32(&b)) > sz-4) goto error; r.read.data = b; r.read.size = cnt; c->r(c, &r); break; case Rwrite: if(sz < 4 || (cnt = r32(&b)) > c->msize) goto error; r.write.size = cnt; c->r(c, &r); break; case Rwalk: if(sz < 2+13 || (cnt = r16(&b))*13 > sz-2) goto error; if(cnt > C9maxpathel){ c->error("c9proc: Rwalk !(%d <= %d)", cnt, C9maxpathel); return C9Epath; } for(i = 0; i < cnt; i++){ r.qid[i].type = r08(&b); r.qid[i].version = r32(&b); r.qid[i].path = r64(&b); } r.numqid = cnt; c->r(c, &r); break; case Rstat: b += 2; sz -= 2; if((err = c9parsedir(c, &r.stat, &b, &sz)) != 0){ c->error("c9proc"); return err; } r.numqid = 1; c->r(c, &r); break; case Rflush: for(i = 0; i < C9maxflush; i++){ if((c->flush[i] & 0xffff) == r.tag){ freetag(c, c->flush[i]>>16); c->flush[i] = 0xffffffff; break; } } case Rclunk: case Rremove: case Rwstat: c->r(c, &r); break; case Ropen: case Rcreate: if(sz < 17) goto error; r.qid[0].type = r08(&b); r.qid[0].version = r32(&b); r.qid[0].path = r64(&b); r.iounit = r32(&b); r.numqid = 1; c->r(c, &r); break; case Rerror: if(sz < 2 || (cnt = r16(&b)) > sz-2) goto error; r.error = memmove(b-1, b, cnt); r.error[cnt] = 0; c->r(c, &r); break; case Rauth: case Rattach: if(sz < 13) goto error; r.qid[0].type = r08(&b); r.qid[0].version = r32(&b); r.qid[0].path = r64(&b); r.numqid = 1; c->r(c, &r); break; case Rversion: if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2) goto error; if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){ c->error("invalid version"); return C9Ever; } if(msize < c->msize) c->msize = msize; c->r(c, &r); break; default: goto error; } return 0; error: c->error("c9proc: invalid packet (type=%d)", r.type); return C9Epkt; } C9error s9version(C9ctx *c) { uint8_t *b; C9error err; if((b = R(c, 4+2+6, Rversion, 0xffff, &err)) != NULL){ w32(&b, c->msize); wcs(&b, "9P2000", 6); err = c->end(c); }; return err; } C9error s9auth(C9ctx *c, C9tag tag, const C9qid *aqid) { uint8_t *b; C9error err; if((b = R(c, 13, Rauth, tag, &err)) != NULL){ w08(&b, aqid->type); w32(&b, aqid->version); w64(&b, aqid->path); err = c->end(c); } return err; } C9error s9error(C9ctx *c, C9tag tag, const char *ename) { uint32_t len = safestrlen(ename); uint8_t *b; C9error err; if(len > C9maxstr){ c->error("s9error: invalid ename"); return C9Estr; } if((b = R(c, 2+len, Rerror, tag, &err)) != NULL){ wcs(&b, ename, len); err = c->end(c); } return err; } C9error s9attach(C9ctx *c, C9tag tag, const C9qid *qid) { uint8_t *b; C9error err; if((b = R(c, 13, Rattach, tag, &err)) != NULL){ w08(&b, qid->type); w32(&b, qid->version); w64(&b, qid->path); err = c->end(c); } return err; } C9error s9flush(C9ctx *c, C9tag tag) { uint8_t *b; C9error err; if((b = R(c, 0, Rflush, tag, &err)) != NULL) err = c->end(c); return err; } C9error s9walk(C9ctx *c, C9tag tag, const C9qid *qids[]) { uint32_t i, n; uint8_t *b; C9error err; for(n = 0; n < C9maxpathel && qids[n] != NULL; n++); if(n > C9maxpathel){ c->error("s9walk: invalid elements !(0 <= %d <= %d)", n, C9maxpathel); return C9Epath; } if((b = R(c, 2+n*13, Rwalk, tag, &err)) != NULL){ w16(&b, n); for(i = 0; i < n; i++){ w08(&b, qids[i]->type); w32(&b, qids[i]->version); w64(&b, qids[i]->path); } err = c->end(c); } return err; } C9error s9open(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) { uint8_t *b; C9error err; if((b = R(c, 13+4, Ropen, tag, &err)) != NULL){ w08(&b, qid->type); w32(&b, qid->version); w64(&b, qid->path); w32(&b, iounit); err = c->end(c); } return err; } C9error s9create(C9ctx *c, C9tag tag, const C9qid *qid, uint32_t iounit) { uint8_t *b; C9error err; if((b = R(c, 13+4, Rcreate, tag, &err)) != NULL){ w08(&b, qid->type); w32(&b, qid->version); w64(&b, qid->path); w32(&b, iounit); err = c->end(c); } return err; } C9error s9read(C9ctx *c, C9tag tag, const void *data, uint32_t size) { uint8_t *b; C9error err; if((b = R(c, 4+size, Rread, tag, &err)) != NULL){ w32(&b, size); memmove(b, data, size); err = c->end(c); } return err; } C9error s9write(C9ctx *c, C9tag tag, uint32_t size) { uint8_t *b; C9error err; if((b = R(c, 4, Rwrite, tag, &err)) != NULL){ w32(&b, size); err = c->end(c); } return err; } C9error s9readdir(C9ctx *c, C9tag tag, const C9stat *st[], int *num, uint64_t *offset, uint32_t size) { uint8_t *b; const C9stat *s; uint32_t nlen, ulen, glen, mulen, m, n; C9error err; int i; if(size > c->msize-4-1-2) size = c->msize-4-1-2; m = 0; for(i = 0; i < *num; i++){ s = st[i]; nlen = safestrlen(s->name); ulen = safestrlen(s->uid); glen = safestrlen(s->gid); mulen = safestrlen(s->muid); if(nlen == 0 || nlen > C9maxstr){ c->error("s9readdir: invalid name"); return C9Epath; } if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ c->error("s9readdir: string too long"); return C9Estr; } n = 2 + 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen; if(4+m+n > size) break; m += n; } if((b = R(c, 4+m, Rread, tag, &err)) != NULL){ *num = i; w32(&b, m); for(i = 0; i < *num; i++){ s = st[i]; nlen = safestrlen(s->name); ulen = safestrlen(s->uid); glen = safestrlen(s->gid); mulen = safestrlen(s->muid); w16(&b, 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen); w16(&b, 0xffff); /* type */ w32(&b, 0xffffffff); /* dev */ w08(&b, s->qid.type); w32(&b, s->qid.version); w64(&b, s->qid.path); w32(&b, s->mode); w32(&b, s->atime); w32(&b, s->mtime); w64(&b, s->size); wcs(&b, s->name, nlen); wcs(&b, s->uid, ulen); wcs(&b, s->gid, glen); wcs(&b, s->muid, mulen); } err = c->end(c); if(err == 0) *offset += m; } return err; } C9error s9clunk(C9ctx *c, C9tag tag) { uint8_t *b; C9error err; if((b = R(c, 0, Rclunk, tag, &err)) != NULL) err = c->end(c); return err; } C9error s9remove(C9ctx *c, C9tag tag) { uint8_t *b; C9error err; if((b = R(c, 0, Rremove, tag, &err)) != NULL) err = c->end(c); return err; } C9error s9stat(C9ctx *c, C9tag tag, const C9stat *s) { uint32_t nlen = safestrlen(s->name), ulen = safestrlen(s->uid); uint32_t glen = safestrlen(s->gid), mulen = safestrlen(s->name); uint32_t statsz = 2+4+13+4+4+4+8+2+nlen+2+ulen+2+glen+2+mulen; uint8_t *b; C9error err; if(nlen == 0 || nlen > C9maxstr){ c->error("s9stat: invalid name"); return C9Epath; } if(ulen > C9maxstr || glen > C9maxstr || mulen > C9maxstr){ c->error("s9stat: string too long"); return C9Estr; } if((b = R(c, 2+2+statsz, Rstat, tag, &err)) != NULL){ w16(&b, statsz+2); w16(&b, statsz); w16(&b, 0xffff); /* type */ w32(&b, 0xffffffff); /* dev */ w08(&b, s->qid.type); w32(&b, s->qid.version); w64(&b, s->qid.path); w32(&b, s->mode); w32(&b, s->atime); w32(&b, s->mtime); w64(&b, s->size); wcs(&b, s->name, nlen); wcs(&b, s->uid, ulen); wcs(&b, s->gid, glen); wcs(&b, s->muid, mulen); err = c->end(c); } return err; } C9error s9wstat(C9ctx *c, C9tag tag) { uint8_t *b; C9error err; if((b = R(c, 0, Rwstat, tag, &err)) != NULL) err = c->end(c); return err; } C9error s9proc(C9ctx *c) { uint32_t i, sz, cnt, n, msize; int readerr; uint8_t *b; C9error err; C9t t; readerr = -1; if((b = c->read(c, 4, &readerr)) == NULL){ if(readerr != 0) c->error("s9proc: short read"); return readerr == 0 ? 0 : C9Epkt; } sz = r32(&b); if(sz < 7 || sz > c->msize){ c->error("s9proc: invalid packet size !(7 <= %u <= %u)", sz, c->msize); return C9Epkt; } sz -= 4; readerr = -1; if((b = c->read(c, sz, &readerr)) == NULL){ if(readerr != 0) c->error("s9proc: short read"); return readerr == 0 ? 0 : C9Epkt; } t.type = r08(&b); t.tag = r16(&b); sz -= 3; if((c->svflags & Svver) == 0 && t.type != Tversion){ c->error("s9proc: expected Tversion, got %d", t.type); return C9Epkt; } switch(t.type){ case Tread: if(sz < 4+8+4) goto error; t.fid = r32(&b); t.read.offset = r64(&b); t.read.size = r32(&b); if(t.read.size > maxread(c)) t.read.size = maxread(c); c->t(c, &t); break; case Twrite: if(sz < 4+8+4) goto error; t.fid = r32(&b); t.write.offset = r64(&b); if((t.write.size = r32(&b)) < sz-4-8-4) goto error; if(t.write.size > maxwrite(c)) t.write.size = maxwrite(c); t.write.data = b; c->t(c, &t); break; case Tclunk: case Tstat: case Tremove: if(sz < 4) goto error; t.fid = r32(&b); c->t(c, &t); break; case Twalk: if(sz < 4+4+2) goto error; t.fid = r32(&b); t.walk.newfid = r32(&b); if((n = r16(&b)) > 16){ c->error("s9proc: Twalk !(%d <= 16)", n); return C9Epath; } sz -= 4+4+2; if(n > 0){ for(i = 0; i < n; i++){ if(sz < 2 || (cnt = r16(&b)) > sz-2) goto error; if(cnt < 1){ c->error("s9proc: Twalk invalid element [%d]", i); return C9Epath; } b[-2] = 0; t.walk.wname[i] = (char*)b; b += cnt; sz -= 2 + cnt; } memmove(t.walk.wname[i-1]-1, t.walk.wname[i-1], (char*)b - t.walk.wname[i-1]); t.walk.wname[i-1]--; b[-1] = 0; }else i = 0; t.walk.wname[i] = NULL; c->t(c, &t); break; case Topen: if(sz < 4+1) goto error; t.fid = r32(&b); t.open.mode = r08(&b); c->t(c, &t); break; case Twstat: if(sz < 4+2) goto error; t.fid = r32(&b); if((cnt = r16(&b)) > sz-4) goto error; if((err = c9parsedir(c, &t.wstat, &b, &cnt)) != 0){ c->error("s9proc"); return err; } c->t(c, &t); break; case Tcreate: if(sz < 4+2+4+1) goto error; t.fid = r32(&b); if((cnt = r16(&b)) < 1 || cnt > sz-4-2-4-1) goto error; t.create.name = (char*)b; t.create.perm = r32(&b); t.create.mode = r08(&b); t.create.name[cnt] = 0; c->t(c, &t); break; case Tflush: if(sz < 2) goto error; t.flush.oldtag = r16(&b); c->t(c, &t); break; case Tversion: if(sz < 4+2 || (msize = r32(&b)) < C9minmsize || (cnt = r16(&b)) > sz-4-2) goto error; if(cnt < 6 || memcmp(b, "9P2000", 6) != 0){ if((b = R(c, 4+2+7, Rversion, 0xffff, &err)) != NULL){ w32(&b, 0); wcs(&b, "unknown", 7); err = c->end(c); c->error("s9proc: invalid version"); } return C9Ever; } if(msize < c->msize) c->msize = msize; c->svflags |= Svver; c->t(c, &t); break; case Tattach: if(sz < 4+4+2+2) goto error; t.fid = r32(&b); t.attach.afid = r32(&b); cnt = r16(&b); sz -= 4+4+2; if(cnt+2 > sz) goto error; t.attach.uname = (char*)b; b += cnt; cnt = r16(&b); b[-2] = 0; sz -= cnt+2; if(cnt > sz) goto error; memmove(b-1, b, cnt); t.attach.aname = (char*)b; t.attach.aname[cnt] = 0; c->t(c, &t); break; case Tauth: if(sz < 4+2+2) goto error; t.auth.afid = r32(&b); cnt = r16(&b); sz -= 4+2; if(cnt+2 > sz) goto error; t.auth.uname = (char*)b; b += cnt; cnt = r16(&b); b[-2] = 0; sz -= cnt+2; if(cnt > sz) goto error; memmove(b-1, b, cnt); t.auth.aname = (char*)b; t.auth.aname[cnt] = 0; c->t(c, &t); break; default: goto error; } return 0; error: c->error("s9proc: invalid packet (type=%d)", t.type); return C9Epkt; }