U0 @nt_Rerror(@nt* nt, U8* data, U8* errormsg) { // size[4] Rerror tag[2] ename[s] SET_MSGS Rmsg->length = (4 + 1 + 2 + 2); nt->out[4] = TERROR + 1; // RERROR MemCpy(nt->out + (4 + 1 + 2 + 2), errormsg, StrLen(errormsg)); *(nt->out + (4 + 1 + 2))(U16*) = StrLen(errormsg); SET_RESPONSE_TAG Rmsg->length += StrLen(errormsg); @9p_debug_dump(Rmsg, Rmsg->length, 1); nt->s->send(Rmsg, Rmsg->length); } U0 @nt_Tversion(@nt* nt, U8* data) { SET_MSGS // Special case; just increment Tmsg tag to Rmsg and echo back to client data[4]++; @9p_debug_dump(data, Tmsg->length, 1); nt->s->send(data, Tmsg->length); } U0 @nt_Tattach(@nt* nt, U8* data) { SET_MSGS @nt_zrmsg(nt, 20); //@nt_tx_dbg(nt, "Tattach fid: %d", Tmsg->fid); nt->attach_fid = Tmsg->fid; // Map fid to qid U8 fidbuf[16]; @9p_qid * qid = @nt_lookup_qid_by_path(nt, "/"); StrPrint(fidbuf, "%d", Tmsg->fid); // nt->fids->set(fidbuf, qid, JSON_NUMBER); @nt_fid_set(nt, fidbuf, qid); MemCpy(nt->out + 7, qid, sizeof(@9p_qid)); SEND_RMSG } U0 @nt_Twalk(@nt* nt, U8* data) { // https://9fans.github.io/plan9port/man/man9/walk.html // size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) // size[4] Rwalk tag[2] nwqid[2] nwqid*(qid[13]) // The fid must be valid in the current session and must not have been opened for I/O by an open or create message. // If the full sequence of nwname elements is walked successfully, newfid will represent the file that results. // If not, newfid (and fid) will be unaffected. However, if newfid is in use or otherwise illegal, an Rerror is returned. // If the first element cannot be walked for any reason, Rerror is returned. // Otherwise, the walk will return an Rwalk message containing: // nwqid qids corresponding, in order, to the files that are visited by the nwqid successful elementwise walks; // nwqid is therefore either nwname or the index of the first elementwise walk that failed. // size[4] Rwalk tag[2] nwqid[2] nwqid*(qid[13]) // The value of nwqid cannot be zero unless nwname is zero. // Also, nwqid will always be less than or equal to nwname. // Only if it is equal, however, will newfid be affected, in which case newfid will represent: // the file reached by the final elementwise walk requested in the message. SET_MSGS @9p_twalk * Twalk = data; U8** wname = NULL; I64 i, j; U16 fid = Twalk->fid; U16 newfid = Twalk->newfid; U16 nwname = Twalk->nwname; U16 nwqid = 0; @9p_qid * qid = NULL; U8 basepath[512]; U8 pathbuf[512]; U8 fidbuf[16]; Rmsg->length = (4 + 1 + 2 + 2); //@nt_tx_dbg(nt, "Twalk fid: %d, newfid: %d, nwname: %d", fid, newfid, nwname); if (fid != nt->attach_fid) { // FIXME: Lookup base path from fid StrPrint(fidbuf, "%d", fid); qid = nt->fids->o(fidbuf)->@("qid"); if (!qid) { @nt_tx_dbg(nt, "ERROR: !qid"); PressAKey; } StrCpy(basepath, qid->path); if (qid->type != NT_IS_DIR) { StrLastOcc(basepath, "/")[0] = NULL; } } else { StrCpy(basepath, ""); } if (nwname) { wname = CAlloc(sizeof(U8*) * nwname, nt->main_task); @nt_pascal_to_c_strings(nt, wname, &Twalk->wname, nwname); for (i = 0; i < nwname; i++) { StrCpy(pathbuf, basepath); for (j = 0; j <= i; j++) { StrPrint(pathbuf + StrLen(pathbuf), "/%s", wname[j]); } qid = @nt_lookup_qid_by_path(nt, pathbuf); if (qid) { // Copy the qid to Rwalk and increment nwqid, Rwalk length MemCpy(nt->out + (4 + 1 + 2 + 2 + (nwqid * sizeof(@9p_qid))), qid, sizeof(@9p_qid)); nwqid++; Rmsg->length += sizeof(@9p_qid); } else { // Rerror Rmsg->length = (4 + 1 + 2 + 2); Rmsg->type = 0x6b; // Rerror SET_RESPONSE_TAG StrPrint(nt->out + (4 + 1 + 2 + 2), "file does not exist: '.%s'", pathbuf); //@nt_tx_dbg(nt, "newfid: %d, %s", newfid, nt->out + (4 + 1 + 2 + 2)); *(nt->out + (4 + 1 + 2))(U16*) = StrLen(nt->out + (4 + 1 + 2 + 2)); Rmsg->length += StrLen(nt->out + (4 + 1 + 2 + 2)); @9p_debug_dump(Rmsg, Rmsg->length, 1); nt->s->send(Rmsg, Rmsg->length); return; break; } } } // Map newfid to qid StrPrint(fidbuf, "%d", newfid); // nt->fids->set(fidbuf, qid, JSON_NUMBER); @nt_fid_set(nt, fidbuf, qid); // Set nwqid *(nt->out + (4 + 1 + 2))(U16*) = nwqid; //@nt_tx_dbg(nt, "newfid: %d, walked: %s", newfid, pathbuf); SEND_RMSG } U0 @nt_Topen(@nt* nt, U8* data) { // https://9fans.github.io/plan9port/man/man9/open.html // size[4] Topen tag[2] fid[4] mode[1] // size[4] Ropen tag[2] qid[13] iounit[4] SET_MSGS U32 fid = *(data + (4 + 1 + 2))(U32*); I64 mode = data[4 + 1 + 2 + 4]; U8 fidbuf[16]; StrPrint(fidbuf, "%d", fid); Rmsg->length = (4 + 1 + 2 + 13 + 4); //@nt_tx_dbg(nt, "Topen fid: %d, mode: %d", fid, mode); @9p_qid * qid = nt->fids->o(fidbuf)->@("qid"); if (!qid) { @nt_tx_dbg("ERROR: no qid for fid %d", fid); PressAKey; } MemCpy(nt->out + 7, qid, sizeof(@9p_qid)); *(nt->out + (4 + 1 + 2 + 13))(U32*) = NULL; // set iounit to NULL for now SEND_RMSG } U0 @nt_Tclunk(@nt* nt, U8* data) { // https://9fans.github.io/plan9port/man/man9/clunk.html // size[4] Tclunk tag[2] fid[4] // size[4] Rclunk tag[2] SET_MSGS U32 fid = *(data + (4 + 1 + 2))(U32*); U8 fidbuf[16]; StrPrint(fidbuf, "%d", fid); Rmsg->length = (4 + 1 + 2); //@nt_tx_dbg(nt, "Tclunk fid: %d", fid); nt->fids->unset(fidbuf); SEND_RMSG } // I64 test_read_cnt = 0; I64 test_read_cnt = 0; // I64 test_entry_size = 0x45; I64 test_entry_size = 0x41; // U8 test_entry[test_entry_size] = { 0x3e, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x27, 0xab, 0xe2, 0x68, 0x1b, 0xab, 0xe2, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x64, 0x72, 0x61, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63 }; U8 test_entry[test_entry_size] = { 0x3f, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x80, 0x27, 0xab, 0xe2, 0x68, 0x1b, 0xab, 0xe2, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x64, 0x72, 0x61, 0x77, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63 }; // U8 test_entry[test_entry_size] = { 0x3f, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x01, 0x00, 0x80, 0x4d, 0xb2, 0xe2, 0x68, 0x47, 0xb2, 0xe2, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x64, 0x72, 0x61, 0x77, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63, 0x04, 0x00, 0x61, 0x6c, 0x65, 0x63, 0x42, 0x00, 0x69, 0x00 }; U0 @dev_draw_new_read(@nt* nt, U8* data) { SET_MSGS U32 read_size = 144; I64 i; U8 newbuf[144]; U8 intbuf[16]; U8 pathbuf[128]; U8* chanstr = "x8r8g8b8"; JsonObject* tmpr; I64 conn = nt->drawobjs->length + 1; JsonObject* dobj = Json.CreateObject(nt->main_task); dobj->set("conn", conn, JSON_NUMBER); dobj->set("imageid", 0, JSON_NUMBER); dobj->set("chan", chanstr, JSON_STRING); dobj->set("r", Json.CreateObject(nt->main_task), JSON_OBJECT); dobj->set("clipr", Json.CreateObject(nt->main_task), JSON_OBJECT); tmpr = dobj->o("r"); tmpr->set("x0", 0, JSON_NUMBER); tmpr->set("y0", 0, JSON_NUMBER); tmpr->set("x1", GR_WIDTH, JSON_NUMBER); tmpr->set("y1", GR_HEIGHT, JSON_NUMBER); tmpr = dobj->o("clipr"); tmpr->set("x0", 0, JSON_NUMBER); tmpr->set("y0", 0, JSON_NUMBER); tmpr->set("x1", GR_WIDTH, JSON_NUMBER); tmpr->set("y1", GR_HEIGHT, JSON_NUMBER); dobj->set("imgs", Json.CreateObject(nt->main_task), JSON_OBJECT); // Insert new draw object [/dev/draw/[conn#] to nt->drawobjs] StrPrint(intbuf, "%d", conn); nt->drawobjs->set(intbuf, dobj, JSON_OBJECT); // Create path objects StrPrint(pathbuf, "/dev/draw/%d", conn); @nt_qid_create(nt, pathbuf, NT_IS_DIR); StrPrint(pathbuf, "/dev/draw/%d/data", conn); @nt_qid_create(nt, pathbuf, NT_IS_FILE); StrPrint(pathbuf, "/dev/draw/%d/refresh", conn); @nt_qid_create(nt, pathbuf, NT_IS_FILE); // ** not yet implemented // StrPrint(pathbuf, "/dev/draw/%d/colormap", conn); // @nt_qid_create(nt, pathbuf, NT_IS_FILE); // StrPrint(pathbuf, "/dev/draw/%d/ctl", conn); // @nt_qid_create(nt, pathbuf, NT_IS_FILE); // *** Build payload for Rread // Default; set all fields to 0 MemSet(newbuf, ' ', read_size); for (i = 0; i < 12; i++) { newbuf[10 + (12 * i)] = '0'; } // Set connection number StrPrint(intbuf, "%d", conn); MemCpy(newbuf + 11 - StrLen(intbuf), intbuf, StrLen(intbuf)); // Set chan string MemCpy(newbuf + 11 - StrLen(chanstr) + (12 * 2), chanstr, StrLen(chanstr)); // Set max.x of display image and clipping rect StrPrint(intbuf, "%d", GR_WIDTH); MemCpy(newbuf + 11 - StrLen(intbuf) + (12 * 6), intbuf, StrLen(intbuf)); MemCpy(newbuf + 11 - StrLen(intbuf) + (12 * 10), intbuf, StrLen(intbuf)); // Set max.y of display image and clipping rect StrPrint(intbuf, "%d", GR_HEIGHT); MemCpy(newbuf + 11 - StrLen(intbuf) + (12 * 7), intbuf, StrLen(intbuf)); MemCpy(newbuf + 11 - StrLen(intbuf) + (12 * 11), intbuf, StrLen(intbuf)); Rmsg->length = (4 + 1 + 2 + 4); MemCpy(nt->out + (4 + 1 + 2 + 4), newbuf, read_size); *(nt->out + (4 + 1 + 2))(U32*) = read_size; Rmsg->length += read_size; SEND_RMSG } U0 @nt_Rread_dir(@nt* nt, U8* data, @9p_qid * qid) { SET_MSGS Rmsg->length = (4 + 1 + 2 + 4); if (test_read_cnt) { *(nt->out + (4 + 1 + 2))(U32*) = 0; SEND_RMSG return; } U32 read_size = test_entry_size; MemCpy(nt->out + (4 + 1 + 2 + 4), test_entry, read_size); *(nt->out + (4 + 1 + 2))(U32*) = read_size; Rmsg->length += read_size; SEND_RMSG test_read_cnt++; } // @9p_msg * Rmsg = nt->out; // // if (ev) { // Rmsg->length = 0x0e; // Rmsg->type = TREAD + 1; // RREAD // Rmsg->tag = nt->kbd_read_tag; // MemCpy(nt->out + (4 + 1 + 2), ev, 7); // // nt->kbd_read_tag = NULL; // nt->kbd_last_jiffies = cnts.jiffies; // nt->s->send(Rmsg, Rmsg->length); // Free(ev); // } U0 @nt_Tread(@nt* nt, U8* data) { // https://9fans.github.io/plan9port/man/man9/read.html // size[4] Tread tag[2] fid[4] offset[8] count[4] // size[4] Rread tag[2] count[4] data[count] SET_MSGS U32 _fid = *(data + (4 + 1 + 2))(U32*); U64 offset = *(data + (4 + 1 + 2 + 4))(U64*); U32 count = *(data + (4 + 1 + 2 + 4 + 8))(U32*); //@nt_tx_dbg(nt, "Tread fid: %d, offset: %d, count: %d", fid, offset, count); U8 fidbuf[16]; StrPrint(fidbuf, "%d", _fid); JsonObject* fid = nt->fids->o(fidbuf); if (!fid) return; @9p_qid * qid = fid->@("qid"); if (!qid) return; I64 read_count = fid->@("read_count"); if (qid) { switch (qid->type) { case NT_IS_DIR: if (!StrCmp(qid->path, "/dev")) { @nt_Rread_dir(nt, data, qid); return; } //@nt_tx_dbg(nt, "got read on dir: %s", qid->path); break; default: if (!StrCmp(qid->path, "/dev/kbd")) { nt->kbd_read_tag = Tmsg->tag; //@nt_tx_dbg(nt, "Set nt->kbd_read_tag: 0x%04x", Tmsg->tag); } if (!StrCmp(qid->path, "/dev/draw/new")) { if (read_count) { @nt_Rerror(nt, data, "unknown id for draw image"); } else { @dev_draw_new_read(nt, data); } } break; } } read_count++; fid->set("read_count", read_count, JSON_NUMBER); } U0 @nt_Twrite(@nt* nt, U8* data) { // https://9fans.github.io/plan9port/man/man9/read.html // size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] // size[4] Rwrite tag[2] count[4] SET_MSGS U32 fid = *(data + (4 + 1 + 2))(U32*); U64 offset = *(data + (4 + 1 + 2 + 4))(U64*); U32 count = *(data + (4 + 1 + 2 + 4 + 8))(U32*); U8* p = data + (4 + 1 + 2 + 4 + 8 + 4); Rmsg->length = (4 + 1 + 2 + 4); //@nt_tx_dbg(nt, "Twrite fid: %d, offset: %d, count: %d", fid, offset, count); U8 fidbuf[16]; U8 errmsgbuf[256]; StrPrint(fidbuf, "%d", fid); @9p_qid * qid = nt->fids->o(fidbuf)->@("qid"); I64 i; if (qid) { if (String.BeginsWith("/dev/draw", qid->path) && String.EndsWith("/data", qid->path)) { } if (!StrCmp(qid->path, "/dev/draw/1/data")) { @nt_draw(nt, data, qid); } if (!StrCmp(qid->path, "/dev/cons")) { for (i = 0; i < count; i++) { DocPrint(nt->main_task->put_doc, "%c", p[i]); } } if (!StrCmp(qid->path, "/dev/mousectl")) { p[count] = NULL; StrPrint(errmsgbuf, "unknown control message \"%s\"", p); @nt_Rerror(nt, data, errmsgbuf); return; } //@nt_tx_dbg(nt, "got write for path: %s", qid->path); } *(nt->out + (4 + 1 + 2))(U32*) = count; SEND_RMSG } U0 @nt_Tflush(@nt* nt, U8* data) { // https://9fans.github.io/plan9port/man/man9/flush.html // size[4] Tflush tag[2] oldtag[2] // size[4] Rflush tag[2] SET_MSGS Rmsg->length = (4 + 1 + 2); SEND_RMSG } U0 @nt_Tstat(@nt* nt, U8* data) { // https://9fans.github.io/plan9port/man/man9/stat.html // size[4] Tstat tag[2] fid[4] // size[4] Rstat tag[2] stat[n] // The reply will contain a machine-independent directory entry, stat, laid out as follows: // size[2]total byte count of the following data // type[2]for kernel use // dev[4]for kernel use // qid.type[1] // the type of the file (directory, etc.), represented as a bit vector corresponding to the high 8 bits of the file’s mode word. // qid.vers[4] // version number for given path // qid.path[8] // the file server’s unique identification for the file // mode[4] // permissions and flags // atime[4] // last access time // mtime[4] // last modification time // length[8] // length of file in bytes // name[ s ] // file name; must be / if the file is the root directory of the server // uid[ s ]owner name // gid[ s ]group name // muid[ s ] // name of the user who last modified the file SET_MSGS U32 fid = *(data + (4 + 1 + 2))(U32*); Rmsg->length = (4 + 1 + 2 + sizeof(@9p_stat)); // set initial Rmsg length //@nt_tx_dbg(nt, "Tstat fid: %d", fid); // PressAKey; U8 fidbuf[16]; StrPrint(fidbuf, "%d", fid); @9p_qid * qid = nt->fids->o(fidbuf)->@("qid"); @9p_stat * stat = nt->out + (4 + 1 + 2); stat->size = sizeof(@9p_stat) - 2; stat->type = 0; stat->dev = 0; MemCpy(&stat->qid, qid, sizeof(@9p_qid)); stat->atime = 0; stat->mtime = 0; stat->length = 1024; // add strings U8* p = NULL; U8* user = "alec"; // name[ s ] p = nt->out + Rmsg->length; U8* path = qid->path; if (StrCmp(path, "/")) { path = StrLastOcc(path, "/") + 1; } *(p)(U16*) = StrLen(path); p += 2; MemCpy(p, path, StrLen(path)); p += StrLen(path); stat->size += StrLen(path) + 2; Rmsg->length += StrLen(path) + 2; // uid[ s ]owner name p = nt->out + Rmsg->length; *(p)(U16*) = StrLen(user); p += 2; MemCpy(p, user, StrLen(user)); p += StrLen(user); stat->size += StrLen(user) + 2; Rmsg->length += StrLen(user) + 2; // uid[ s ]group name p = nt->out + Rmsg->length; *(p)(U16*) = StrLen(user); p += 2; MemCpy(p, user, StrLen(user)); p += StrLen(user); stat->size += StrLen(user) + 2; Rmsg->length += StrLen(user) + 2; // muid[ s ] p = nt->out + Rmsg->length; *(p)(U16*) = StrLen(user); p += 2; MemCpy(p, user, StrLen(user)); p += StrLen(user); stat->size += StrLen(user) + 2; Rmsg->length += StrLen(user) + 2; SEND_RMSG }