9p.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. // -------------------------------------------------
  2. // --------------------- 9P ------------------------
  3. // -------------------------------------------------
  4. // Implementation of the 9p filesystem device following the
  5. // 9P2000.L protocol ( https://code.google.com/p/diod/wiki/protocol )
  6. "use strict";
  7. // Feature bit (bit position) for mount tag.
  8. const VIRTIO_9P_F_MOUNT_TAG = 0;
  9. // Assumed max tag length in bytes.
  10. const VIRTIO_9P_MAX_TAGLEN = 254;
  11. // TODO
  12. // flush
  13. // lock?
  14. // correct hard links
  15. var EPERM = 1; /* Operation not permitted */
  16. var ENOENT = 2; /* No such file or directory */
  17. var EINVAL = 22; /* Invalid argument */
  18. var ENOTSUPP = 524; /* Operation is not supported */
  19. var ENOTEMPTY = 39; /* Directory not empty */
  20. var EPROTO = 71 /* Protocol error */
  21. var P9_SETATTR_MODE = 0x00000001;
  22. var P9_SETATTR_UID = 0x00000002;
  23. var P9_SETATTR_GID = 0x00000004;
  24. var P9_SETATTR_SIZE = 0x00000008;
  25. var P9_SETATTR_ATIME = 0x00000010;
  26. var P9_SETATTR_MTIME = 0x00000020;
  27. var P9_SETATTR_CTIME = 0x00000040;
  28. var P9_SETATTR_ATIME_SET = 0x00000080;
  29. var P9_SETATTR_MTIME_SET = 0x00000100;
  30. var P9_STAT_MODE_DIR = 0x80000000;
  31. var P9_STAT_MODE_APPEND = 0x40000000;
  32. var P9_STAT_MODE_EXCL = 0x20000000;
  33. var P9_STAT_MODE_MOUNT = 0x10000000;
  34. var P9_STAT_MODE_AUTH = 0x08000000;
  35. var P9_STAT_MODE_TMP = 0x04000000;
  36. var P9_STAT_MODE_SYMLINK = 0x02000000;
  37. var P9_STAT_MODE_LINK = 0x01000000;
  38. var P9_STAT_MODE_DEVICE = 0x00800000;
  39. var P9_STAT_MODE_NAMED_PIPE = 0x00200000;
  40. var P9_STAT_MODE_SOCKET = 0x00100000;
  41. var P9_STAT_MODE_SETUID = 0x00080000;
  42. var P9_STAT_MODE_SETGID = 0x00040000;
  43. var P9_STAT_MODE_SETVTX = 0x00010000;
  44. var FID_NONE = -1;
  45. var FID_INODE = 1;
  46. var FID_XATTR = 2;
  47. /**
  48. * @constructor
  49. *
  50. * @param {FS} filesystem
  51. * @param {CPU} cpu
  52. */
  53. function Virtio9p(filesystem, cpu, bus) {
  54. /** @type {FS} */
  55. this.fs = filesystem;
  56. /** @const @type {BusConnector} */
  57. this.bus = bus;
  58. //this.configspace = [0x0, 0x4, 0x68, 0x6F, 0x73, 0x74]; // length of string and "host" string
  59. //this.configspace = [0x0, 0x9, 0x2F, 0x64, 0x65, 0x76, 0x2F, 0x72, 0x6F, 0x6F, 0x74 ]; // length of string and "/dev/root" string
  60. this.configspace_tagname = [0x68, 0x6F, 0x73, 0x74, 0x39, 0x70]; // "host9p" string
  61. this.configspace_taglen = this.configspace_tagname.length; // num bytes
  62. this.VERSION = "9P2000.L";
  63. this.BLOCKSIZE = 8192; // Let's define one page.
  64. this.msize = 8192; // maximum message size
  65. this.replybuffer = new Uint8Array(this.msize*2); // Twice the msize to stay on the safe site
  66. this.replybuffersize = 0;
  67. this.fids = [];
  68. /** @type {VirtIO} */
  69. this.virtio = new VirtIO(cpu,
  70. {
  71. name: "virtio-9p",
  72. pci_id: 0x06 << 3,
  73. device_id: 0x1049,
  74. subsystem_device_id: 9,
  75. common:
  76. {
  77. initial_port: 0xA800,
  78. queues:
  79. [
  80. {
  81. size_supported: 32,
  82. notify_offset: 0,
  83. },
  84. ],
  85. features:
  86. [
  87. VIRTIO_9P_F_MOUNT_TAG,
  88. VIRTIO_F_VERSION_1,
  89. VIRTIO_F_RING_EVENT_IDX,
  90. VIRTIO_F_RING_INDIRECT_DESC,
  91. ],
  92. on_driver_ok: () => {},
  93. },
  94. notification:
  95. {
  96. initial_port: 0xA900,
  97. single_handler: false,
  98. handlers:
  99. [
  100. (queue_id) =>
  101. {
  102. if(queue_id !== 0)
  103. {
  104. dbg_assert(false, "Virtio9P Notified for non-existent queue: " + queue_id +
  105. " (expected queue_id of 0)");
  106. return;
  107. }
  108. while(this.virtqueue.has_request())
  109. {
  110. const bufchain = this.virtqueue.pop_request();
  111. this.ReceiveRequest(bufchain);
  112. }
  113. this.virtqueue.notify_me_after(0);
  114. // Don't flush replies here: async replies are not completed yet.
  115. },
  116. ],
  117. },
  118. isr_status:
  119. {
  120. initial_port: 0xA700,
  121. },
  122. device_specific:
  123. {
  124. initial_port: 0xA600,
  125. struct:
  126. [
  127. {
  128. bytes: 2,
  129. name: "mount tag length",
  130. read: () => this.configspace_taglen,
  131. write: data => { /* read only */ },
  132. },
  133. ].concat(v86util.range(VIRTIO_9P_MAX_TAGLEN).map(index =>
  134. ({
  135. bytes: 1,
  136. name: "mount tag name " + index,
  137. // Note: configspace_tagname may have changed after set_state
  138. read: () => this.configspace_tagname[index] || 0,
  139. write: data => { /* read only */ },
  140. })
  141. )),
  142. },
  143. });
  144. this.virtqueue = this.virtio.queues[0];
  145. }
  146. Virtio9p.prototype.get_state = function()
  147. {
  148. var state = [];
  149. state[0] = this.configspace_tagname;
  150. state[1] = this.configspace_taglen;
  151. state[2] = this.virtio;
  152. state[3] = this.VERSION;
  153. state[4] = this.BLOCKSIZE;
  154. state[5] = this.msize;
  155. state[6] = this.replybuffer;
  156. state[7] = this.replybuffersize;
  157. state[8] = this.fids.map(function(f) { return [f.inodeid, f.type, f.uid]; });
  158. state[9] = this.fs;
  159. return state;
  160. };
  161. Virtio9p.prototype.set_state = function(state)
  162. {
  163. this.configspace_tagname = state[0];
  164. this.configspace_taglen = state[1];
  165. this.virtio = state[2];
  166. this.virtqueue = this.virtio.queues[0];
  167. this.VERSION = state[3];
  168. this.BLOCKSIZE = state[4];
  169. this.msize = state[5];
  170. this.replybuffer = state[6];
  171. this.replybuffersize = state[7];
  172. this.fids = state[8].map(function(f) { return { inodeid: f[0], type: f[1], uid: f[2] } });
  173. this.fs = state[9];
  174. };
  175. Virtio9p.prototype.Createfid = function(inode, type, uid) {
  176. return {inodeid: inode, type: type, uid: uid};
  177. }
  178. Virtio9p.prototype.Reset = function() {
  179. this.fids = [];
  180. }
  181. Virtio9p.prototype.BuildReply = function(id, tag, payloadsize) {
  182. marshall.Marshall(["w", "b", "h"], [payloadsize+7, id+1, tag], this.replybuffer, 0);
  183. if ((payloadsize+7) >= this.replybuffer.length) {
  184. message.Debug("Error in 9p: payloadsize exceeds maximum length");
  185. }
  186. //for(var i=0; i<payload.length; i++)
  187. // this.replybuffer[7+i] = payload[i];
  188. this.replybuffersize = payloadsize+7;
  189. return;
  190. }
  191. Virtio9p.prototype.SendError = function (tag, errormsg, errorcode) {
  192. //var size = marshall.Marshall(["s", "w"], [errormsg, errorcode], this.replybuffer, 7);
  193. var size = marshall.Marshall(["w"], [errorcode], this.replybuffer, 7);
  194. this.BuildReply(6, tag, size);
  195. }
  196. Virtio9p.prototype.SendReply = function (bufchain) {
  197. bufchain.set_next_blob(this.replybuffer.subarray(0, this.replybuffersize));
  198. this.virtqueue.push_reply(bufchain);
  199. this.virtqueue.flush_replies();
  200. }
  201. Virtio9p.prototype.ReceiveRequest = function (bufchain) {
  202. // TODO: split into header + data blobs to avoid unnecessary copying.
  203. const buffer = new Uint8Array(bufchain.length_readable);
  204. bufchain.get_next_blob(buffer);
  205. const state = { offset : 0 };
  206. var header = marshall.Unmarshall(["w", "b", "h"], buffer, state);
  207. var size = header[0];
  208. var id = header[1];
  209. var tag = header[2];
  210. //message.Debug("size:" + size + " id:" + id + " tag:" + tag);
  211. switch(id)
  212. {
  213. case 8: // statfs
  214. size = this.fs.GetTotalSize(); // size used by all files
  215. var space = this.fs.GetSpace();
  216. var req = [];
  217. req[0] = 0x01021997;
  218. req[1] = this.BLOCKSIZE; // optimal transfer block size
  219. req[2] = Math.floor(space/req[1]); // free blocks, let's say 1GB
  220. req[3] = req[2] - Math.floor(size/req[1]); // free blocks in fs
  221. req[4] = req[2] - Math.floor(size/req[1]); // free blocks avail to non-superuser
  222. req[5] = this.fs.inodes.length; // total number of inodes
  223. req[6] = 1024*1024;
  224. req[7] = 0; // file system id?
  225. req[8] = 256; // maximum length of filenames
  226. size = marshall.Marshall(["w", "w", "d", "d", "d", "d", "d", "d", "w"], req, this.replybuffer, 7);
  227. this.BuildReply(id, tag, size);
  228. this.SendReply(bufchain);
  229. break;
  230. case 112: // topen
  231. case 12: // tlopen
  232. var req = marshall.Unmarshall(["w", "w"], buffer, state);
  233. var fid = req[0];
  234. var mode = req[1];
  235. message.Debug("[open] fid=" + fid + ", mode=" + mode);
  236. var idx = this.fids[fid].inodeid;
  237. var inode = this.fs.GetInode(idx);
  238. message.Debug("file open " + inode.name);
  239. //if (inode.status == STATUS_LOADING) return;
  240. var ret = this.fs.OpenInode(idx, mode);
  241. this.fs.AddEvent(this.fids[fid].inodeid,
  242. function() {
  243. message.Debug("file opened " + inode.name + " tag:"+tag);
  244. var req = [];
  245. req[0] = inode.qid;
  246. req[1] = this.msize - 24;
  247. marshall.Marshall(["Q", "w"], req, this.replybuffer, 7);
  248. this.BuildReply(id, tag, 13+4);
  249. this.SendReply(bufchain);
  250. }.bind(this)
  251. );
  252. break;
  253. case 70: // link (just copying)
  254. var req = marshall.Unmarshall(["w", "w", "s"], buffer, state);
  255. var dfid = req[0];
  256. var fid = req[1];
  257. var name = req[2];
  258. message.Debug("[link] dfid=" + dfid + ", name=" + name);
  259. var inode = this.fs.CreateInode();
  260. var inodetarget = this.fs.GetInode(this.fids[fid].inodeid);
  261. var targetdata = this.fs.inodedata[this.fids[fid].inodeid];
  262. //inode = inodetarget;
  263. inode.mode = inodetarget.mode;
  264. inode.size = inodetarget.size;
  265. inode.symlink = inodetarget.symlink;
  266. var data = this.fs.inodedata[this.fs.inodes.length] = new Uint8Array(inode.size);
  267. if(targetdata)
  268. {
  269. data.set(targetdata.subarray(0, inode.size), 0);
  270. }
  271. inode.name = name;
  272. inode.parentid = this.fids[dfid].inodeid;
  273. this.fs.PushInode(inode);
  274. //inode.uid = inodetarget.uid;
  275. //inode.gid = inodetarget.gid;
  276. //inode.mode = inodetarget.mode | S_IFLNK;
  277. this.BuildReply(id, tag, 0);
  278. this.SendReply(bufchain);
  279. break;
  280. case 16: // symlink
  281. var req = marshall.Unmarshall(["w", "s", "s", "w"], buffer, state);
  282. var fid = req[0];
  283. var name = req[1];
  284. var symgt = req[2];
  285. var gid = req[3];
  286. message.Debug("[symlink] fid=" + fid + ", name=" + name + ", symgt=" + symgt + ", gid=" + gid);
  287. var idx = this.fs.CreateSymlink(name, this.fids[fid].inodeid, symgt);
  288. var inode = this.fs.GetInode(idx);
  289. inode.uid = this.fids[fid].uid;
  290. inode.gid = gid;
  291. marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
  292. this.BuildReply(id, tag, 13);
  293. this.SendReply(bufchain);
  294. break;
  295. case 18: // mknod
  296. var req = marshall.Unmarshall(["w", "s", "w", "w", "w", "w"], buffer, state);
  297. var fid = req[0];
  298. var name = req[1];
  299. var mode = req[2];
  300. var major = req[3];
  301. var minor = req[4];
  302. var gid = req[5];
  303. message.Debug("[mknod] fid=" + fid + ", name=" + name + ", major=" + major + ", minor=" + minor+ "");
  304. var idx = this.fs.CreateNode(name, this.fids[fid].inodeid, major, minor);
  305. var inode = this.fs.GetInode(idx);
  306. inode.mode = mode;
  307. inode.uid = this.fids[fid].uid;
  308. inode.gid = gid;
  309. marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
  310. this.BuildReply(id, tag, 13);
  311. this.SendReply(bufchain);
  312. break;
  313. case 22: // TREADLINK
  314. var req = marshall.Unmarshall(["w"], buffer, state);
  315. var fid = req[0];
  316. message.Debug("[readlink] fid=" + fid);
  317. var inode = this.fs.GetInode(this.fids[fid].inodeid);
  318. size = marshall.Marshall(["s"], [inode.symlink], this.replybuffer, 7);
  319. this.BuildReply(id, tag, size);
  320. this.SendReply(bufchain);
  321. break;
  322. case 72: // tmkdir
  323. var req = marshall.Unmarshall(["w", "s", "w", "w"], buffer, state);
  324. var fid = req[0];
  325. var name = req[1];
  326. var mode = req[2];
  327. var gid = req[3];
  328. message.Debug("[mkdir] fid=" + fid + ", name=" + name + ", mode=" + mode + ", gid=" + gid);
  329. var idx = this.fs.CreateDirectory(name, this.fids[fid].inodeid);
  330. var inode = this.fs.GetInode(idx);
  331. inode.mode = mode | S_IFDIR;
  332. inode.uid = this.fids[fid].uid;
  333. inode.gid = gid;
  334. marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
  335. this.BuildReply(id, tag, 13);
  336. this.SendReply(bufchain);
  337. break;
  338. case 14: // tlcreate
  339. var req = marshall.Unmarshall(["w", "s", "w", "w", "w"], buffer, state);
  340. var fid = req[0];
  341. var name = req[1];
  342. var flags = req[2];
  343. var mode = req[3];
  344. var gid = req[4];
  345. message.Debug("[create] fid=" + fid + ", name=" + name + ", flags=" + flags + ", mode=" + mode + ", gid=" + gid);
  346. var idx = this.fs.CreateFile(name, this.fids[fid].inodeid);
  347. this.fids[fid].inodeid = idx;
  348. this.fids[fid].type = FID_INODE;
  349. var inode = this.fs.GetInode(idx);
  350. inode.uid = this.fids[fid].uid;
  351. inode.gid = gid;
  352. inode.mode = mode;
  353. marshall.Marshall(["Q", "w"], [inode.qid, this.msize - 24], this.replybuffer, 7);
  354. this.BuildReply(id, tag, 13+4);
  355. this.SendReply(bufchain);
  356. break;
  357. case 52: // lock always suceed
  358. message.Debug("lock file\n");
  359. marshall.Marshall(["w"], [0], this.replybuffer, 7);
  360. this.BuildReply(id, tag, 1);
  361. this.SendReply(bufchain);
  362. break;
  363. /*
  364. case 54: // getlock
  365. break;
  366. */
  367. case 24: // getattr
  368. var req = marshall.Unmarshall(["w", "d"], buffer, state);
  369. var fid = req[0];
  370. var inode = this.fs.GetInode(this.fids[fid].inodeid);
  371. message.Debug("[getattr]: fid=" + fid + " name=" + inode.name + " request mask=" + req[1]);
  372. if(!inode || inode.status === STATUS_UNLINKED)
  373. {
  374. message.Debug("getattr: unlinked");
  375. this.SendError(tag, "No such file or directory", ENOENT);
  376. this.SendReply(bufchain);
  377. break;
  378. }
  379. req[0] |= 0x1000; // P9_STATS_GEN
  380. req[0] = req[1]; // request mask
  381. req[1] = inode.qid;
  382. req[2] = inode.mode;
  383. req[3] = inode.uid; // user id
  384. req[4] = inode.gid; // group id
  385. req[5] = inode.nlinks; // number of hard links
  386. req[6] = (inode.major<<8) | (inode.minor); // device id low
  387. req[7] = inode.size; // size low
  388. req[8] = this.BLOCKSIZE;
  389. req[9] = Math.floor(inode.size/512+1);; // blk size low
  390. req[10] = inode.atime; // atime
  391. req[11] = 0x0;
  392. req[12] = inode.mtime; // mtime
  393. req[13] = 0x0;
  394. req[14] = inode.ctime; // ctime
  395. req[15] = 0x0;
  396. req[16] = 0x0; // btime
  397. req[17] = 0x0;
  398. req[18] = 0x0; // st_gen
  399. req[19] = 0x0; // data_version
  400. marshall.Marshall([
  401. "d", "Q",
  402. "w",
  403. "w", "w",
  404. "d", "d",
  405. "d", "d", "d",
  406. "d", "d", // atime
  407. "d", "d", // mtime
  408. "d", "d", // ctime
  409. "d", "d", // btime
  410. "d", "d",
  411. ], req, this.replybuffer, 7);
  412. this.BuildReply(id, tag, 8 + 13 + 4 + 4+ 4 + 8*15);
  413. this.SendReply(bufchain);
  414. break;
  415. case 26: // setattr
  416. var req = marshall.Unmarshall(["w", "w",
  417. "w", // mode
  418. "w", "w", // uid, gid
  419. "d", // size
  420. "d", "d", // atime
  421. "d", "d"] // mtime
  422. , buffer, state);
  423. var fid = req[0];
  424. var inode = this.fs.GetInode(this.fids[fid].inodeid);
  425. message.Debug("[setattr]: fid=" + fid + " request mask=" + req[1] + " name=" +inode.name);
  426. if (req[1] & P9_SETATTR_MODE) {
  427. inode.mode = req[2];
  428. }
  429. if (req[1] & P9_SETATTR_UID) {
  430. inode.uid = req[3];
  431. }
  432. if (req[1] & P9_SETATTR_GID) {
  433. inode.gid = req[4];
  434. }
  435. if (req[1] & P9_SETATTR_ATIME) {
  436. inode.atime = Math.floor((new Date()).getTime()/1000);
  437. }
  438. if (req[1] & P9_SETATTR_MTIME) {
  439. inode.mtime = Math.floor((new Date()).getTime()/1000);
  440. }
  441. if (req[1] & P9_SETATTR_CTIME) {
  442. inode.ctime = Math.floor((new Date()).getTime()/1000);
  443. }
  444. if (req[1] & P9_SETATTR_ATIME_SET) {
  445. inode.atime = req[6];
  446. }
  447. if (req[1] & P9_SETATTR_MTIME_SET) {
  448. inode.mtime = req[8];
  449. }
  450. if (req[1] & P9_SETATTR_SIZE) {
  451. this.fs.ChangeSize(this.fids[fid].inodeid, req[5]);
  452. }
  453. this.BuildReply(id, tag, 0);
  454. this.SendReply(bufchain);
  455. break;
  456. case 50: // fsync
  457. var req = marshall.Unmarshall(["w", "d"], buffer, state);
  458. var fid = req[0];
  459. this.BuildReply(id, tag, 0);
  460. this.SendReply(bufchain);
  461. break;
  462. case 40: // TREADDIR
  463. case 116: // read
  464. var req = marshall.Unmarshall(["w", "d", "w"], buffer, state);
  465. var fid = req[0];
  466. var offset = req[1];
  467. var count = req[2];
  468. var inode = this.fs.GetInode(this.fids[fid].inodeid);
  469. if (id == 40) message.Debug("[treaddir]: fid=" + fid + " offset=" + offset + " count=" + count);
  470. if (id == 116) message.Debug("[read]: fid=" + fid + " (" + inode.name + ") offset=" + offset + " count=" + count + " fidtype=" + this.fids[fid].type);
  471. if(!inode || inode.status === STATUS_UNLINKED)
  472. {
  473. message.Debug("read/treaddir: unlinked");
  474. this.SendError(tag, "No such file or directory", ENOENT);
  475. this.SendReply(bufchain);
  476. break;
  477. }
  478. if (this.fids[fid].type == FID_XATTR) {
  479. if (inode.caps.length < offset+count) count = inode.caps.length - offset;
  480. for(var i=0; i<count; i++)
  481. this.replybuffer[7+4+i] = inode.caps[offset+i];
  482. marshall.Marshall(["w"], [count], this.replybuffer, 7);
  483. this.BuildReply(id, tag, 4 + count);
  484. this.SendReply(bufchain);
  485. } else {
  486. var file = this.fs.inodes[this.fids[fid].inodeid];
  487. this.bus.send("9p-read-start");
  488. this.fs.OpenInode(this.fids[fid].inodeid, undefined);
  489. this.fs.AddEvent(this.fids[fid].inodeid,
  490. function() {
  491. this.bus.send("9p-read-end", [file.name, count]);
  492. if (inode.size < offset+count) count = inode.size - offset;
  493. var data = this.fs.inodedata[this.fids[fid].inodeid];
  494. if(data) {
  495. this.replybuffer.set(data.subarray(offset, offset + count), 7 + 4);
  496. }
  497. marshall.Marshall(["w"], [count], this.replybuffer, 7);
  498. this.BuildReply(id, tag, 4 + count);
  499. this.SendReply(bufchain);
  500. }.bind(this)
  501. );
  502. }
  503. break;
  504. case 118: // write
  505. var req = marshall.Unmarshall(["w", "d", "w"], buffer, state);
  506. var fid = req[0];
  507. var offset = req[1];
  508. var count = req[2];
  509. message.Debug("[write]: fid=" + fid + " (" + this.fs.inodes[this.fids[fid].inodeid].name + ") offset=" + offset + " count=" + count);
  510. // XXX: Size of the subarray is unchecked
  511. this.fs.Write(this.fids[fid].inodeid, offset, count, buffer.subarray(state.offset));
  512. var file = this.fs.inodes[this.fids[fid].inodeid];
  513. this.bus.send("9p-write-end", [file.name, count]);
  514. marshall.Marshall(["w"], [count], this.replybuffer, 7);
  515. this.BuildReply(id, tag, 4);
  516. this.SendReply(bufchain);
  517. break;
  518. case 74: // RENAMEAT
  519. var req = marshall.Unmarshall(["w", "s", "w", "s"], buffer, state);
  520. var olddirfid = req[0];
  521. var oldname = req[1];
  522. var newdirfid = req[2];
  523. var newname = req[3];
  524. message.Debug("[renameat]: oldname=" + oldname + " newname=" + newname);
  525. var ret = this.fs.Rename(this.fids[olddirfid].inodeid, oldname, this.fids[newdirfid].inodeid, newname);
  526. if (ret == false) {
  527. this.SendError(tag, "No such file or directory", ENOENT);
  528. this.SendReply(bufchain);
  529. break;
  530. }
  531. this.BuildReply(id, tag, 0);
  532. this.SendReply(bufchain);
  533. break;
  534. case 76: // TUNLINKAT
  535. var req = marshall.Unmarshall(["w", "s", "w"], buffer, state);
  536. var dirfd = req[0];
  537. var name = req[1];
  538. var flags = req[2];
  539. message.Debug("[unlink]: dirfd=" + dirfd + " name=" + name + " flags=" + flags);
  540. var fid = this.fs.Search(this.fids[dirfd].inodeid, name);
  541. if (fid == -1) {
  542. this.SendError(tag, "No such file or directory", ENOENT);
  543. this.SendReply(bufchain);
  544. break;
  545. }
  546. var ret = this.fs.Unlink(fid);
  547. if (!ret) {
  548. this.SendError(tag, "Directory not empty", ENOTEMPTY);
  549. this.SendReply(bufchain);
  550. break;
  551. }
  552. this.BuildReply(id, tag, 0);
  553. this.SendReply(bufchain);
  554. break;
  555. case 100: // version
  556. var version = marshall.Unmarshall(["w", "s"], buffer, state);
  557. message.Debug("[version]: msize=" + version[0] + " version=" + version[1]);
  558. this.msize = version[0];
  559. size = marshall.Marshall(["w", "s"], [this.msize, this.VERSION], this.replybuffer, 7);
  560. this.BuildReply(id, tag, size);
  561. this.SendReply(bufchain);
  562. break;
  563. case 104: // attach
  564. // return root directorie's QID
  565. var req = marshall.Unmarshall(["w", "w", "s", "s", "w"], buffer, state);
  566. var fid = req[0];
  567. var uid = req[4];
  568. message.Debug("[attach]: fid=" + fid + " afid=" + hex8(req[1]) + " uname=" + req[2] + " aname=" + req[3]);
  569. this.fids[fid] = this.Createfid(0, FID_INODE, uid);
  570. var inode = this.fs.GetInode(this.fids[fid].inodeid);
  571. marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
  572. this.BuildReply(id, tag, 13);
  573. this.SendReply(bufchain);
  574. break;
  575. case 108: // tflush
  576. var req = marshall.Unmarshall(["h"], buffer, state);
  577. var oldtag = req[0];
  578. message.Debug("[flush] " + tag);
  579. //marshall.Marshall(["Q"], [inode.qid], this.replybuffer, 7);
  580. this.BuildReply(id, tag, 0);
  581. this.SendReply(bufchain);
  582. break;
  583. case 110: // walk
  584. var req = marshall.Unmarshall(["w", "w", "h"], buffer, state);
  585. var fid = req[0];
  586. var nwfid = req[1];
  587. var nwname = req[2];
  588. message.Debug("[walk]: fid=" + req[0] + " nwfid=" + req[1] + " nwname=" + nwname);
  589. if (nwname == 0) {
  590. this.fids[nwfid] = this.Createfid(this.fids[fid].inodeid, FID_INODE, this.fids[fid].uid);
  591. //this.fids[nwfid].inodeid = this.fids[fid].inodeid;
  592. marshall.Marshall(["h"], [0], this.replybuffer, 7);
  593. this.BuildReply(id, tag, 2);
  594. this.SendReply(bufchain);
  595. break;
  596. }
  597. var wnames = [];
  598. for(var i=0; i<nwname; i++) {
  599. wnames.push("s");
  600. }
  601. var walk = marshall.Unmarshall(wnames, buffer, state);
  602. var idx = this.fids[fid].inodeid;
  603. var offset = 7+2;
  604. var nwidx = 0;
  605. //console.log(idx, this.fs.inodes[idx]);
  606. message.Debug("walk in dir " + this.fs.inodes[idx].name + " to: " + walk.toString());
  607. for(var i=0; i<nwname; i++) {
  608. idx = this.fs.Search(idx, walk[i]);
  609. if (idx == -1) {
  610. message.Debug("Could not find: " + walk[i]);
  611. break;
  612. }
  613. offset += marshall.Marshall(["Q"], [this.fs.inodes[idx].qid], this.replybuffer, offset);
  614. nwidx++;
  615. //message.Debug(this.fids[nwfid].inodeid);
  616. //this.fids[nwfid].inodeid = idx;
  617. //this.fids[nwfid].type = FID_INODE;
  618. this.fids[nwfid] = this.Createfid(idx, FID_INODE, this.fids[fid].uid);
  619. }
  620. marshall.Marshall(["h"], [nwidx], this.replybuffer, 7);
  621. this.BuildReply(id, tag, offset-7);
  622. this.SendReply(bufchain);
  623. break;
  624. case 120: // clunk
  625. var req = marshall.Unmarshall(["w"], buffer, state);
  626. message.Debug("[clunk]: fid=" + req[0]);
  627. if (this.fids[req[0]] && this.fids[req[0]].inodeid >= 0) {
  628. this.fs.CloseInode(this.fids[req[0]].inodeid);
  629. this.fids[req[0]].inodeid = -1;
  630. this.fids[req[0]].type = FID_NONE;
  631. }
  632. this.BuildReply(id, tag, 0);
  633. this.SendReply(bufchain);
  634. break;
  635. case 32: // txattrcreate
  636. var req = marshall.Unmarshall(["w", "s", "d", "w"], buffer, state);
  637. var fid = req[0];
  638. var name = req[1];
  639. var attr_size = req[2];
  640. var flags = req[3];
  641. message.Debug("[txattrcreate]: fid=" + fid + " name=" + name + " attr_size=" + attr_size + " flags=" + flags);
  642. this.BuildReply(id, tag, 0);
  643. this.SendReply(bufchain);
  644. //this.SendError(tag, "Operation i not supported", EINVAL);
  645. //this.SendReply(bufchain);
  646. break;
  647. case 30: // xattrwalk
  648. var req = marshall.Unmarshall(["w", "w", "s"], buffer, state);
  649. var fid = req[0];
  650. var newfid = req[1];
  651. var name = req[2];
  652. message.Debug("[xattrwalk]: fid=" + req[0] + " newfid=" + req[1] + " name=" + req[2]);
  653. this.fids[newfid] = this.Createfid(this.fids[fid].inodeid, FID_NONE, this.fids[fid].uid);
  654. //this.fids[newfid].inodeid = this.fids[fid].inodeid;
  655. //this.fids[newfid].type = FID_NONE;
  656. var length = 0;
  657. if (name == "security.capability") {
  658. length = this.fs.PrepareCAPs(this.fids[fid].inodeid);
  659. this.fids[newfid].type = FID_XATTR;
  660. }
  661. marshall.Marshall(["d"], [length], this.replybuffer, 7);
  662. this.BuildReply(id, tag, 8);
  663. this.SendReply(bufchain);
  664. break;
  665. default:
  666. message.Debug("Error in Virtio9p: Unknown id " + id + " received");
  667. message.Abort();
  668. //this.SendError(tag, "Operation i not supported", ENOTSUPP);
  669. //this.SendReply(bufchain);
  670. break;
  671. }
  672. //consistency checks if there are problems with the filesystem
  673. //this.fs.Check();
  674. };