#include #include #include #include #include #include "es.h" #include "qfs.h" #include "qfs-internal.h" typedef enum { // request-independent RS_IDLE = 1, RS_ID_LEN, RS_ID_BODY, RS_REQ, RS_INO_LEN, RS_INO_BODY, // lookup RS_NAME_LEN, RS_NAME_BODY, // read RS_OFF, RS_LEN, // readdir RS_COOKIE_LEN, RS_COOKIE_BODY, RS_COUNT, // none for readlink } READ_STATE; #define REQ_LOOKUP 108 // ASCII 'l' #define REQ_READ 114 // ASCII 'r' #define REQ_READDIR 100 // ASCII 'd' #define REQ_READLINK 76 // ASCII 'L' struct qfs_s { const QFS_S_OPS *ops; void *ap; READ_STATE rs; unsigned int slen; ES id; unsigned char req; ES ino; unsigned int off; unsigned int len; ES name; ES cookie; unsigned int count; QFS_S_REQ *reqs; ES resp; } ; #define MAX_REQ_STRS 2 struct qfs_s_req { QFS_S_REQ *flink; QFS_S_REQ *blink; QFS_S *s; QFS_STR id; unsigned char req; int nstr; QFS_STR strs[MAX_REQ_STRS]; int ino_x; int name_x; int cookie_x; } ; QFS_S *qfs_s_init(const QFS_S_OPS *ops, void *ap) { QFS_S *s; s = malloc(sizeof(QFS_S)); s->ops = ops; s->ap = ap; s->rs = RS_IDLE; es_init(&s->id); es_init(&s->ino); es_init(&s->name); es_init(&s->cookie); s->reqs = 0; es_init(&s->resp); return(s); } void *qfs_s_app_priv(QFS_S *s) { return(s->ap); } void qfs_s_up(const QFS_S *s) { (void)s; } const QFS_S_OPS *qfs_s_set_ops(QFS_S *s, const QFS_S_OPS *ops) { const QFS_S_OPS *o; o = s->ops; s->ops = ops; return(o); } static QFS_STR *str_for_req(ES *e, QFS_S_REQ *r, int *xp) { QFS_STR *s; if ((r->nstr < 0) || (r->nstr >= MAX_REQ_STRS)) abort(); *xp = r->nstr; s = &r->strs[r->nstr++]; s->l = es_len(e); s->s = malloc(s->l+1); bcopy(es_buf(e),s->s,s->l); s->s[s->l] = '\0'; return(s); } static void process_request(QFS_S *s) { QFS_S_REQ *req; req = malloc(sizeof(QFS_S_REQ)); req->s = s; req->id = qfs__str_copy_es(&s->id); req->req = s->req; req->nstr = 0; req->flink = s->reqs; req->blink = 0; if (req->flink) req->flink->blink = req; s->reqs = req; switch (s->req) { case REQ_LOOKUP: if (s->rs != RS_NAME_BODY) abort(); (*s->ops->lookup)(req,str_for_req(&s->ino,req,&req->ino_x),str_for_req(&s->name,req,&req->name_x)); break; case REQ_READ: if (s->rs != RS_LEN) abort(); (*s->ops->read)(req,str_for_req(&s->ino,req,&req->ino_x),s->off,s->len); break; case REQ_READDIR: if (s->rs != RS_COUNT) abort(); (*s->ops->readdir)(req,str_for_req(&s->ino,req,&req->ino_x),str_for_req(&s->cookie,req,&req->cookie_x),s->count); break; case REQ_READLINK: if (s->rs != RS_INO_BODY) abort(); (*s->ops->readlink)(req,str_for_req(&s->ino,req,&req->ino_x)); break; default: abort(); // XXX protocol error break; } s->rs = RS_IDLE; } void qfs_s_receive(QFS_S *s, const void *b, int l) { const unsigned char *bp; unsigned int *np; int dv; ES *esp; int adv; int n; printf("qfs_s_receive %d:\n",l); qfs__dump_data(b,l,stdout); bp = b; adv = 0; while (1) { bp += adv; l -= adv; adv = 0; if (l < 1) break; switch (s->rs) { case RS_IDLE: s->rs = RS_ID_LEN; s->slen = 0; /* fall through */ case RS_ID_LEN: case RS_INO_LEN: case RS_NAME_LEN: case RS_COOKIE_LEN: np = &s->slen; if (0) { case RS_OFF: np = &s->off; } if (0) { case RS_LEN: np = &s->len; } if (0) { case RS_COUNT: np = &s->count; } esp = 0; switch (*bp) { case '0': dv = 0; if (0) { case '1': dv = 1; } if (0) { case '2': dv = 2; } if (0) { case '3': dv = 3; } if (0) { case '4': dv = 4; } if (0) { case '5': dv = 5; } if (0) { case '6': dv = 6; } if (0) { case '7': dv = 7; } if (0) { case '8': dv = 8; } if (0) { case '9': dv = 9; } adv = 1; *np = (*np * 10) + dv; break; case '.': adv = 1; switch (s->rs) { case RS_ID_LEN: s->rs = RS_ID_BODY; esp = &s->id; if (0) { case RS_INO_LEN: s->rs = RS_INO_BODY; esp = &s->ino; } if (0) { case RS_NAME_LEN: s->rs = RS_NAME_BODY; esp = &s->name; } if (0) { case RS_COOKIE_LEN: s->rs = RS_COOKIE_BODY; esp = &s->cookie; } es_clear(esp); break; case RS_OFF: s->rs = RS_LEN; s->len = 0; break; case RS_LEN: case RS_COUNT: process_request(s); break; default: abort(); break; } break; default: abort(); // XXX protocol error break; } if (!esp || s->slen) break; if (0) { case RS_ID_BODY: esp = &s->id; if (0) { case RS_INO_BODY: esp = &s->ino; } if (0) { case RS_NAME_BODY: esp = &s->name; } if (0) { case RS_COOKIE_BODY: esp = &s->cookie; } n = s->slen - es_len(esp); if (n > l) n = l; if (n < 1) abort(); es_append_n(esp,bp,n); adv = n; if (es_len(esp) < s->slen) break; } s->slen = 0; switch (s->rs) { case RS_ID_BODY: s->rs = RS_REQ; break; case RS_INO_BODY: switch (s->req) { case REQ_LOOKUP: s->rs = RS_NAME_LEN; break; case REQ_READ: s->rs = RS_OFF; s->off = 0; break; case REQ_READDIR: s->rs = RS_COOKIE_LEN; break; case REQ_READLINK: process_request(s); break; default: abort(); // XXX break; } break; case RS_NAME_BODY: process_request(s); break; case RS_COOKIE_BODY: s->rs = RS_COUNT; break; default: abort(); break; } break; case RS_REQ: adv = 1; s->req = *bp; s->rs = RS_INO_LEN; s->slen = 0; break; default: abort(); break; } } } void qfs_s_error_result(QFS_S_REQ *r, int err) { ES *resp; if (err < 1) err = EIO; resp = &r->s->resp; es_clear(resp); qfs__append_str_str(resp,&r->id); switch (r->req) { case REQ_LOOKUP: case REQ_READ: case REQ_READDIR: case REQ_READLINK: es_append_1(resp,'e'); qfs__append_str_c(resp,strerror(err)); break; default: abort(); break; } (*r->s->ops->write)(r->s,es_buf(resp),es_len(resp)); } void qfs_s_lookup_result(QFS_S_REQ *r, char kind, ...) { va_list ap; const QFS_STR *ino; unsigned int mode; unsigned int size; ES *resp; resp = &r->s->resp; es_clear(resp); qfs__append_str_str(resp,&r->id); es_append_1(resp,kind); va_start(ap,kind); switch (kind) { case 'd': ino = va_arg(ap,const QFS_STR *); mode = va_arg(ap,unsigned int); qfs__append_str_str(resp,ino); qfs__append_num(resp,mode); break; case 'f': ino = va_arg(ap,const QFS_STR *); mode = va_arg(ap,unsigned int); size = va_arg(ap,unsigned int); qfs__append_str_str(resp,ino); qfs__append_num(resp,mode); qfs__append_num(resp,size); break; case 'l': ino = va_arg(ap,const QFS_STR *); qfs__append_str_str(resp,ino); break; default: abort(); break; } va_end(ap); (*r->s->ops->write)(r->s,es_buf(resp),es_len(resp)); } void qfs_s_read_result(QFS_S_REQ *r, unsigned int len, const void *data) { ES *resp; resp = &r->s->resp; es_clear(resp); qfs__append_str_str(resp,&r->id); es_append_1(resp,'g'); qfs__append_str_ptr_len(resp,data,len); (*r->s->ops->write)(r->s,es_buf(resp),es_len(resp)); } void qfs_s_readdir_result(QFS_S_REQ *r, unsigned int nent, const QFS_DIRENT *ent) { ES *resp; resp = &r->s->resp; es_clear(resp); qfs__append_str_str(resp,&r->id); es_append_1(resp,'g'); qfs__append_num(resp,nent); for (;nent>0;nent--,ent++) { qfs__append_str_str(resp,&ent->name); qfs__append_str_str(resp,&ent->ino); switch (ent->type) { case QFS_DE_DIR: es_append_1(resp,'d'); break; case QFS_DE_FILE: es_append_1(resp,'f'); break; case QFS_DE_LINK: es_append_1(resp,'l'); break; default: abort(); break; } qfs__append_str_str(resp,&ent->cookie); } (*r->s->ops->write)(r->s,es_buf(resp),es_len(resp)); } void qfs_s_readlink_result(QFS_S_REQ *r, const QFS_STR *linkto) { ES *resp; resp = &r->s->resp; es_clear(resp); qfs__append_str_str(resp,&r->id); es_append_1(resp,'g'); qfs__append_str_str(resp,linkto); (*r->s->ops->write)(r->s,es_buf(resp),es_len(resp)); }