library for ix, take three. Reference: /n/patches.lsub.org/patch/libix Date: Wed May 30 17:12:16 CES 2012 Signed-off-by: nemo@lsub.org --- /sys/src/libix Thu Jan 1 00:00:00 1970 +++ /sys/src/libix Tue May 29 16:24:42 2012 @@ -0,0 +1,226 @@ +#include +#include +#include /* dirmodefmt */ +#include + +static char* cname[CMAX] = +{ + [CEQ] "==", + [CGE] ">=", + [CGT] "> ", + [CLT] "< ", + [CLE] "<=", + [CNE] "!=", +}; + +char* ixcallname[IXTmax] = +{ + /* ix requests */ + [IXTversion] "Tversion", + [IXRversion] "Rversion", + [IXTattach] "Tattach", + [IXRattach] "Rattach", + [IXTfid] "Tfid", + [IXRfid] "Rfid", + [__IXunused__] "__IXunused__", + [IXRerror] "Rerror", + [IXTclone] "Tclone", + [IXRclone] "Rclone", + [IXTwalk] "Twalk", + [IXRwalk] "Rwalk", + [IXTopen] "Topen", + [IXRopen] "Ropen", + [IXTcreate] "Tcreate", + [IXRcreate] "Rcreate", + [IXTread] "Tread", + [IXRread] "Rread", + [IXTwrite] "Twrite", + [IXRwrite] "Rwrite", + [IXTclunk] "Tclunk", + [IXRclunk] "Rclunk", + [IXTremove] "Tremove", + [IXRremove] "Rremove", + [IXTattr] "Tattr", + [IXRattr] "Rattr", + [IXTwattr] "Twattr", + [IXRwattr] "Rwattr", + [IXTcond] "Tcond", + [IXRcond] "Rcond", + [IXTmove] "Tmove", + [IXRmove] "Rmove", +}; + +/* + * dump out count (or DUMPL, if count is bigger) bytes from + * buf to ans, as a string if they are all printable, + * else as a series of hex bytes + */ +#define DUMPL 64 + +static uint +dumpsome(char *ans, char *e, void *b, long count) +{ + int i, printable; + char *p; + char *buf; + + buf = b; + if(buf == nil){ + seprint(ans, e, ""); + return strlen(ans); + } + printable = 1; + if(count > DUMPL) + count = DUMPL; + for(i=0; i127) + printable = 0; + p = ans; + *p++ = '\''; + if(printable){ + if(count > e-p-2) + count = e-p-2; + for(; count > 0; count--, p++, buf++) + if(*buf == '\n' || *buf == '\t') + *p = ' '; + else + *p = *buf; + }else{ + if(2*count > e-p-2) + count = (e-p-2)/2; + for(i=0; i0 && i%4==0) + *p++ = ' '; + sprint(p, "%2.2ux", (uchar)buf[i]); + p += 2; + } + } + *p++ = '\''; + *p = 0; + return p - ans; +} + +/* + * Uses a buffer so prints are not mixed with other debug prints. + */ +int +ixcallfmt(Fmt *fmt) +{ + IXcall *f; + int type; + char buf[512]; + char *e, *s; + + e = buf+sizeof(buf); + f = va_arg(fmt->args, IXcall*); + type = f->type; + if(type < IXTversion || type >= IXTmax) + return fmtprint(fmt, "", type); + s = seprint(buf, e, "%s", ixcallname[type]); + switch(type){ + case IXTversion: + case IXRversion: + seprint(s, e, " msize %ud version '%s'", f->msize, f->version); + break; + break; + + case IXTattach: + seprint(s, e, " uname '%s' aname '%s'", f->uname, f->aname); + break; + case IXRattach: + seprint(s, e, " fid %d", f->fid); + break; + + case IXTfid: + seprint(s, e, " fid %ud", f->fid); + break; + case IXRfid: + break; + + case IXRerror: + seprint(s, e, " ename '%s'", f->ename); + break; + + case IXTclone: + seprint(s, e, " cflags %#x", f->cflags); + break; + case IXRclone: + seprint(s, e, " fid %d", f->fid); + break; + + case IXTwalk: + seprint(s, e, " '%s'", f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + seprint(s, e, " mode %d", f->mode); + break; + case IXRopen: + break; + + case IXTcreate: + seprint(s, e, " name '%s' perm %M mode %d", + f->name, (ulong)f->perm, f->mode); + break; + case IXRcreate: + break; + + case IXTread: + seprint(s, e, " nmsg %d offset %lld count %ud", + f->nmsg, f->offset, f->count); + break; + case IXRread: + s = seprint(s, e, " count %ud ", f->count); + dumpsome(s, e, f->data, f->count); + break; + + case IXTwrite: + s = seprint(s, e, " offset %lld endoffset %lld count %ud ", + f->offset, f->endoffset, f->count); + dumpsome(s, e, f->data, f->count); + break; + case IXRwrite: + seprint(s, e, " offset %lld count %ud", f->offset, f->count); + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + seprint(s, e, " attr '%s'", f->attr); + break; + case IXRattr: + seprint(s, e, " value '%s'", f->value); + break; + + case IXTwattr: + seprint(s, e, " attr '%s' value '%s'", f->attr, f->value); + break; + case IXRwattr: + break; + + case IXTcond: + s = "??"; + if(f->op < CMAX) + s = cname[f->op]; + s = seprint(s, e, " op '%s'", s); + seprint(s, e, " attr '%s' value '%s'", f->attr, f->value); + break; + case IXRcond: + break; + + case IXTmove: + seprint(s, e, " dirfid %d newname '%s'", f->dirfid, f->newname); + break; + case IXRmove: + break; + + } + return fmtstrcpy(fmt, buf); +} + --- /sys/src/libix Thu Jan 1 00:00:00 1970 +++ /sys/src/libix Tue May 29 16:32:29 2012 @@ -0,0 +1,322 @@ +#include +#include +#include + +#define BIT8SZ 1 +#define BIT16SZ 2 +#define BIT32SZ 4 +#define BIT64SZ 8 + +#define PBIT8(p,v) putle((p), (v), 1) +#define PBIT16(p,v) putle((p), (v), 2) +#define PBIT32(p,v) putle((p), (v), 4) +#define PBIT64(p,v) putle((p), (v), 8) + +static uchar* +pstring(uchar *p, char *s) +{ + uint n; + + if(s == nil){ + PBIT16(p, 0); + p += BIT16SZ; + return p; + } + + n = strlen(s); + /* + * We are moving the string before the length, + * so you can S2M a struct into an existing message + */ + memmove(p + BIT16SZ, s, n); + PBIT16(p, n); + p += n + BIT16SZ; + return p; +} + +static uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +/* + * Does NOT count the data bytes added past the packed + * message for IXRread, IXTwrite. This is so to save copying. + * The caller is expected to copy the final data in-place and + * adjust the total message length. + */ +uint +ixpackedsize(IXcall *f) +{ + uint n; + + n = BIT8SZ; /* type */ + + switch(f->type){ + case IXTversion: + case IXRversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case IXTattach: + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + case IXRattach: + n += BIT32SZ; + break; + + case IXTfid: + n += BIT32SZ; + break; + case IXRfid: + break; + + case IXRerror: + n += stringsz(f->ename); + break; + + case IXTclone: + n += BIT8SZ; + break; + case IXRclone: + n += BIT32SZ; + break; + + case IXTwalk: + n += stringsz(f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + n += BIT8SZ; + break; + case IXRopen: + break; + + case IXTcreate: + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + case IXRcreate: + break; + + case IXTread: + n += BIT16SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + case IXRread: + /* data follows; not counted */ + break; + + case IXTwrite: + n += BIT64SZ; + n += BIT64SZ; + /* data follows; not counted */ + break; + case IXRwrite: + n += BIT64SZ; + n += BIT32SZ; + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + n += stringsz(f->attr); + break; + case IXRattr: + n += stringsz(f->value); + break; + + case IXTwattr: + n += stringsz(f->attr); + n += stringsz(f->value); + break; + case IXRwattr: + break; + + case IXTcond: + n += BIT8SZ; + n += stringsz(f->attr); + n += stringsz(f->value); + break; + case IXRcond: + break; + + case IXTmove: + n += BIT32SZ; + n += stringsz(f->newname); + break; + case IXRmove: + break; + + default: + sysfatal("packedsize: unknown type %d", f->type); + + } + return n; +} + +uint +ixpack(IXcall *f, uchar *ap, uint nap) +{ + uchar *p; + uint size; + + size = ixpackedsize(f); + if(size == 0 || size > nap) + return 0; + + p = (uchar*)ap; + + PBIT8(p, f->type); + p += BIT8SZ; + + switch(f->type){ + case IXTversion: + case IXRversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case IXTattach: + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + case IXRattach: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case IXTfid: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + case IXRfid: + break; + + case IXRerror: + p = pstring(p, f->ename); + break; + + case IXTclone: + PBIT8(p, f->cflags); + p += BIT8SZ; + break; + case IXRclone: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case IXTwalk: + p = pstring(p, f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + PBIT8(p, f->mode); + p += BIT8SZ; + break; + case IXRopen: + break; + + case IXTcreate: + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + case IXRcreate: + break; + + case IXTread: + PBIT16(p, f->nmsg); + p += BIT16SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + case IXRread: + /* data follows; not packed */ + break; + + case IXTwrite: + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT64(p, f->endoffset); + p += BIT64SZ; + /* data follows; not packed */ + break; + case IXRwrite: + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + p = pstring(p, f->attr); + break; + case IXRattr: + p = pstring(p, f->value); + break; + + case IXTwattr: + p = pstring(p, f->attr); + p = pstring(p, f->value); + break; + case IXRwattr: + break; + + case IXTcond: + if(f->op >= CMAX){ + werrstr("unknown cond op"); + return 0; + } + PBIT8(p, f->op); + p += BIT8SZ; + p = pstring(p, f->attr); + p = pstring(p, f->value); + break; + case IXRcond: + break; + + case IXTmove: + PBIT32(p, f->dirfid); + p += BIT32SZ; + p = pstring(p, f->newname); + break; + case IXRmove: + break; + + default: + sysfatal("pack: type %d", f->type); + + } + if(size != p-ap) + return 0; + return size; +} + --- /sys/src/libix Thu Jan 1 00:00:00 1970 +++ /sys/src/libix Tue May 29 16:34:19 2012 @@ -0,0 +1,218 @@ +#include +#include +#include + +#define BIT8SZ 1 +#define BIT16SZ 2 +#define BIT32SZ 4 +#define BIT64SZ 8 + +#define GBIT8(p) getle((p), 1) +#define GBIT16(p) getle((p), 2) +#define GBIT32(p) getle((p), 4) +#define GBIT64(p) getle((p), 8) + +static uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p == nil || p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ - 1; + if(p+n+1 > ep) + return nil; + /* move it down, on top of count, to make room for '\0' */ + memmove(p, p + 1, n); + p[n] = '\0'; + *s = (char*)p; + p += n+1; + return p; +} + +uint +ixunpack(uchar *ap, uint nap, IXcall *f) +{ + uchar *p, *ep; + + p = ap; + ep = p + nap; + + if(p+BIT8SZ > ep){ + werrstr("msg too short"); + return 0; + } + + f->type = GBIT8(p); + p += BIT8SZ; + + switch(f->type){ + case IXTversion: + case IXRversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case IXTattach: + p = gstring(p, ep, &f->uname); + if(p == nil) + return 0; + p = gstring(p, ep, &f->aname); + break; + case IXRattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case IXTfid: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + case IXRfid: + break; + + case IXRerror: + p = gstring(p, ep, &f->ename); + break; + + case IXTclone: + if(p+BIT8SZ > ep) + return 0; + f->cflags = GBIT8(p); + p += BIT8SZ; + break; + case IXRclone: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case IXTwalk: + p = gstring(p, ep, &f->wname); + break; + case IXRwalk: + break; + + case IXTopen: + if(p+BIT8SZ > ep) + return 0; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + case IXRopen: + break; + + case IXTcreate: + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + case IXRcreate: + break; + + case IXTread: + if(p+BIT16SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->nmsg = GBIT16(p); + p += BIT16SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + case IXRread: + f->data = p; + f->count = ep - p; + break; + + case IXTwrite: + if(p+BIT64SZ > ep) + return 0; + f->offset = GBIT64(p); + p += BIT64SZ; + f->endoffset = GBIT64(p); + p += BIT64SZ; + f->data = p; + f->count = ep - p; + break; + case IXRwrite: + if(p+BIT32SZ+BIT64SZ > ep) + return 0; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case IXTclunk: + case IXRclunk: + case IXTremove: + case IXRremove: + break; + + case IXTattr: + p = gstring(p, ep, &f->attr); + break; + case IXRattr: + p = gstring(p, ep, &f->value); + break; + + case IXTwattr: + p = gstring(p, ep, &f->attr); + p = gstring(p, ep, &f->value); + break; + case IXRwattr: + break; + + case IXTcond: + if(p+BIT8SZ > ep) + return 0; + f->op = GBIT8(p); + if(f->op >= CMAX){ + werrstr("unknown cond op"); + return 0; + } + p += BIT8SZ; + p = gstring(p, ep, &f->attr); + p = gstring(p, ep, &f->value); + break; + case IXRcond: + break; + + case IXTmove: + if(p+BIT32SZ > ep) + return 0; + f->dirfid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->newname); + break; + case IXRmove: + break; + + default: + werrstr("unpack: unknown type %d", f->type); + return 0; + } + + if(p==nil || p>ep || p == ap){ + werrstr("unpack: p %#p ep %#p", p, ep); + return 0; + } + return p - ap; +} + --- /sys/src/libix Thu Jan 1 00:00:00 1970 +++ /sys/src/libix Tue May 29 16:23:27 2012 @@ -0,0 +1,17 @@ + +.br +.B #include +.br +.br +.B #include +.PP +.B +int ixcallfmt(Fmt *fmt) +.PP +.B +uint ixpack(IXcall *f, uchar *ap, uint nap) +.PP +.B +uint ixpackedsize(IXcall *f) +.PP +.B +uint ixunpack(uchar *ap, uint nap, IXcall *f) +.PP +.B +char *ixcallname[] +.SH DESCRIPTION +These +routines convert messages in the machine-independent format of +the IX file protocol to and from a more convenient form, +an +.B IXcall +structure: +.PP +.EX +.if n .ta 4n +6n +5n +6n +18n +4n +.if t .ta \w'xxxx'u +\w'short 'u +\w'xxxx'u +\w'ushort 'u +\w'ticket[TICKETLEN]; 'u +\w'/* 'u +#define MAXWELEM 16 + +typedef struct IXcall IXcall; +struct IXcall +{ + uchar type; + union{ + struct{ /* Tversion, Rversion */ + u32int msize; + char *version; + }; + struct{ + u32int fid; /* Tfid, Rattach, Rclone */ + }; + struct{ + char *uname; /* Tattach */ + char *aname; /* Tattach */ + }; + struct{ + char *ename; /* Rerror */ + }; + struct{ + uchar cflags; /* Tclone (OCEND|OCERR) */ + }; + struct{ + uchar mode; /* Topen, Tcreate */ + u32int perm; /* Tcreate */ + char *name; /* Tcreate */ + }; + struct{ /* Twalk */ + char *wname; + }; + struct{ + u16int nmsg; /* Tread */ + uvlong offset; /* Tread, Twrite, Rwrite */ + uvlong endoffset; /* Twrite */ + u32int count; /* Tread, Rread, Twrite, Rwrite */ + uchar *data; /* Twrite, Rread */ + }; + struct{ + uchar op; /* Tcond */ + char *attr; /* Tattr, Twattr, Tcond */ + char *value; /* Rattr, Twattr, Tcond */ + }; + struct{ /* Tmove */ + u32int dirfid; + char *newname; + }; + /* With struct{}: + * Rfid, + * Rwalk, Ropen, Rcreate, Tclunk, Rclunk, Tclose, Rclose, + * Tremove, Rremove, Rwattr, Rcond, Rmove + */ + }; +}; +.EE +.PP +The protocol is described elsewhere (see also the description in the standard header file). +The function +.I ixcallfmt +can be used with +.IR fmtinstall (2) +to print IX calls. +.PP +.I Ixpack +packs into the buffer +.I ap +the call pointed to by +.I f +using at most +.I nap +bytes. +It does not append data bytes for +.B IXTwrite +and +.B IXRread +calls. +The caller is expected to append those. +.PP +.I Ixpackedsize +returns the size of the call when packed. It does not count the data bytes +for +.B IXTwrite +and +.B IXRread +calls. +.PP +.I Ixunpack +unpacks from the buffer starting at +.I ap +with +.I nap +bytes an IX message into the +.I f +structure. +.PP +All routines follow conventions similar to those in +.IR fcall (2). +.SS Protocol +The protocol packs requests within transport channels. +It is similar to 9P, described in +.IR intro (5). +Here we focus on the differences. +.PP +The format in the wire is +.EX + len[2] tag[2] msg[] +.EE +.PP +.I Len +is the number of bytes of the message and +.I tag +is a channel number. A channel groups requests together and is a duplex +pipe sharing the transport with other channels. An error on a request implies +ignoring further requests in the same channel. +.PP +The first message in a channel must set the Tfirst bit in the tag, +the last one must set the Tlast bit. Both may be set in the same message. +.EX + enum{Tfirst = 0x8000, Tlast = 0x4000}; +.EE +.PP +A channel is released when both directions have +exchanged messages with the Tlast bit set. +An error reply always has Tlast set. +.PP +Authentication is fully left out of the protocol. +The underlying transport must be secured in a way that +provides trust between both parties. +.PP +There is no flush request. Flushing is achieved by aborting a channel. To abort +a channel send a (perhaps empty) message with the Tlast bit set (after the last message +has been sent). +.PP +These flags are used to release a fid upon errors or after the last request: +.EX +enum +{ + OCEND = 0x4, /* clunk on end of rpc */ + OCERR = 0x8, /* clunk on error */ +}; +.EE +.PP +These values correspond to operations in the +.I Tcond +request. +.EX +enum +{ + CEQ = 0, /* Tcond.op */ + CGE, + CGT, + CLE, + CLT, + CNE, + CMAX, +}; +.EE +.PP +Fids are chosen by the server, not by the client. In particular, +.I Tattach +and +.I Tclone +allocate new fids. Both of them, and +.IR Tfid , +set the fid conveyed in the request as implicit for further requests in the same channel. +Most requests do not carry a fid value, but use the implicit fid instead. +.PP +This is the set of individual requests or messages: +.EX + IXTversion msize[4] version[s] + IXRversion msize[4] version[s] + IXTattach uname[s] aname[s] + IXRattach fid[4] // fid becomes the current fid + IXTfid fid[4] + IXRfid + IXRerror ename[s] + IXTclone cflags[1] + IXRclone fid[4] // fid becomes the current fid + IXTwalk wname[s] + IXRwalk + IXTopen mode[1] // mode includes cflags + IXRopen + IXTcreate name[s] perm[4] mode[1] // mode includes cflags + IXRcreate + IXTread nmsg[2] offset[8] count[4] + IXRread data[] + IXTwrite offset[8] endoffset[8] data[] + IXRwrite offset[8] count[4] + IXTclunk + IXRclunk + IXTremove + IXRremove + IXTattr attr[s] //"*" : return attr name list + IXRattr value[s] + IXTwattr attr[s] value[s] + IXRwattr + IXTcond op[1] attr[s] value[s] + IXRcond + IXTmove dirfid[4] newname[s] + IXRmove +.EE +.SH SOURCE +.B /sys/src/libix +.SH SEE ALSO +.IR intro (2), +.IR fcall (2). +.SH BUGS +Must document the protocol.