#include #include #include #define __STDC_FORMAT_MACROS #include #include "pi9.h" #include #define VERBOSE false #define MAXWELEM 16 static const uint16_t NOTAG = (uint16_t)~0; static const uint32_t HDRSZ = 7; // size of header static const uint32_t QIDSZ = 13; // size of serialized pi9_qid static const uint32_t STATHDRSZ = 47; // size of serialized pi9_stat, until the variable length data struct pi9_stream { struct chck_buffer out, in; }; enum op { OPFIRST, Tversion = 0x64, Rversion, Tauth = 0x66, Rauth, Tattach = 0x68, Rattach, Terror = 0x6A, // illegal Rerror, Tflush = 0x6C, Rflush, Twalk = 0x6E, Rwalk, Topen = 0x70, Ropen, Tcreate = 0x72, Rcreate, Tread = 0x74, Rread, Twrite = 0x76, Rwrite, Tclunk = 0x78, Rclunk, Tremove = 0x7A, Rremove, Tstat = 0x7C, Rstat, Twstat = 0x7E, Rwstat, OPLAST, }; #define DECOP(x) static bool op_##x(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) DECOP(Tversion); DECOP(Tauth); DECOP(Tattach); DECOP(Tflush); DECOP(Twalk); DECOP(Topen); DECOP(Tcreate); DECOP(Tread); DECOP(Twrite); DECOP(Tclunk); DECOP(Tremove); DECOP(Tstat); DECOP(Twstat); #undef DECOP static struct { size_t msz; bool (*cb)(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream); } ops[] = { [Tversion] = { 6, op_Tversion }, [Tauth] = { 8, op_Tauth }, [Terror] = { 0, NULL }, // invalid [Tflush] = { 2, op_Tflush }, [Tattach] = { 12, op_Tattach }, [Twalk] = { 10, op_Twalk }, [Topen] = { 5, op_Topen }, [Tcreate] = { 11, op_Tcreate }, [Tread] = { 16, op_Tread }, [Twrite] = { 16, op_Twrite }, [Tclunk] = { 4, op_Tclunk }, [Tremove] = { 4, op_Tremove }, [Tstat] = { 4, op_Tstat }, [Twstat] = { 6, op_Twstat }, }; static const struct { const char *msg; size_t size; } errors[PI9_ERR_LAST] = { #define MSG(x) { x, sizeof(x) } MSG("Could not read message."), MSG("Could not write message."), MSG("Authentication unnecessary."), MSG("File is not a directory."), MSG("No such file or directory."), MSG("Fid already in user."), MSG("Operation not allowed."), MSG("Unknown op code."), MSG("Out of memory."), #undef MSG }; static inline bool write_qid(struct pi9_qid *qid, struct pi9_stream *stream) { assert(qid && stream); return (chck_buffer_write_int(&qid->type, sizeof(qid->type), &stream->out) && chck_buffer_write_int(&qid->vers, sizeof(qid->vers), &stream->out) && chck_buffer_write_int(&qid->path, sizeof(qid->path), &stream->out)); } static inline bool read_qid(struct pi9_qid *qid, struct pi9_stream *stream) { assert(qid && stream); return (chck_buffer_read_int(&qid->type, sizeof(qid->type), &stream->in) && chck_buffer_read_int(&qid->vers, sizeof(qid->vers), &stream->in) && chck_buffer_read_int(&qid->path, sizeof(qid->path), &stream->in)); } static inline bool read_stat(struct pi9_stat *stat, struct pi9_stream *stream) { assert(stat && stream); uint16_t size; if (!chck_buffer_read_int(&size, sizeof(size), &stream->in) || !chck_buffer_read_int(&stat->type, sizeof(stat->type), &stream->in) || !chck_buffer_read_int(&stat->dev, sizeof(stat->dev), &stream->in) || !read_qid(&stat->qid, stream) || !chck_buffer_read_int(&stat->mode, sizeof(stat->mode), &stream->in) || !chck_buffer_read_int(&stat->atime, sizeof(stat->atime), &stream->in) || !chck_buffer_read_int(&stat->mtime, sizeof(stat->mtime), &stream->in) || !chck_buffer_read_int(&stat->length, sizeof(stat->length), &stream->in)) return false; struct pi9_string *fields[4] = { &stat->name, &stat->uid, &stat->gid, &stat->muid }; for (uint32_t i = 0; i < 4; ++i) { uint16_t len; if (!chck_buffer_read_int(&len, sizeof(len), &stream->in)) return false; pi9_string_set_cstr_with_length(fields[i], (char*)stream->in.curpos, len, false); } return true; } static bool op_Tversion(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { // tag should always be NOTAG in version messages if (tag != NOTAG) goto err_not_allowed; uint32_t msize; uint16_t vsize; if (!chck_buffer_read_int(&msize, sizeof(msize), &stream->in) || !chck_buffer_read_int(&vsize, sizeof(vsize), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tversion %u %u\n", tag, msize); #endif struct pi9_string version = {0}; pi9_string_set_cstr_with_length(&version, (char*)stream->in.curpos, vsize, false); // A successful version request initializes the connection. // All outstanding I/O on the connection is aborted; all active fids are freed (`clunked') automatically. // The set of messages between version requests is called a session. // FIXME: ↑ we need to tell higher level to abort all outstanding I/O // honor the buffer size request pi9->msize = (msize > 0 ? msize : pi9->msize); // only support 9P2000, maybe 9P2000.L later, .u probably never if (!pi9_string_eq_cstr(&version, "9P2000")) { static const char *preferred = "9P2000"; const char *reply = (pi9_string_starts_with_cstr(&version, "9P") ? preferred : "unknown"); vsize = strlen(reply); const uint32_t size = HDRSZ + sizeof(msize) + sizeof(vsize) + vsize; if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rversion}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || !chck_buffer_write_int(&msize, sizeof(msize), &stream->out) || !chck_buffer_write_string_of_type(reply, vsize, sizeof(uint16_t), &stream->out)) goto err_write; } else { const size_t size = HDRSZ + sizeof(msize) + sizeof(vsize) + vsize; if (chck_buffer_write(stream->in.buffer, 1, size, &stream->out) != size) goto err_write; *(uint8_t*)(stream->in.buffer + sizeof(uint32_t)) = Rversion; } return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; err_not_allowed: pi9_write_error(tag, PI9_ERR_NOT_ALLOWED, stream); return false; } static bool op_Tauth(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint32_t afid; if (!chck_buffer_read_int(&afid, sizeof(afid), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tauth %u %u\n", tag, afid); #endif uint16_t usize; if (!chck_buffer_read_int(&usize, sizeof(usize), &stream->in)) goto err_read; struct pi9_string uname = {0}; pi9_string_set_cstr_with_length(&uname, (char*)stream->in.curpos, usize, false); chck_buffer_seek(&stream->in, usize, SEEK_CUR); uint16_t asize; if (!chck_buffer_read_int(&asize, sizeof(asize), &stream->in)) goto err_read; struct pi9_string aname = {0}; pi9_string_set_cstr_with_length(&aname, (char*)stream->in.curpos, asize, false); struct pi9_qid *qid = NULL; if (pi9->procs.auth && !pi9->procs.auth(pi9, tag, afid, &uname, &aname, &qid)) return false; if (!qid) goto err_no_auth; const uint32_t size = HDRSZ + QIDSZ; if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rauth}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || (qid && !write_qid(qid, stream))) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; err_no_auth: pi9_write_error(tag, PI9_ERR_NO_AUTH, stream); return false; } static bool op_Tattach(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint32_t fid, afid; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in) || !chck_buffer_read_int(&afid, sizeof(afid), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tattach %u %u %u\n", tag, fid, afid); #endif uint16_t usize; if (!chck_buffer_read_int(&usize, sizeof(usize), &stream->in)) goto err_read; struct pi9_string uname = {0}; pi9_string_set_cstr_with_length(&uname, (char*)stream->in.curpos, usize, false); chck_buffer_seek(&stream->in, usize, SEEK_CUR); uint16_t asize; if (!chck_buffer_read_int(&asize, sizeof(asize), &stream->in)) goto err_read; struct pi9_string aname = {0}; pi9_string_set_cstr_with_length(&aname, (char*)stream->in.curpos, asize, false); struct pi9_qid *qid = NULL; if (pi9->procs.attach && !pi9->procs.attach(pi9, tag, fid, afid, &uname, &aname, &qid)) return false; const uint32_t size = HDRSZ + QIDSZ; if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rattach}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || !write_qid(qid, stream)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static bool op_Tflush(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint16_t oldtag; if (!chck_buffer_read_int(&oldtag, sizeof(oldtag), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tflush %u %u\n", tag, oldtag); #endif if (pi9->procs.flush && !pi9->procs.flush(pi9, tag, oldtag)) return false; // The server should answer the flush message immediately. // If it recognizes oldtag as the tag of a pending transaction, it should abort any pending response and discard that tag. // In either case, it should respond with an Rflush echoing the tag (not oldtag) of the Tflush message. // A Tflush can never be responded to by an Rerror message. if (!chck_buffer_write_int(&HDRSZ, sizeof(HDRSZ), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rflush}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static bool op_Twalk(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint16_t nwname; uint32_t fid, newfid; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in) || !chck_buffer_read_int(&newfid, sizeof(newfid), &stream->in) || !chck_buffer_read_int(&nwname, sizeof(nwname), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Twalk %u %u %u %u\n", tag, fid, newfid, nwname); #endif if (nwname > MAXWELEM) goto err_not_allowed; struct pi9_string walks[MAXWELEM] = {{0}}; for (uint32_t i = 0; i < nwname; ++i) { uint16_t len; if (!chck_buffer_read_int(&len, sizeof(len), &stream->in)) goto err_read; pi9_string_set_cstr_with_length(&walks[i], (char*)stream->in.curpos, len, false); } uint16_t nwqid = 0; struct pi9_qid *qids[MAXWELEM]; if (pi9->procs.walk && !pi9->procs.walk(pi9, tag, fid, newfid, nwname, walks, qids, &nwqid)) return false; // must be always less or equal assert(nwqid <= nwname); const uint32_t size = HDRSZ + sizeof(nwqid) + nwqid * QIDSZ; if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rwalk}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || !chck_buffer_write_int(&nwqid, sizeof(nwqid), &stream->out)) goto err_write; for (uint32_t i = 0; i < nwqid; ++i) { if (!write_qid(qids[i], stream)) goto err_write; } return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; err_not_allowed: pi9_write_error(tag, PI9_ERR_NOT_ALLOWED, stream); return false; } static bool op_Topen(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint8_t mode; uint32_t fid; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in) || !chck_buffer_read_int(&mode, sizeof(mode), &stream->in)) goto err_read; // All other bits in mode should be zero // ↑ Means that I should validate the mode for unknown bits to p9 protocol #if VERBOSE fprintf(stderr, "Topen %u %u %u\n", tag, fid, mode); #endif uint32_t iounit = 0; struct pi9_qid *qid = NULL; if (pi9->procs.open && !pi9->procs.open(pi9, tag, fid, mode, &qid, &iounit)) return false; if (!qid) goto err_not_allowed; const uint32_t size = HDRSZ + QIDSZ + sizeof(iounit); if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Ropen}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || !write_qid(qid, stream) || !chck_buffer_write_int(&iounit, sizeof(iounit), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; err_not_allowed: pi9_write_error(tag, PI9_ERR_NOT_ALLOWED, stream); return false; } static bool op_Tcreate(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint32_t fid; uint16_t nsize; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in) || !chck_buffer_read_int(&nsize, sizeof(nsize), &stream->in)) goto err_read; struct pi9_string name = {0}; pi9_string_set_cstr_with_length(&name, (char*)stream->in.curpos, nsize, false); uint8_t mode; uint32_t perm; if (!chck_buffer_read_int(&perm, sizeof(perm), &stream->in) || !chck_buffer_read_int(&mode, sizeof(mode), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tcreate %u %u\n", tag, fid); #endif // The names . and .. are special; it is illegal to create files with these names. if (pi9_string_eq_cstr(&name, ".") || pi9_string_eq_cstr(&name, "..")) goto err_not_allowed; uint32_t iounit = 0; struct pi9_qid *qid = NULL; if (pi9->procs.create && !pi9->procs.create(pi9, tag, fid, &name, perm, mode, &qid, &iounit)) return false; if (!qid) goto err_not_allowed; const uint32_t size = HDRSZ + QIDSZ + sizeof(iounit); if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rcreate}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || !write_qid(qid, stream) || !chck_buffer_write_int(&iounit, sizeof(iounit), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; err_not_allowed: pi9_write_error(tag, PI9_ERR_NOT_ALLOWED, stream); return false; } static bool op_Tread(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint64_t offset; uint32_t fid, count; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in) || !chck_buffer_read_int(&offset, sizeof(offset), &stream->in) || !chck_buffer_read_int(&count, sizeof(count), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tread %u %u %"PRIu64" %u\n", tag, fid, offset, count); #endif if (pi9->procs.read && !pi9->procs.read(pi9, tag, fid, offset, count)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static bool op_Twrite(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint64_t offset; uint32_t fid, count; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in) || !chck_buffer_read_int(&offset, sizeof(offset), &stream->in) || !chck_buffer_read_int(&count, sizeof(count), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Twrite %u %"PRIu64" %u\n", fid, offset, count); #endif if (pi9->procs.write && !pi9->procs.write(pi9, tag, fid, offset, count, (count > 0 ? stream->in.curpos : NULL))) return false; const uint32_t size = HDRSZ + sizeof(count); if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rwrite}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || !chck_buffer_write_int(&count, sizeof(count), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static bool op_Tclunk(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint32_t fid; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tclunk %u %u\n", tag, fid); #endif if (pi9->procs.clunk && !pi9->procs.clunk(pi9, tag, fid)) return false; if (!chck_buffer_write_int(&HDRSZ, sizeof(HDRSZ), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rclunk}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static bool op_Tremove(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint32_t fid; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tremove %u %u\n", tag, fid); #endif if (pi9->procs.remove && !pi9->procs.remove(pi9, tag, fid)) return false; if (!chck_buffer_write_int(&HDRSZ, sizeof(HDRSZ), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rremove}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static bool op_Tstat(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint32_t fid; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Tstat %u %u\n", tag, fid); #endif chck_buffer_seek(&stream->out, HDRSZ + sizeof(uint16_t), SEEK_SET); uint8_t *start = stream->out.curpos; struct pi9_stat *stat = NULL; if (pi9->procs.stat && !pi9->procs.stat(pi9, tag, fid, &stat)) return false; if (stat && !pi9_write_stat(stat, stream)) goto err_write; // too big if (stream->out.curpos - start > 0xFFFF) goto err_write; const uint16_t sbufsz = (stream->out.curpos - start); const uint32_t size = HDRSZ + sizeof(sbufsz) + sbufsz; chck_buffer_seek(&stream->out, 0, SEEK_SET); if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rstat}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out) || !chck_buffer_write_int(&sbufsz, sizeof(sbufsz), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static bool op_Twstat(struct pi9 *pi9, uint16_t tag, struct pi9_stream *stream) { uint32_t fid; uint16_t sbufsz; if (!chck_buffer_read_int(&fid, sizeof(fid), &stream->in) || !chck_buffer_read_int(&sbufsz, sizeof(sbufsz), &stream->in)) goto err_read; #if VERBOSE fprintf(stderr, "Twstat %u %u %u\n", tag, fid, sbufsz); #endif struct pi9_stat stat = {0}; if (!read_stat(&stat, stream)) goto err_read; if (pi9->procs.twstat && !pi9->procs.twstat(pi9, tag, fid, &stat)) return false; if (!chck_buffer_write_int(&HDRSZ, sizeof(HDRSZ), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rwstat}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&tag, sizeof(tag), &stream->out)) goto err_write; return true; err_read: pi9_write_error(tag, PI9_ERR_READ, stream); return false; err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; } static inline bool call_op(struct pi9 *pi9, enum op op, uint16_t tag, struct pi9_stream *stream) { // check opcode range, and only allow T opcodes (% 2), also check that message meets minimum size if (op <= OPFIRST || op >= OPLAST || op % 2 != 0 || stream->in.size < ops[op].msz) goto err_unknown_op; if (op == Terror) { #if VERBOSE fprintf(stderr, "Got error from the client.\n"); #endif return true; } if (stream->out.size < ops[op].msz && !chck_buffer_resize(&stream->out, ops[op].msz)) goto err_write; return ops[op].cb(pi9, tag, stream); err_write: pi9_write_error(tag, PI9_ERR_WRITE, stream); return false; err_unknown_op: pi9_write_error(tag, PI9_ERR_UNKNOWN_OP, stream); return false; } static bool read_msg(struct pi9 *pi9, int fd, struct pi9_stream *stream) { assert(pi9 && fd >= 0); if (chck_buffer_fill_from_fd(fd, 1, pi9->msize, &stream->in) < HDRSZ) return false; uint32_t size; if (!chck_buffer_read_int(&size, sizeof(size), &stream->in) || size < HDRSZ) return false; if (stream->in.size < size) { const size_t to_read = size - stream->in.size; chck_buffer_seek(&stream->in, 0, SEEK_END); if (chck_buffer_fill_from_fd(fd, 1, to_read, &stream->in) < to_read) return false; chck_buffer_seek(&stream->in, sizeof(size), SEEK_SET); } uint8_t op; uint16_t tag; if (!chck_buffer_read_int(&op, sizeof(uint8_t), &stream->in) || !chck_buffer_read_int(&tag, sizeof(uint16_t), &stream->in)) return false; #if VERBOSE fprintf(stderr, "Read message of size: %u (%u : %u)\n", size, op, tag); for (uint32_t i = 0; i < size; ++i) putc(*(char*)(stream->in.buffer + i), stderr); putc('\n', stderr); #endif return call_op(pi9, op, tag, stream); } static inline bool write_msg(int fd, struct pi9_stream *stream) { assert(fd >= 0 && stream->out.buffer); if ((size_t)(stream->out.curpos - stream->out.buffer) < sizeof(uint32_t)) return true; const uint32_t size = *(uint32_t*)stream->out.buffer; #if VERBOSE fprintf(stderr, "Write message of size: %u\n", size); #endif return write(fd, stream->out.buffer, size) == size; } void pi9_reply_start(struct pi9_reply *reply, uint16_t tag, struct pi9_stream *stream) { assert(reply && stream); memset(reply, 0, sizeof(struct pi9_reply)); chck_buffer_seek(&stream->out, HDRSZ + sizeof(uint32_t), SEEK_SET); reply->start = stream->out.curpos; reply->tag = tag; } bool pi9_reply_send(struct pi9_reply *reply, int fd, struct pi9_stream *stream) { assert(reply && stream); const uint32_t sbufsz = (stream->out.curpos - reply->start); const uint32_t size = HDRSZ + sizeof(sbufsz) + sbufsz; chck_buffer_seek(&stream->out, 0, SEEK_SET); if (!chck_buffer_write_int(&size, sizeof(size), &stream->out) || !chck_buffer_write_int((uint8_t[]){Rread}, sizeof(uint8_t), &stream->out) || !chck_buffer_write_int(&reply->tag, sizeof(reply->tag), &stream->out) || !chck_buffer_write_int(&sbufsz, sizeof(sbufsz), &stream->out)) return false; return (stream->in.buffer == stream->in.curpos ? write_msg(fd, stream) : true); } bool pi9_write_stat(struct pi9_stat *stat, struct pi9_stream *stream) { const size_t size = STATHDRSZ + stat->name.size + stat->uid.size + stat->gid.size + stat->muid.size; // too big if (size > 0xFFFF) return false; const uint16_t size16 = size; return (chck_buffer_write_int(&size16, sizeof(size16), &stream->out) && chck_buffer_write_int(&stat->type, sizeof(stat->type), &stream->out) && chck_buffer_write_int(&stat->dev, sizeof(stat->dev), &stream->out) && write_qid(&stat->qid, stream) && chck_buffer_write_int(&stat->mode, sizeof(stat->mode), &stream->out) && chck_buffer_write_int(&stat->atime, sizeof(stat->atime), &stream->out) && chck_buffer_write_int(&stat->mtime, sizeof(stat->mtime), &stream->out) && chck_buffer_write_int(&stat->length, sizeof(stat->length), &stream->out) && chck_buffer_write_string_of_type(stat->name.data, stat->name.size, sizeof(uint16_t), &stream->out) && chck_buffer_write_string_of_type(stat->uid.data, stat->uid.size, sizeof(uint16_t), &stream->out) && chck_buffer_write_string_of_type(stat->gid.data, stat->gid.size, sizeof(uint16_t), &stream->out) && chck_buffer_write_string_of_type(stat->muid.data, stat->muid.size, sizeof(uint16_t), &stream->out)); } size_t pi9_write(const void *src, size_t size, size_t nmemb, struct pi9_stream *stream) { return chck_buffer_write(src, size, nmemb, &stream->out); } void pi9_stat_release(struct pi9_stat *stat) { if (!stat) return; struct pi9_string *fields[4] = { &stat->name, &stat->uid, &stat->gid, &stat->muid }; for (uint32_t i = 0; i < 4; ++i) pi9_string_release(fields[i]); } bool pi9_process(struct pi9 *pi9, int fd) { assert(pi9 && fd >= 0 && pi9->stream); pi9->fd = fd; bool ret = true; if (!read_msg(pi9, fd, pi9->stream)) { if (pi9->stream->out.curpos == pi9->stream->out.buffer) pi9_write_error(NOTAG, PI9_ERR_READ, pi9->stream); ret = false; } if (!write_msg(fd, pi9->stream)) { pi9_write_error(NOTAG, PI9_ERR_WRITE, pi9->stream); write_msg(fd, pi9->stream); ret = false; } chck_buffer_seek(&pi9->stream->in, 0, SEEK_SET); chck_buffer_seek(&pi9->stream->out, 0, SEEK_SET); pi9->fd = -1; return ret; } void pi9_release(struct pi9 *pi9) { if (!pi9) return; chck_buffer_release(&pi9->stream->out); chck_buffer_release(&pi9->stream->in); free(pi9->stream); memset(pi9, 0, sizeof(struct pi9)); } bool pi9_init(struct pi9 *pi9, uint32_t msize, struct pi9_procs *procs, void *userdata) { assert(pi9); memset(pi9, 0, sizeof(struct pi9)); memcpy(&pi9->procs, procs, sizeof(struct pi9_procs)); pi9->msize = (msize > 0 ? msize : 8192); pi9->userdata = userdata; pi9->fd = -1; if (!(pi9->stream = calloc(1, sizeof(struct pi9_stream)))) goto fail; if (!chck_buffer(&pi9->stream->in, pi9->msize, CHCK_ENDIANESS_LITTLE)) goto fail; if (!chck_buffer(&pi9->stream->out, pi9->msize, CHCK_ENDIANESS_LITTLE)) goto fail; return true; fail: pi9_release(pi9); return false; } #undef pi9_write_error void pi9_write_error(uint16_t tag, enum pi9_error error, struct pi9_stream *stream) { assert(stream); const int32_t size = HDRSZ + sizeof(uint16_t) + errors[error].size; chck_buffer_seek(&stream->out, 0, SEEK_SET); chck_buffer_write_int(&size, sizeof(size), &stream->out); chck_buffer_write_int((uint8_t[]){ Rerror }, sizeof(uint8_t), &stream->out); chck_buffer_write_int(&tag, sizeof(tag), &stream->out); chck_buffer_write_string_of_type(errors[error].msg, errors[error].size, sizeof(uint16_t), &stream->out); fprintf(stderr, "%s\n", errors[error].msg); }