1
0

virtio_9p.js 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758
  1. #!/usr/bin/env node
  2. "use strict";
  3. process.on("unhandledRejection", exn => { throw exn; });
  4. const TEST_RELEASE_BUILD = +process.env.TEST_RELEASE_BUILD;
  5. var V86 = require(`../../build/${TEST_RELEASE_BUILD ? "libv86" : "libv86-debug"}.js`).V86;
  6. const fs = require("fs");
  7. const testfsjson = require("./testfs.json");
  8. const SHOW_LOGS = false;
  9. const STOP_ON_FIRST_FAILURE = false;
  10. function assert_equal(actual, expected, message)
  11. {
  12. if(actual !== expected)
  13. {
  14. console.warn("Failed assert equal (Test: %s). %s", tests[test_num].name, message || "");
  15. console.warn("Expected:\n" + expected);
  16. console.warn("Actual:\n" + actual);
  17. test_fail();
  18. }
  19. }
  20. function assert_not_equal(actual, expected, message)
  21. {
  22. if(actual === expected)
  23. {
  24. console.warn("Failed assert not equal (Test: %s). %s", tests[test_num].name, message || "");
  25. console.warn("Expected something different than:\n" + expected);
  26. test_fail();
  27. }
  28. }
  29. // Random printable characters.
  30. const test_file = new Uint8Array(512).map(v => 0x20 + Math.random() * 0x5e);
  31. const test_file_string = Buffer.from(test_file).toString();
  32. const test_file_small = new Uint8Array(16).map(v => 0x20 + Math.random() * 0x5e);
  33. const test_file_small_string = Buffer.from(test_file_small).toString();
  34. const tests =
  35. [
  36. {
  37. name: "API SearchPath",
  38. timeout: 60,
  39. mounts:
  40. [
  41. { path: "/x/fs2" },
  42. ],
  43. start: () =>
  44. {
  45. emulator.serial0_send("mkdir -p /mnt/a/b/c\n");
  46. emulator.serial0_send("touch /mnt/a/b/c/file1\n");
  47. emulator.serial0_send("touch /mnt/file2\n");
  48. emulator.serial0_send("mkdir -p /mnt/x/fs2/y/z\n");
  49. emulator.serial0_send("echo done-searchpath\n");
  50. },
  51. end_trigger: "done-searchpath",
  52. end: (capture, done) =>
  53. {
  54. const root1 = emulator.fs9p.SearchPath("");
  55. assert_equal(root1.id, 0, "root1 id");
  56. assert_equal(root1.parentid, -1, "root1 parentid");
  57. const root2 = emulator.fs9p.SearchPath("/");
  58. assert_equal(root2.id, 0, "root2 / id");
  59. assert_equal(root2.parentid, -1, "root2 / parentid");
  60. const notfound1 = emulator.fs9p.SearchPath("c");
  61. assert_equal(notfound1.id, -1, "notfound1 c id");
  62. assert_equal(notfound1.parentid, 0, "notfound1 c parentid");
  63. const notfound2 = emulator.fs9p.SearchPath("c/d");
  64. assert_equal(notfound2.id, -1, "notfound2 c/d id");
  65. assert_equal(notfound2.parentid, -1, "notfound2 c/d parentid");
  66. const notfound3 = emulator.fs9p.SearchPath("a/d");
  67. assert_equal(notfound3.id, -1, "notfound3 a/d id");
  68. assert_not_equal(notfound3.parentid, -1, "notfound3 a/d parent id");
  69. const idx_a = notfound3.parentid;
  70. const notfound4 = emulator.fs9p.SearchPath("a/d/e");
  71. assert_equal(notfound4.id, -1, "notfound4 a/d/e id");
  72. assert_equal(notfound4.parentid, -1, "notfound4 a/d/e parentid");
  73. const dir1 = emulator.fs9p.SearchPath("a");
  74. assert_equal(dir1.id, idx_a, "dir1 a id");
  75. assert_equal(dir1.parentid, 0, "dir1 a parentid");
  76. const dir2 = emulator.fs9p.SearchPath("a/b/c");
  77. assert_not_equal(dir2.id, -1, "dir2 a/b/c id");
  78. assert_not_equal(dir2.parentid, -1, "dir2 a/b/c parentid");
  79. const idx_b = dir2.parentid;
  80. const idx_c = dir2.id;
  81. const file1 = emulator.fs9p.SearchPath("a/b/c/file1");
  82. assert_not_equal(file1.id, -1, "file1 a/b/c/file1 id");
  83. assert_equal(file1.parentid, idx_c, "file1 a/b/c/file1 parentid");
  84. const file2 = emulator.fs9p.SearchPath("file2");
  85. assert_not_equal(file2.id, -1, "file2 id");
  86. assert_equal(file2.parentid, 0, "file2 parentid");
  87. const fwdpath1 = emulator.fs9p.SearchPath("x/fs2");
  88. assert_equal(fwdpath1.forward_path, null, "fwdpath1 x/fs2");
  89. const fwdpath2 = emulator.fs9p.SearchPath("x/fs2/y");
  90. assert_equal(fwdpath2.forward_path, "/y", "fwdpath2 x/fs2/y");
  91. const fwdpath3 = emulator.fs9p.SearchPath("x/fs2/y/z");
  92. assert_equal(fwdpath3.forward_path, "/y/z", "fwdpath3 x/fs2/y/z");
  93. const fwdpath4 = emulator.fs9p.SearchPath("x/fs2/nonexistent");
  94. assert_equal(fwdpath4.forward_path, "/nonexistent", "fwdpath4 x/fs2/nonexistent");
  95. done();
  96. },
  97. },
  98. {
  99. name: "Read Existing",
  100. timeout: 60,
  101. start: () =>
  102. {
  103. emulator.serial0_send("cp /etc/profile /mnt/read-existing\n");
  104. emulator.serial0_send("echo start-capture; cat /etc/profile; echo done-read-existing\n");
  105. },
  106. capture_trigger: "start-capture",
  107. end_trigger: "done-read-existing",
  108. end: async (capture, done) =>
  109. {
  110. const data = await emulator.read_file("read-existing");
  111. assert_equal(capture, Buffer.from(data).toString());
  112. done();
  113. },
  114. },
  115. {
  116. name: "Read New",
  117. timeout: 60,
  118. start: () =>
  119. {
  120. emulator.serial0_send("dd if=/dev/zero of=/mnt/read-new bs=1k count=512\n");
  121. emulator.serial0_send("echo done-read-new\n");
  122. },
  123. end_trigger: "done-read-new",
  124. end: async (capture, done) =>
  125. {
  126. const data = await emulator.read_file("read-new");
  127. assert_equal(data.length, 512 * 1024);
  128. if(data.find(v => v !== 0))
  129. {
  130. console.warn("Fail: Incorrect data. Expected all zeros.");
  131. test_fail();
  132. }
  133. done();
  134. },
  135. },
  136. {
  137. name: "Read Async",
  138. use_fsjson: true,
  139. timeout: 60,
  140. start: () =>
  141. {
  142. emulator.serial0_send("echo start-capture;");
  143. // "foo" is from ./testfs/foo
  144. emulator.serial0_send("cat /mnt/foo;");
  145. emulator.serial0_send("echo done-read-async\n");
  146. },
  147. capture_trigger: "start-capture",
  148. end_trigger: "done-read-async",
  149. end: async (capture, done) =>
  150. {
  151. assert_equal(capture, "bar\n");
  152. const data = await emulator.read_file("foo");
  153. assert_equal(Buffer.from(data).toString(), "bar\n");
  154. done();
  155. },
  156. },
  157. {
  158. name: "Write New",
  159. timeout: 60,
  160. files:
  161. [
  162. {
  163. file: "write-new",
  164. data: test_file,
  165. },
  166. ],
  167. start: () =>
  168. {
  169. emulator.serial0_send("echo start-capture; cat /mnt/write-new; echo; echo done-write-new\n");
  170. },
  171. capture_trigger: "start-capture",
  172. end_trigger: "done-write-new",
  173. end: (capture, done) =>
  174. {
  175. // Handle word wrapping.
  176. const lines = capture.split("\n");
  177. let pos = 0;
  178. for(const line of lines)
  179. {
  180. assert_equal(line, test_file_string.slice(pos, line.length));
  181. pos += line.length;
  182. }
  183. done();
  184. },
  185. },
  186. {
  187. name: "New file time",
  188. timeout: 10,
  189. start: () =>
  190. {
  191. emulator.serial0_send("echo start-capture; echo foo > /mnt/bar; ls -l --full-time --color=never /mnt/bar; echo; echo done-write-new\n");
  192. },
  193. capture_trigger: "start-capture",
  194. end_trigger: "done-write-new",
  195. end: (capture, done) =>
  196. {
  197. const outputs = capture.split("\n").map(output => output.split(/\s+/));
  198. // atime: Should be fresh
  199. const [year, month, day] = outputs[0][5].split("-");
  200. assert_not_equal(year, "1970");
  201. done();
  202. },
  203. },
  204. {
  205. name: "Move",
  206. timeout: 60,
  207. files:
  208. [
  209. {
  210. file: "test-file",
  211. data: test_file,
  212. },
  213. ],
  214. start: () =>
  215. {
  216. emulator.serial0_send("echo start-capture;");
  217. emulator.serial0_send("cat /mnt/test-file;");
  218. emulator.serial0_send("find /mnt;");
  219. // Rename. Verify updated directory.
  220. emulator.serial0_send("mv /mnt/test-file /mnt/renamed;");
  221. emulator.serial0_send("cat /mnt/renamed;");
  222. emulator.serial0_send("find /mnt;");
  223. // Move between folders. Verify directories.
  224. emulator.serial0_send("mkdir /mnt/somedir;");
  225. emulator.serial0_send("mv /mnt/renamed /mnt/somedir/file;");
  226. emulator.serial0_send("cat /mnt/somedir/file;");
  227. emulator.serial0_send("find /mnt;");
  228. // Rename folder.
  229. emulator.serial0_send("mv /mnt/somedir /mnt/otherdir;");
  230. emulator.serial0_send("cat /mnt/otherdir/file;");
  231. emulator.serial0_send("find /mnt;");
  232. // Move folder.
  233. emulator.serial0_send("mkdir /mnt/thirddir;");
  234. emulator.serial0_send("mv /mnt/otherdir /mnt/thirddir;");
  235. emulator.serial0_send("cat /mnt/thirddir/otherdir/file;");
  236. emulator.serial0_send("find /mnt;");
  237. // Move folder outside /mnt. Should be removed from 9p filesystem.
  238. emulator.serial0_send("mv /mnt/thirddir/otherdir /root/movedoutside;");
  239. emulator.serial0_send("cat /root/movedoutside/file;");
  240. emulator.serial0_send("find /mnt;");
  241. // Cleanup.
  242. emulator.serial0_send("rm -rf /root/movedoutside;");
  243. emulator.serial0_send("echo done-move\n");
  244. },
  245. capture_trigger: "start-capture",
  246. end_trigger: "done-move",
  247. end: (capture, done) =>
  248. {
  249. assert_equal(capture,
  250. test_file_string +
  251. "/mnt\n" +
  252. "/mnt/test-file\n" +
  253. test_file_string +
  254. "/mnt\n" +
  255. "/mnt/renamed\n" +
  256. test_file_string +
  257. "/mnt\n" +
  258. "/mnt/somedir\n" +
  259. "/mnt/somedir/file\n" +
  260. test_file_string +
  261. "/mnt\n" +
  262. "/mnt/otherdir\n" +
  263. "/mnt/otherdir/file\n" +
  264. test_file_string +
  265. "/mnt\n" +
  266. "/mnt/thirddir\n" +
  267. "/mnt/thirddir/otherdir\n" +
  268. "/mnt/thirddir/otherdir/file\n" +
  269. test_file_string +
  270. "/mnt\n" +
  271. "/mnt/thirddir\n");
  272. done();
  273. },
  274. },
  275. {
  276. name: "Unlink",
  277. timeout: 60,
  278. files:
  279. [
  280. {
  281. file: "existing-file",
  282. data: test_file,
  283. },
  284. ],
  285. start: () =>
  286. {
  287. emulator.serial0_send("touch /mnt/new-file\n");
  288. emulator.serial0_send("mkdir /mnt/new-dir\n");
  289. emulator.serial0_send("touch /mnt/new-dir/file\n");
  290. emulator.serial0_send("echo start-capture;");
  291. emulator.serial0_send("rm /mnt/new-file;");
  292. emulator.serial0_send("test ! -e /mnt/new-file && echo new-file-unlinked;");
  293. emulator.serial0_send("cat /mnt/new-file 2>/dev/null || echo read-failed;");
  294. emulator.serial0_send("rm /mnt/existing-file;");
  295. emulator.serial0_send("test ! -e /mnt/existing-file && echo existing-file-unlinked;");
  296. emulator.serial0_send("cat /mnt/existing-file 2>/dev/null || echo read-failed;");
  297. emulator.serial0_send("rmdir /mnt/new-dir 2>/dev/null || echo rmdir-failed;");
  298. emulator.serial0_send("test -e /mnt/new-dir && echo new-dir-exist;");
  299. emulator.serial0_send("rm /mnt/new-dir/file;");
  300. emulator.serial0_send("rmdir /mnt/new-dir;");
  301. emulator.serial0_send("test ! -e /mnt/new-dir/file && echo new-dir-file-unlinked;");
  302. emulator.serial0_send("test ! -e /mnt/new-dir && echo new-dir-unlinked;");
  303. emulator.serial0_send("ls /mnt/new-dir 2>/dev/null || echo read-failed;");
  304. emulator.serial0_send("echo done-unlink\n");
  305. },
  306. capture_trigger: "start-capture",
  307. end_trigger: "done-unlink",
  308. end: (capture, done) =>
  309. {
  310. assert_equal(capture,
  311. "new-file-unlinked\n" +
  312. "read-failed\n" +
  313. "existing-file-unlinked\n" +
  314. "read-failed\n" +
  315. "rmdir-failed\n" +
  316. "new-dir-exist\n" +
  317. "new-dir-file-unlinked\n" +
  318. "new-dir-unlinked\n" +
  319. "read-failed\n");
  320. done();
  321. },
  322. },
  323. {
  324. name: "Hard Links",
  325. timeout: 60,
  326. files:
  327. [
  328. {
  329. file: "target",
  330. data: test_file_small,
  331. },
  332. ],
  333. start: () =>
  334. {
  335. // Helper that prints filename followed by nlinks.
  336. emulator.serial0_send("nlinks() {\n");
  337. emulator.serial0_send(` ls -dli $@ | awk '{ print "'$@' "$3 }'\n`);
  338. emulator.serial0_send("}\n");
  339. // Check nlinks before mkdir.
  340. emulator.serial0_send("nlinks /mnt | tee -a /mnt/target\n");
  341. emulator.serial0_send("mkdir /mnt/dir\n");
  342. emulator.serial0_send("echo other > /mnt/target2\n");
  343. // Check nlinks after mkdir.
  344. emulator.serial0_send("nlinks /mnt | tee -a /mnt/target\n");
  345. emulator.serial0_send("nlinks /mnt/dir | tee -a /mnt/target\n");
  346. emulator.serial0_send("nlinks /mnt/target | tee -a /mnt/target\n");
  347. // Create hard links.
  348. emulator.serial0_send("ln /mnt/target /mnt/link1\n");
  349. emulator.serial0_send("ln /mnt/link1 /mnt/dir/link2\n");
  350. emulator.serial0_send("ln /mnt/dir/link2 /mnt/dir/link3\n");
  351. emulator.serial0_send("ln /mnt/target2 /mnt/link-other\n");
  352. // Test inode numbers.
  353. emulator.serial0_send("{ test /mnt/target -ef /mnt/link1 && \n");
  354. emulator.serial0_send(" test /mnt/link1 -ef /mnt/dir/link2 && \n");
  355. emulator.serial0_send(" test /mnt/target -ef /mnt/dir/link3 && \n");
  356. emulator.serial0_send(" echo same inode | tee -a /mnt/target; }\n");
  357. emulator.serial0_send("{ test /mnt/link-other -ef /mnt/dir/link3 || \n");
  358. emulator.serial0_send(" echo different inode | tee -a /mnt/link1; }\n");
  359. // Check nlinks after hard links.
  360. emulator.serial0_send("nlinks /mnt | tee -a /mnt/dir/link2\n");
  361. emulator.serial0_send("nlinks /mnt/dir | tee -a /mnt/dir/link2\n");
  362. emulator.serial0_send("nlinks /mnt/target | tee -a /mnt/dir/link2\n");
  363. emulator.serial0_send("nlinks /mnt/dir/link2 | tee -a /mnt/dir/link2\n");
  364. emulator.serial0_send("nlinks /mnt/target2 | tee -a /mnt/dir/link2\n");
  365. emulator.serial0_send("nlinks /mnt/link-other | tee -a /mnt/dir/link2\n");
  366. // Movement and unlink.
  367. emulator.serial0_send("mv /mnt/link1 /mnt/link1-renamed\n");
  368. emulator.serial0_send("echo renamed | tee -a /mnt/link1-renamed\n");
  369. emulator.serial0_send("mv /mnt/dir/link2 /mnt/link2-moved\n");
  370. emulator.serial0_send("echo moved | tee -a /mnt/link2-moved\n");
  371. emulator.serial0_send("rm /mnt/target\n");
  372. emulator.serial0_send("echo unlinked original | tee -a /mnt/dir/link3\n");
  373. // Test inode numbers after movement and unlinking.
  374. emulator.serial0_send("{ test /mnt/link1-renamed -ef /mnt/link2-moved && \n");
  375. emulator.serial0_send(" test /mnt/link2-moved -ef /mnt/dir/link3 && \n");
  376. emulator.serial0_send(" echo same inode after mv | tee -a /mnt/link1-renamed; }\n");
  377. // Check nlinks after movement and unlinking.
  378. emulator.serial0_send("nlinks /mnt | tee -a /mnt/link2-moved\n");
  379. emulator.serial0_send("nlinks /mnt/dir | tee -a /mnt/link2-moved\n");
  380. emulator.serial0_send("nlinks /mnt/link1-renamed | tee -a /mnt/link2-moved\n");
  381. emulator.serial0_send("echo start-capture;\\\n");
  382. // Unlink the rest and output the above messages.
  383. emulator.serial0_send("rm /mnt/link1-renamed;\\\n");
  384. emulator.serial0_send("echo unlinked link1 >> /mnt/link2-moved;\\\n");
  385. emulator.serial0_send("nlinks /mnt/link2-moved >> /mnt/link2-moved;\\\n");
  386. emulator.serial0_send("rm /mnt/link2-moved;\\\n");
  387. emulator.serial0_send("echo unlinked link2 >> /mnt/dir/link3;\\\n");
  388. emulator.serial0_send("nlinks /mnt/dir/link3 >> /mnt/dir/link3;\\\n");
  389. emulator.serial0_send("cat /mnt/dir/link3;\\\n");
  390. emulator.serial0_send("rm /mnt/dir/link3;\\\n");
  391. // Verify nlinks of directories after unlinking hardlinks.
  392. emulator.serial0_send("nlinks /mnt;\\\n");
  393. emulator.serial0_send("nlinks /mnt/dir;\\\n");
  394. // Verify nlinks of root directory after subdirectory is unlinked.
  395. emulator.serial0_send("rmdir /mnt/dir;\\\n");
  396. emulator.serial0_send("nlinks /mnt;\\\n");
  397. emulator.serial0_send("echo done-hard-links\n");
  398. },
  399. capture_trigger: "start-capture",
  400. end_trigger: "done-hard-links",
  401. end: (capture, done) =>
  402. {
  403. assert_equal(capture,
  404. test_file_small_string +
  405. "/mnt 2\n" +
  406. "/mnt 3\n" +
  407. "/mnt/dir 2\n" +
  408. "/mnt/target 1\n" +
  409. "same inode\n" +
  410. "different inode\n" +
  411. "/mnt 3\n" +
  412. "/mnt/dir 2\n" +
  413. "/mnt/target 4\n" +
  414. "/mnt/dir/link2 4\n" +
  415. "/mnt/target2 2\n" +
  416. "/mnt/link-other 2\n" +
  417. "renamed\n" +
  418. "moved\n" +
  419. "unlinked original\n" +
  420. "same inode after mv\n" +
  421. "/mnt 3\n" +
  422. "/mnt/dir 2\n" +
  423. "/mnt/link1-renamed 3\n" +
  424. "unlinked link1\n" +
  425. "/mnt/link2-moved 2\n" +
  426. "unlinked link2\n" +
  427. "/mnt/dir/link3 1\n" +
  428. "/mnt 3\n" +
  429. "/mnt/dir 2\n" +
  430. "/mnt 2\n");
  431. done();
  432. },
  433. },
  434. {
  435. name: "Symlinks",
  436. timeout: 60,
  437. files:
  438. [
  439. {
  440. file: "target",
  441. data: test_file_small,
  442. },
  443. ],
  444. start: () =>
  445. {
  446. emulator.serial0_send("echo otherdata > /mnt/target2\n");
  447. emulator.serial0_send("ln -s /mnt/target /mnt/symlink\n");
  448. emulator.serial0_send("echo appended >> /mnt/symlink\n");
  449. emulator.serial0_send("echo start-capture;");
  450. // Should output same file data.
  451. emulator.serial0_send("cat /mnt/target;");
  452. emulator.serial0_send("cat /mnt/symlink;");
  453. // Swap target with the other file.
  454. emulator.serial0_send("rm /mnt/target;");
  455. emulator.serial0_send("mv /mnt/target2 /mnt/target;");
  456. // Symlink should now read from that file.
  457. emulator.serial0_send("cat /mnt/symlink;");
  458. emulator.serial0_send("rm /mnt/target;");
  459. emulator.serial0_send("rm /mnt/symlink;");
  460. emulator.serial0_send("echo done-symlinks\n");
  461. },
  462. capture_trigger: "start-capture",
  463. end_trigger: "done-symlinks",
  464. end: (capture, done) =>
  465. {
  466. assert_equal(capture,
  467. test_file_small_string + "appended\n" +
  468. test_file_small_string + "appended\n" +
  469. "otherdata\n");
  470. done();
  471. },
  472. },
  473. {
  474. name: "Mknod - fifo",
  475. timeout: 60,
  476. start: () =>
  477. {
  478. emulator.serial0_send("mkfifo /mnt/testfifo\n");
  479. emulator.serial0_send('(cat /mnt/testfifo > /mnt/testfifo-output;echo "\ndone-fifo") &\n');
  480. emulator.serial0_send("echo fifomessage > /mnt/testfifo\n");
  481. },
  482. end_trigger: "done-fifo",
  483. end: async (capture, done) =>
  484. {
  485. const data = await emulator.read_file("testfifo-output");
  486. assert_equal(Buffer.from(data).toString(), "fifomessage\n");
  487. done();
  488. },
  489. },
  490. {
  491. name: "Readlink",
  492. timeout: 60,
  493. start: () =>
  494. {
  495. emulator.serial0_send("touch /mnt/target\n");
  496. emulator.serial0_send("ln -s /mnt/target /mnt/link\n");
  497. emulator.serial0_send("echo start-capture;");
  498. emulator.serial0_send("readlink /mnt/link;");
  499. emulator.serial0_send("echo done-readlink\n");
  500. },
  501. capture_trigger: "start-capture",
  502. end_trigger: "done-readlink",
  503. end: (capture, done) =>
  504. {
  505. assert_equal(capture, "/mnt/target\n");
  506. done();
  507. },
  508. },
  509. {
  510. name: "Mkdir",
  511. timeout: 60,
  512. start: () =>
  513. {
  514. emulator.serial0_send("echo notfoobar > /mnt/e-file\n");
  515. emulator.serial0_send("mkdir /mnt/a-dir\n");
  516. emulator.serial0_send("mkdir /mnt/a-dir/b-dir\n");
  517. emulator.serial0_send("mkdir /mnt/a-dir/c-dir\n");
  518. emulator.serial0_send("touch /mnt/a-dir/d-file\n");
  519. emulator.serial0_send("echo mkdirfoobar > /mnt/a-dir/e-file\n");
  520. emulator.serial0_send("echo done-mkdir\n");
  521. },
  522. end_trigger: "done-mkdir",
  523. end: async (capture, done) =>
  524. {
  525. const data = await emulator.read_file("a-dir/e-file");
  526. assert_equal(Buffer.from(data).toString(), "mkdirfoobar\n");
  527. done();
  528. },
  529. },
  530. {
  531. name: "Walk",
  532. timeout: 60,
  533. start: () =>
  534. {
  535. emulator.serial0_send("mkdir -p /mnt/walk/a/aa/aaa/aaaa\n");
  536. emulator.serial0_send("mkdir -p /mnt/walk/a/aa/aaa/aaaa\n");
  537. emulator.serial0_send("mkdir -p /mnt/walk/b/ba\n");
  538. emulator.serial0_send("mkdir -p /mnt/walk/a/aa/aab\n");
  539. emulator.serial0_send("mkdir -p /mnt/walk/a/aa/aac\n");
  540. emulator.serial0_send("touch /mnt/walk/a/aa/aab/aabfile\n");
  541. emulator.serial0_send("touch /mnt/walk/b/bfile\n");
  542. emulator.serial0_send("echo start-capture;");
  543. emulator.serial0_send("find /mnt/walk | sort;"); // order agnostic
  544. emulator.serial0_send("echo done-walk\n");
  545. },
  546. capture_trigger: "start-capture",
  547. end_trigger: "done-walk",
  548. end: (capture, done) =>
  549. {
  550. const actual = capture;
  551. const expected =
  552. "/mnt/walk\n" +
  553. "/mnt/walk/a\n" +
  554. "/mnt/walk/a/aa\n" +
  555. "/mnt/walk/a/aa/aaa\n" +
  556. "/mnt/walk/a/aa/aaa/aaaa\n" +
  557. "/mnt/walk/a/aa/aab\n" +
  558. "/mnt/walk/a/aa/aab/aabfile\n" +
  559. "/mnt/walk/a/aa/aac\n" +
  560. "/mnt/walk/b\n" +
  561. "/mnt/walk/b/ba\n" +
  562. "/mnt/walk/b/bfile\n";
  563. assert_equal(actual, expected);
  564. done();
  565. },
  566. },
  567. {
  568. name: "Statfs",
  569. timeout: 60,
  570. allow_failure: true,
  571. start: () =>
  572. {
  573. emulator.serial0_send("echo start-capture;");
  574. emulator.serial0_send("touch /mnt/file;");
  575. emulator.serial0_send("df -PTk /mnt | tail -n 1;");
  576. // Grow file and verify space usage.
  577. emulator.serial0_send("dd if=/dev/zero of=/mnt/file bs=1k count=4 status=none;");
  578. emulator.serial0_send("df -PTk /mnt | tail -n 1;");
  579. // Shrink file and verify space usage.
  580. emulator.serial0_send("truncate -s 0 /mnt/file;");
  581. emulator.serial0_send("df -PTk /mnt | tail -n 1;");
  582. emulator.serial0_send("echo done-statfs\n");
  583. },
  584. capture_trigger: "start-capture",
  585. end_trigger: "done-statfs",
  586. end: (capture, done) =>
  587. {
  588. const outputs = capture.split("\n").map(output => output.split(/\s+/));
  589. if(outputs.length < 3)
  590. {
  591. console.warn("Wrong format: %s", capture);
  592. test_fail();
  593. done();
  594. return;
  595. }
  596. const before = outputs[0];
  597. const after_add = outputs[1];
  598. const after_rm = outputs[2];
  599. // mount tag
  600. assert_equal(before[0], "host9p");
  601. // fs type
  602. assert_equal(before[1], "9p");
  603. // total size in 1024 blocks
  604. assert_equal(after_add[2], before[2]);
  605. assert_equal(after_rm[2], before[2]);
  606. // used size in 1024 blocks
  607. assert_equal(+after_add[3], (+before[3]) + 4);
  608. assert_equal(after_rm[3], before[3]);
  609. // free size in 1024 blocks
  610. assert_equal(+after_add[4], (+before[4]) - 4);
  611. assert_equal(after_rm[4], before[4]);
  612. // Entry [5] is percentage used.
  613. // mount path
  614. assert_equal(before[6], "/mnt");
  615. done();
  616. },
  617. },
  618. {
  619. name: "File Attributes",
  620. timeout: 60,
  621. start: () =>
  622. {
  623. emulator.serial0_send("echo start-capture;");
  624. emulator.serial0_send("dd if=/dev/zero of=/mnt/file bs=1 count=137 status=none;");
  625. emulator.serial0_send("touch -t 200002022222 /mnt/file;");
  626. emulator.serial0_send("chmod =rw /mnt/file;");
  627. emulator.serial0_send("ls -l --full-time --color=never /mnt/file;");
  628. emulator.serial0_send("chmod +x /mnt/file;");
  629. emulator.serial0_send("chmod -w /mnt/file;");
  630. emulator.serial0_send("ln /mnt/file /mnt/file-link;");
  631. emulator.serial0_send("ls -l --full-time --color=never /mnt/file;");
  632. emulator.serial0_send("chmod -x /mnt/file;");
  633. emulator.serial0_send("truncate -s 100 /mnt/file;");
  634. emulator.serial0_send("touch -t 201011220344 /mnt/file;");
  635. emulator.serial0_send("rm /mnt/file-link;");
  636. emulator.serial0_send("ls -l --full-time --color=never /mnt/file;");
  637. emulator.serial0_send("echo done-file-attr\n");
  638. },
  639. capture_trigger: "start-capture",
  640. end_trigger: "done-file-attr",
  641. end: (capture, done) =>
  642. {
  643. const outputs = capture.split("\n").map(output => output.split(/\s+/));
  644. if(outputs.length < 3)
  645. {
  646. console.warn("Wrong format (expected 3 rows): %s", capture);
  647. test_fail();
  648. done();
  649. return;
  650. }
  651. // mode
  652. assert_equal(outputs[0][0], "-rw-r--r--");
  653. // nlinks
  654. assert_equal(outputs[0][1], "1");
  655. // user
  656. assert_equal(outputs[0][2], "root");
  657. // group
  658. assert_equal(outputs[0][3], "root");
  659. // size
  660. assert_equal(outputs[0][4], "137");
  661. // atime
  662. assert_equal(outputs[0][5], "2000-02-02");
  663. assert_equal(outputs[0][6], "22:22:00");
  664. assert_equal(outputs[0][7], "+0000");
  665. // pathname
  666. assert_equal(outputs[0][8], "/mnt/file");
  667. // mode
  668. assert_equal(outputs[1][0], "-r-xr-xr-x");
  669. // nlinks
  670. assert_equal(outputs[1][1], "2");
  671. // user
  672. assert_equal(outputs[1][2], "root");
  673. // group
  674. assert_equal(outputs[1][3], "root");
  675. // size
  676. assert_equal(outputs[1][4], "137");
  677. // atime
  678. assert_equal(outputs[1][5], "2000-02-02");
  679. assert_equal(outputs[1][6], "22:22:00");
  680. assert_equal(outputs[1][7], "+0000");
  681. // pathname
  682. assert_equal(outputs[1][8], "/mnt/file");
  683. // mode
  684. assert_equal(outputs[2][0], "-r--r--r--");
  685. // nlinks
  686. assert_equal(outputs[2][1], "1");
  687. // user
  688. assert_equal(outputs[2][2], "root");
  689. // group
  690. assert_equal(outputs[2][3], "root");
  691. // size
  692. assert_equal(outputs[2][4], "100");
  693. // atime
  694. assert_equal(outputs[2][5], "2010-11-22");
  695. assert_equal(outputs[2][6], "03:44:00");
  696. assert_equal(outputs[2][7], "+0000");
  697. // pathname
  698. assert_equal(outputs[2][8], "/mnt/file");
  699. done();
  700. },
  701. },
  702. {
  703. name: "Xattrwalk and Listxattr",
  704. timeout: 60,
  705. allow_failure: true,
  706. start: () =>
  707. {
  708. emulator.serial0_send("echo originalvalue > /mnt/file\n");
  709. emulator.serial0_send("echo start-capture;");
  710. emulator.serial0_send('setfattr --name=user.attr1 --value="val1" /mnt/file;');
  711. emulator.serial0_send('setfattr --name=user.attr2 --value="val2" /mnt/file;');
  712. emulator.serial0_send('setfattr --name=user.mime_type --value="text/plain" /mnt/file;');
  713. emulator.serial0_send('setfattr --name=user.nested.attr --value="foobar" /mnt/file;');
  714. // Unrecognized attribute name under other namespaces should be allowed.
  715. emulator.serial0_send('setfattr --name=security.not_an_attr --value="val3" /mnt/file;');
  716. // Remove the caps attribute we've automatically put in. Tested later.
  717. emulator.serial0_send("setfattr --remove=security.capability /mnt/file;");
  718. emulator.serial0_send("getfattr --encoding=text --absolute-names --dump /mnt/file | sort;");
  719. emulator.serial0_send("getfattr --encoding=text --absolute-names --name=user.nested.attr /mnt/file;");
  720. emulator.serial0_send("getfattr --encoding=text --absolute-names --name=security.not_an_attr /mnt/file;");
  721. emulator.serial0_send("getfattr --encoding=text --absolute-names --name=user.attr2 /mnt/file;");
  722. emulator.serial0_send("echo done-listxattr\n");
  723. },
  724. capture_trigger: "start-capture",
  725. end_trigger: "done-listxattr",
  726. end: (capture, done) =>
  727. {
  728. assert_equal(capture,
  729. "# file: /mnt/file\n" +
  730. 'security.not_an_attr="val3"\n' +
  731. 'user.attr1="val1"\n' +
  732. 'user.attr2="val2"\n' +
  733. 'user.mime_type="text/plain"\n' +
  734. 'user.nested.attr="foobar"\n' +
  735. "\n" +
  736. "# file: /mnt/file\n" +
  737. 'user.nested.attr="foobar"\n' +
  738. "\n" +
  739. "# file: /mnt/file\n" +
  740. 'security.not_an_attr="val3"\n' +
  741. "\n" +
  742. "# file: /mnt/file\n" +
  743. 'user.attr2="val2"\n');
  744. done();
  745. },
  746. },
  747. {
  748. name: "Xattrcreate",
  749. timeout: 60,
  750. allow_failure: true,
  751. start: () =>
  752. {
  753. emulator.serial0_send("echo originalvalue > /mnt/file\n");
  754. // Remove the caps attribute we've automatically put in. Tested later.
  755. emulator.serial0_send("setfattr --remove=security.capability /mnt/file\n");
  756. emulator.serial0_send("echo start-capture;");
  757. // Creation of new xattr using xattrcreate.
  758. emulator.serial0_send("setfattr --name=user.foo --value=bar /mnt/file;");
  759. // File contents should not be overriden.
  760. emulator.serial0_send("cat /mnt/file;");
  761. emulator.serial0_send("getfattr --encoding=hex --absolute-names --name=user.foo /mnt/file;");
  762. // Overwriting of xattr using xattrcreate.
  763. emulator.serial0_send("setfattr --name=user.foo --value=baz /mnt/file;");
  764. // File contents should not be overriden.
  765. emulator.serial0_send("cat /mnt/file;");
  766. emulator.serial0_send("getfattr --encoding=hex --absolute-names --name=user.foo /mnt/file;");
  767. emulator.serial0_send("echo done-xattrcreate\n");
  768. },
  769. capture_trigger: "start-capture",
  770. end_trigger: "done-xattrcreate",
  771. end: (capture, done) =>
  772. {
  773. assert_equal(capture,
  774. "originalvalue\n" +
  775. "# file: /mnt/file\n" +
  776. 'user.foo="bar"\n' +
  777. "\n" +
  778. "originalvalue\n" +
  779. "# file: /mnt/file\n" +
  780. 'user.foo="baz"\n' +
  781. "\n");
  782. done();
  783. },
  784. },
  785. {
  786. name: "Report All Security Capabilities",
  787. timeout: 60,
  788. allow_failure: true,
  789. start: () =>
  790. {
  791. emulator.serial0_send("touch /mnt/file\n");
  792. emulator.serial0_send("echo start-capture;");
  793. emulator.serial0_send("getfattr --encoding=hex --absolute-names --name=security.capability /mnt/file;");
  794. emulator.serial0_send("echo done-xattr\n");
  795. },
  796. capture_trigger: "start-capture",
  797. end_trigger: "done-xattr",
  798. end: (capture, done) =>
  799. {
  800. assert_equal(capture,
  801. "# file: /mnt/file\n" +
  802. "security.capability=0x" +
  803. // magic and revision number
  804. "00000002" +
  805. // lower permitted
  806. "ffffffff" +
  807. // lower inheritable
  808. "ffffffff" +
  809. // higher permitted
  810. "3f000000" +
  811. // higher inheritable
  812. "3f000000" +
  813. "\n\n");
  814. done();
  815. },
  816. },
  817. {
  818. name: "File Locks",
  819. timeout: 60,
  820. start: () =>
  821. {
  822. emulator.serial0_send("touch /mnt/file\n");
  823. emulator.serial0_send("touch /mnt/logs\n");
  824. emulator.serial0_send("mkfifo /mnt/fifo1\n");
  825. emulator.serial0_send("mkfifo /mnt/fifo2\n");
  826. emulator.serial0_send("flock -s /mnt/file -c 'cat /mnt/fifo1 >> /mnt/file' &\n");
  827. emulator.serial0_send("flock -s /mnt/file -c 'echo lock-shared-2 >> /mnt/file' \n");
  828. emulator.serial0_send("flock -xn /mnt/file -c 'echo lock unblocked! >> /mnt/logs' \n");
  829. emulator.serial0_send("echo lock-shared-1 > /mnt/fifo1\n");
  830. emulator.serial0_send("flock -x /mnt/file -c 'cat /mnt/fifo1 >> /mnt/file' &\n");
  831. emulator.serial0_send("flock -x /mnt/file -c 'echo lock-exclusive-2 >> /mnt/file' &\n");
  832. emulator.serial0_send("flock -sn /mnt/file -c 'echo lock unblocked! >> /mnt/logs' \n");
  833. emulator.serial0_send("echo lock-exclusive-1 > /mnt/fifo1\n");
  834. emulator.serial0_send("flock -sn /mnt/file -c 'cat /mnt/fifo1 >> /mnt/file' &\n");
  835. emulator.serial0_send("flock -s /mnt/file -c 'cat /mnt/fifo2 >> /mnt/file' &\n");
  836. emulator.serial0_send("flock -x /mnt/file -c 'echo lock-exclusive-3 >> /mnt/file' &\n");
  837. emulator.serial0_send("echo lock-shared-4 > /mnt/fifo2\n");
  838. emulator.serial0_send("echo lock-shared-3 > /mnt/fifo1\n");
  839. emulator.serial0_send("echo start-capture;\\\n");
  840. emulator.serial0_send("cat /mnt/file;\\\n");
  841. emulator.serial0_send("cat /mnt/logs;\\\n");
  842. emulator.serial0_send("echo done-locks\n");
  843. },
  844. capture_trigger: "start-capture",
  845. end_trigger: "done-locks",
  846. end: (capture, done) =>
  847. {
  848. assert_equal(capture,
  849. "lock-shared-2\n" +
  850. "lock-shared-1\n" +
  851. "lock-exclusive-1\n" +
  852. "lock-exclusive-2\n" +
  853. "lock-shared-4\n" +
  854. "lock-shared-3\n" +
  855. "lock-exclusive-3\n");
  856. const idx = emulator.fs9p.Search(0, "file");
  857. const P9_LOCK_TYPE_RDLCK = 0;
  858. const P9_LOCK_TYPE_WRLCK = 1;
  859. const P9_LOCK_TYPE_UNLCK = 2;
  860. const P9_LOCK_SUCCESS = 0;
  861. const P9_LOCK_BLOCKED = 1;
  862. const CLIENT_ID = "under test";
  863. function test_getlock(num, type, pos, proc_id, locked)
  864. {
  865. const lock = emulator.fs9p.DescribeLock(type, pos, 1, proc_id, CLIENT_ID);
  866. const ret = emulator.fs9p.GetLock(idx, lock, 0);
  867. assert_equal(ret !== null, locked,
  868. `getlock ${num}: type=${type}, pos=${pos}, proc_id=${proc_id}. Wrong state:`);
  869. }
  870. function test_lock(num, type, start, length, proc_id, status, lock_state)
  871. {
  872. console.log(` Lock ${num}: type=${type}, start=${start}, length=${length} ` +
  873. ` proc_id=${proc_id}, expected state=${lock_state}`);
  874. const lock = emulator.fs9p.DescribeLock(type, start, length, proc_id, CLIENT_ID);
  875. assert_equal(emulator.fs9p.Lock(idx, lock, 0), status, "Wrong status:");
  876. for(const [i, state] of [...lock_state].entries())
  877. {
  878. switch(state)
  879. {
  880. case "1":
  881. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 1, false);
  882. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 2, false);
  883. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 2, true);
  884. break;
  885. case "2":
  886. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 2, false);
  887. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 1, false);
  888. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 1, true);
  889. break;
  890. case "3":
  891. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 1, false);
  892. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 1, true);
  893. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 2, false);
  894. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 2, true);
  895. break;
  896. case "e":
  897. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 1, false);
  898. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 2, true);
  899. break;
  900. case "E":
  901. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 1, true);
  902. test_getlock(num, P9_LOCK_TYPE_RDLCK, i, 2, false);
  903. break;
  904. case "-":
  905. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 1, false);
  906. test_getlock(num, P9_LOCK_TYPE_WRLCK, i, 2, false);
  907. break;
  908. }
  909. }
  910. }
  911. // Key:
  912. // 1/2/3 = shared lock by process 1/2/both
  913. // e/E = exclusive lock by process 1/2
  914. // - = no locks
  915. const I = Infinity;
  916. test_lock(0, P9_LOCK_TYPE_RDLCK, 0, 1, 1, P9_LOCK_SUCCESS, "1-------"); // First lock.
  917. test_lock(1, P9_LOCK_TYPE_RDLCK, 0, 2, 1, P9_LOCK_SUCCESS, "11------"); // Replace.
  918. test_lock(2, P9_LOCK_TYPE_RDLCK, 1, 1, 2, P9_LOCK_SUCCESS, "13------");
  919. test_lock(3, P9_LOCK_TYPE_RDLCK, 2, 2, 1, P9_LOCK_SUCCESS, "1311----"); // Skip. Merge before.
  920. test_lock(4, P9_LOCK_TYPE_WRLCK, 0, 1, 1, P9_LOCK_SUCCESS, "e311----"); // Shrink left.
  921. test_lock(5, P9_LOCK_TYPE_WRLCK, 1, 1, 1, P9_LOCK_BLOCKED, "e311----");
  922. test_lock(6, P9_LOCK_TYPE_UNLCK, 0, 4, 1, P9_LOCK_SUCCESS, "-2------"); // Delete.
  923. test_lock(7, P9_LOCK_TYPE_WRLCK, 1, 2, 1, P9_LOCK_BLOCKED, "-2------");
  924. test_lock(8, P9_LOCK_TYPE_UNLCK, 1, 3, 2, P9_LOCK_SUCCESS, "--------"); // Delete.
  925. test_lock(9, P9_LOCK_TYPE_WRLCK, 1, 1, 1, P9_LOCK_SUCCESS, "-e------");
  926. test_lock(10, P9_LOCK_TYPE_RDLCK, 3, 3, 1, P9_LOCK_SUCCESS, "-e-111--"); // Skip.
  927. test_lock(11, P9_LOCK_TYPE_RDLCK, 2, 1, 2, P9_LOCK_SUCCESS, "-e2111--"); // Skip past.
  928. test_lock(12, P9_LOCK_TYPE_UNLCK, 2, 1, 2, P9_LOCK_SUCCESS, "-e-111--"); // Delete.
  929. test_lock(13, P9_LOCK_TYPE_WRLCK, 0, 1, 1, P9_LOCK_SUCCESS, "ee-111--");
  930. test_lock(14, P9_LOCK_TYPE_WRLCK, 1, 4, 1, P9_LOCK_SUCCESS, "eeeee1--"); // Merge before. Shrink both ways.
  931. test_lock(15, P9_LOCK_TYPE_WRLCK, 1, 2, 2, P9_LOCK_BLOCKED, "eeeee1--");
  932. test_lock(16, P9_LOCK_TYPE_RDLCK, 4, 5, 2, P9_LOCK_BLOCKED, "eeeee1--");
  933. test_lock(17, P9_LOCK_TYPE_RDLCK, 5, I, 2, P9_LOCK_SUCCESS, "eeeee322");
  934. test_lock(18, P9_LOCK_TYPE_UNLCK, 0, I, 1, P9_LOCK_SUCCESS, "-----222"); // Replace.
  935. test_lock(19, P9_LOCK_TYPE_RDLCK, 4, I, 2, P9_LOCK_SUCCESS, "----2222"); // Replace.
  936. test_lock(20, P9_LOCK_TYPE_WRLCK, 2, I, 2, P9_LOCK_SUCCESS, "--EEEEEE"); // Replace.
  937. test_lock(21, P9_LOCK_TYPE_WRLCK, 0, 1, 2, P9_LOCK_SUCCESS, "E-EEEEEE");
  938. test_lock(22, P9_LOCK_TYPE_WRLCK, 1, 3, 2, P9_LOCK_SUCCESS, "EEEEEEEE"); // Merge both. Shrink left.
  939. test_lock(23, P9_LOCK_TYPE_RDLCK, 3, 4, 2, P9_LOCK_SUCCESS, "EEE2222E"); // Split.
  940. test_lock(24, P9_LOCK_TYPE_RDLCK, 1, 2, 2, P9_LOCK_SUCCESS, "E222222E"); // Merge after. Shrink right.
  941. test_lock(25, P9_LOCK_TYPE_RDLCK, 2, 3, 2, P9_LOCK_SUCCESS, "E222222E"); // No-op.
  942. done();
  943. },
  944. },
  945. {
  946. name: "Stress Files",
  947. timeout: 360,
  948. start: () =>
  949. {
  950. emulator.serial0_send("mkdir /mnt/stress-files\n");
  951. emulator.serial0_send('cat << "EOF" | sh\n');
  952. // Create files.
  953. // Ensure directory inode data exceeds maximum message size for 9p.
  954. emulator.serial0_send("for f in $(seq -w 0 999)\n");
  955. emulator.serial0_send("do\n");
  956. emulator.serial0_send(' echo "$f" > "/mnt/stress-files/file-$f"\n');
  957. emulator.serial0_send("done\n");
  958. emulator.serial0_send("echo start-capture\n");
  959. // Read some of them.
  960. emulator.serial0_send("for f in $(seq -w 0 31 999)\n");
  961. emulator.serial0_send("do\n");
  962. emulator.serial0_send(' cat "/mnt/stress-files/file-$f"\n');
  963. emulator.serial0_send("done\n");
  964. // Walk.
  965. emulator.serial0_send("find /mnt/stress-files | sort\n");
  966. // Delete and verify.
  967. // Using glob checks readdir.
  968. emulator.serial0_send("rm /mnt/stress-files/file-*\n");
  969. emulator.serial0_send('test -z "$(ls /mnt/stress-files)" && echo delete-success\n');
  970. emulator.serial0_send("echo done-stress-files\n");
  971. emulator.serial0_send("EOF\n");
  972. },
  973. capture_trigger: "start-capture",
  974. end_trigger: "done-stress-files",
  975. end: (capture, done) =>
  976. {
  977. let expected = "";
  978. for(let i = 0; i < 1000; i += 31)
  979. {
  980. expected += i.toString().padStart(3, "0") + "\n";
  981. }
  982. expected += "/mnt/stress-files\n";
  983. for(let i = 0; i < 1000; i ++)
  984. {
  985. expected += "/mnt/stress-files/file-" + i.toString().padStart(3, "0") + "\n";
  986. }
  987. expected += "delete-success\n";
  988. assert_equal(capture, expected);
  989. done();
  990. },
  991. },
  992. {
  993. name: "Stress Directories",
  994. timeout: 360,
  995. start: () =>
  996. {
  997. emulator.serial0_send('cat << "EOF" | sh\n');
  998. emulator.serial0_send("p=/mnt/stress-dirs\n");
  999. emulator.serial0_send('mkdir "$p"\n');
  1000. // Create deep folder structure
  1001. emulator.serial0_send("for i in $(seq 0 99)\n");
  1002. emulator.serial0_send("do\n");
  1003. emulator.serial0_send(' p="$p/$i"\n');
  1004. emulator.serial0_send(' mkdir "$p"\n');
  1005. emulator.serial0_send(' echo "$i" > "$p/file"\n');
  1006. emulator.serial0_send("done\n");
  1007. // Try accessing deep files
  1008. emulator.serial0_send("p=/mnt/stress-dirs\n");
  1009. emulator.serial0_send("echo start-capture\n");
  1010. // Skip first 80 - otherwise too slow
  1011. emulator.serial0_send("for i in $(seq 0 79)\n");
  1012. emulator.serial0_send("do\n");
  1013. emulator.serial0_send(' p="$p/$i"\n');
  1014. emulator.serial0_send("done\n");
  1015. emulator.serial0_send("for i in $(seq 80 99)\n");
  1016. emulator.serial0_send("do\n");
  1017. emulator.serial0_send(' p="$p/$i"\n');
  1018. emulator.serial0_send(' cat "$p/file"\n');
  1019. emulator.serial0_send("done\n");
  1020. // Delete and verify
  1021. emulator.serial0_send("rm -rf /mnt/stress-dirs/0\n");
  1022. emulator.serial0_send('test -z "$(ls /mnt/stress-dirs)" && echo delete-success\n');
  1023. emulator.serial0_send("echo done-stress-dirs\n");
  1024. emulator.serial0_send("EOF\n");
  1025. },
  1026. capture_trigger: "start-capture",
  1027. end_trigger: "done-stress-dirs",
  1028. end: (capture, done) =>
  1029. {
  1030. const outputs = capture.split("\n");
  1031. for(let i = 0; i < 20; i++)
  1032. {
  1033. assert_equal(outputs[i], `${i + 80}`);
  1034. }
  1035. assert_equal(outputs[20], "delete-success");
  1036. done();
  1037. },
  1038. },
  1039. {
  1040. name: "Read Past Available",
  1041. timeout: 60,
  1042. start: () =>
  1043. {
  1044. emulator.serial0_send("echo a > /mnt/small-file\n");
  1045. emulator.serial0_send("echo start-capture;");
  1046. // Reading from offsets > size of file should not read anything.
  1047. emulator.serial0_send("dd if=/mnt/small-file bs=1 count=1 skip=10;");
  1048. emulator.serial0_send("dd if=/mnt/small-file bs=1 count=1 skip=100;");
  1049. emulator.serial0_send("dd if=/mnt/small-file bs=1 count=1 skip=1000;");
  1050. emulator.serial0_send("echo done-read-exceed\n");
  1051. },
  1052. capture_trigger: "start-capture",
  1053. end_trigger: "done-read-exceed",
  1054. end: (capture, done) =>
  1055. {
  1056. const outputs = capture.split("\n");
  1057. assert_equal(outputs[0], "0+0 records in");
  1058. assert_equal(outputs[1], "0+0 records out");
  1059. assert_equal(outputs[2], "0+0 records in");
  1060. assert_equal(outputs[3], "0+0 records out");
  1061. assert_equal(outputs[4], "0+0 records in");
  1062. assert_equal(outputs[5], "0+0 records out");
  1063. done();
  1064. },
  1065. },
  1066. {
  1067. name: "Read Mounted",
  1068. timeout: 60,
  1069. mounts:
  1070. [
  1071. { path: "/a/b/fs2", baseurl: __dirname + "/testfs/", basefs: testfsjson },
  1072. ],
  1073. start: () =>
  1074. {
  1075. emulator.serial0_send("echo start-capture;");
  1076. emulator.serial0_send("cat /mnt/a/b/fs2/foo;");
  1077. emulator.serial0_send("cat /mnt/a/b/fs2/dir/bar;");
  1078. emulator.serial0_send("echo done-read-mounted\n");
  1079. },
  1080. capture_trigger: "start-capture",
  1081. end_trigger: "done-read-mounted",
  1082. end: async (capture, done) =>
  1083. {
  1084. assert_equal(capture, "bar\nfoobaz\n");
  1085. const data = await emulator.read_file("/a/b/fs2/dir/bar");
  1086. assert_equal(Buffer.from(data).toString(), "foobaz\n");
  1087. done();
  1088. },
  1089. },
  1090. {
  1091. name: "Write Mounted",
  1092. timeout: 60,
  1093. mounts:
  1094. [
  1095. { path: "/a/b/fs2" },
  1096. ],
  1097. files:
  1098. [
  1099. {
  1100. file: "/a/b/fs2/write-new-host",
  1101. data: test_file,
  1102. },
  1103. ],
  1104. start: () =>
  1105. {
  1106. emulator.serial0_send("mkdir /mnt/a/b/fs2/c\n");
  1107. emulator.serial0_send("echo foobar > /mnt/a/b/fs2/c/write-new-guest\n");
  1108. emulator.serial0_send("echo start-capture;");
  1109. emulator.serial0_send("cat /mnt/a/b/fs2/c/write-new-guest;");
  1110. emulator.serial0_send("cat /mnt/a/b/fs2/write-new-host; echo;");
  1111. emulator.serial0_send("echo done-write-mounted\n");
  1112. },
  1113. capture_trigger: "start-capture",
  1114. end_trigger: "done-write-mounted",
  1115. end: async (capture, done) =>
  1116. {
  1117. const lines = capture.split("\n");
  1118. assert_equal(lines.shift(), "foobar");
  1119. let pos = 0;
  1120. for(const line of lines)
  1121. {
  1122. assert_equal(line, test_file_string.slice(pos, line.length));
  1123. pos += line.length;
  1124. }
  1125. const data = await emulator.read_file("a/b/fs2/c/write-new-guest");
  1126. assert_equal(Buffer.from(data).toString(), "foobar\n");
  1127. done();
  1128. },
  1129. },
  1130. {
  1131. name: "Walk Mounted",
  1132. timeout: 180,
  1133. mounts:
  1134. [
  1135. { path: "/a/fs2" },
  1136. { path: "/fs3" },
  1137. { path: "/fs3/fs4" },
  1138. ],
  1139. start: () =>
  1140. {
  1141. emulator.serial0_send("echo start-capture;");
  1142. emulator.serial0_send("mkdir -p /mnt/a/fs2/aa/aaa/aaaa;");
  1143. emulator.serial0_send("mkdir -p /mnt/a/fs2/aa/aab;");
  1144. emulator.serial0_send("mkdir -p /mnt/a/fs2/ab/aba;");
  1145. emulator.serial0_send("touch /mnt/a/fs2/ab/aba/abafile;");
  1146. emulator.serial0_send("mkdir -p /mnt/a/fs2/ab/abb;");
  1147. emulator.serial0_send("mkdir -p /mnt/fs3/a/aa/aaa;");
  1148. emulator.serial0_send("mkdir -p /mnt/fs3/a/ab/aba;");
  1149. emulator.serial0_send("touch /mnt/fs3/a/afile;");
  1150. emulator.serial0_send("mkdir -p /mnt/fs3/b;");
  1151. emulator.serial0_send("mkdir -p /mnt/fs3/fs4/a/aa/aaa;");
  1152. emulator.serial0_send("mkdir -p /mnt/fs3/fs4/a/ab/;");
  1153. emulator.serial0_send("mkdir -p /mnt/fs3/fs4/a/ac/aca;");
  1154. emulator.serial0_send("touch /mnt/fs3/fs4/a/ac/aca/acafile;");
  1155. emulator.serial0_send("find /mnt | sort;"); // order agnostic
  1156. emulator.serial0_send("echo done-walk-mounted\n");
  1157. },
  1158. capture_trigger: "start-capture",
  1159. end_trigger: "done-walk-mounted",
  1160. end: (capture, done) =>
  1161. {
  1162. const lines = capture.split("\n");
  1163. const expected_lines =
  1164. [
  1165. "/mnt",
  1166. "/mnt/a",
  1167. "/mnt/a/fs2",
  1168. "/mnt/a/fs2/aa",
  1169. "/mnt/a/fs2/aa/aaa",
  1170. "/mnt/a/fs2/aa/aaa/aaaa",
  1171. "/mnt/a/fs2/aa/aab",
  1172. "/mnt/a/fs2/ab",
  1173. "/mnt/a/fs2/ab/aba",
  1174. "/mnt/a/fs2/ab/aba/abafile",
  1175. "/mnt/a/fs2/ab/abb",
  1176. "/mnt/fs3",
  1177. "/mnt/fs3/a",
  1178. "/mnt/fs3/a/aa",
  1179. "/mnt/fs3/a/aa/aaa",
  1180. "/mnt/fs3/a/ab",
  1181. "/mnt/fs3/a/ab/aba",
  1182. "/mnt/fs3/a/afile",
  1183. "/mnt/fs3/b",
  1184. "/mnt/fs3/fs4",
  1185. "/mnt/fs3/fs4/a",
  1186. "/mnt/fs3/fs4/a/aa",
  1187. "/mnt/fs3/fs4/a/aa/aaa",
  1188. "/mnt/fs3/fs4/a/ab",
  1189. "/mnt/fs3/fs4/a/ac",
  1190. "/mnt/fs3/fs4/a/ac/aca",
  1191. "/mnt/fs3/fs4/a/ac/aca/acafile",
  1192. ];
  1193. for(const expected of expected_lines)
  1194. {
  1195. assert_equal(lines.shift(), expected);
  1196. }
  1197. done();
  1198. },
  1199. },
  1200. {
  1201. name: "Move Mounted",
  1202. timeout: 60,
  1203. mounts:
  1204. [
  1205. { path: "/a/b/fs2" },
  1206. { path: "/fs3" },
  1207. { path: "/fs3/fs4" },
  1208. { path: "/fs3/fs4/fs5" },
  1209. ],
  1210. start: () =>
  1211. {
  1212. emulator.serial0_send("echo foobar > /mnt/fs3/file\n");
  1213. emulator.serial0_send("mkdir /mnt/a/b/fs2/dir\n");
  1214. emulator.serial0_send("mkdir /mnt/fs3/fs4/fs5/otherdir\n");
  1215. emulator.serial0_send("echo contents > /mnt/a/b/fs2/dir/child\n");
  1216. // Using tail -f to keep 'file' open for modification in bg while it is being moved.
  1217. // Using fifo to send data from fg job to bg job to write to file.
  1218. emulator.serial0_send("mkfifo /mnt/fs3/fifo\n");
  1219. emulator.serial0_send("mkfifo /mnt/fs3/fifo_intermediate\n");
  1220. emulator.serial0_send("tail -f /mnt/fs3/fifo > /mnt/fs3/fifo_intermediate &\n");
  1221. emulator.serial0_send('echo "$!" > /mnt/tailpid\n');
  1222. emulator.serial0_send('{ sed "/EOF/q" < /mnt/fs3/fifo_intermediate && kill "$(cat /mnt/tailpid)"; } >> /mnt/fs3/file &\n');
  1223. emulator.serial0_send("echo start-capture; \\\n");
  1224. emulator.serial0_send("echo untouched > /mnt/fs3/fifo; \\\n");
  1225. // File from forwarder to non-forwarder. Divert forwarder file.
  1226. emulator.serial0_send("{ mv /mnt/fs3/file /mnt/file1 &&");
  1227. emulator.serial0_send(" echo file jump to root > /mnt/fs3/fifo; }; \\\n");
  1228. // File from non-forwarder to forwarder. Divert non-forwarder file.
  1229. emulator.serial0_send("{ mv /mnt/file1 /mnt/fs3/file2 &&");
  1230. emulator.serial0_send(" echo file jump filesystems > /mnt/fs3/fifo; }; \\\n");
  1231. // File rename within the same foreign filesystem. Divert non-forwarder file.
  1232. emulator.serial0_send("{ mv /mnt/fs3/file2 /mnt/fs3/file3 && ");
  1233. emulator.serial0_send(" echo file renamed > /mnt/fs3/fifo; }; \\\n");
  1234. // File from forwarder to forwarder under directory. Divert forwarder file.
  1235. emulator.serial0_send("{ mv /mnt/fs3/file3 /mnt/a/b/fs2/dir/file4 && ");
  1236. emulator.serial0_send(" echo file move to dir > /mnt/fs3/fifo; }; \\\n");
  1237. // Directory from forwarder to forwarder.
  1238. emulator.serial0_send("{ mv /mnt/a/b/fs2/dir /mnt/fs3/fs4/fs5/dir1 && ");
  1239. emulator.serial0_send(" echo dir jump filesystems > /mnt/fs3/fifo; }; \\\n");
  1240. // Moving mountpoint across filesystems.
  1241. emulator.serial0_send("{ mv /mnt/fs3/fs4 /mnt/a/b/fs2/fs4 2>/dev/null || ");
  1242. emulator.serial0_send(" echo move mount point across - fails > /mnt/fs3/fifo; }; \\\n");
  1243. emulator.serial0_send("{ mv /mnt/fs3/fs4/fs5 /mnt/fs5 2>/dev/null || ");
  1244. emulator.serial0_send(" echo move mount point upwards - fails > /mnt/fs3/fifo; }; \\\n");
  1245. // Directory move within the same foreign filesystem.
  1246. emulator.serial0_send("{ mv /mnt/fs3/fs4/fs5/dir1 /mnt/fs3/fs4/fs5/otherdir/dir2 && ");
  1247. emulator.serial0_send(" echo dir move > /mnt/fs3/fifo; }; \\\n");
  1248. // Directory from forwarder to non-forwarder. Divert forwarder directory.
  1249. emulator.serial0_send("{ mv /mnt/fs3/fs4/fs5/otherdir/dir2 /mnt/dir3 && ");
  1250. emulator.serial0_send(" echo dir jump to root > /mnt/fs3/fifo; }; \\\n");
  1251. // Directory from non-forwarder to forwarder. Divert non-forwarder directory.
  1252. emulator.serial0_send("{ mv /mnt/dir3 /mnt/fs3/fs4/dir4 && ");
  1253. emulator.serial0_send(" echo dir jump back > /mnt/fs3/fifo; }; \\\n");
  1254. // Moving empty file (treated differently when rewriting data.
  1255. emulator.serial0_send("touch /mnt/a/b/fs2/emptyfile; \\\n");
  1256. emulator.serial0_send("{ mv /mnt/a/b/fs2/emptyfile /mnt/fs3/fs4/dir4/emptyfile && ");
  1257. emulator.serial0_send(" echo move empty file > /mnt/fs3/fifo; }; \\\n");
  1258. emulator.serial0_send("cat /mnt/fs3/fs4/dir4/emptyfile; \\\n");
  1259. emulator.serial0_send('printf "EOF\\n\\n" > /mnt/fs3/fifo & wait "$(cat /mnt/tailpid)" 2>/dev/null; \\\n');
  1260. emulator.serial0_send("cat /mnt/fs3/fs4/dir4/file4; \\\n");
  1261. emulator.serial0_send("cat /mnt/fs3/fs4/dir4/child; \\\n");
  1262. emulator.serial0_send("find /mnt | sort; \\\n");
  1263. emulator.serial0_send("echo done-move-mounted\n");
  1264. },
  1265. capture_trigger: "start-capture",
  1266. end_trigger: "done-move-mounted",
  1267. end: (capture, done) =>
  1268. {
  1269. assert_equal(capture,
  1270. "foobar\n" +
  1271. "untouched\n" +
  1272. "file jump to root\n" +
  1273. "file jump filesystems\n" +
  1274. "file renamed\n" +
  1275. "file move to dir\n" +
  1276. "dir jump filesystems\n" +
  1277. "move mount point across - fails\n" +
  1278. "move mount point upwards - fails\n" +
  1279. "dir move\n" +
  1280. "dir jump to root\n" +
  1281. "dir jump back\n" +
  1282. "move empty file\n" +
  1283. "EOF\n" +
  1284. "contents\n" +
  1285. "/mnt\n" +
  1286. "/mnt/a\n" +
  1287. "/mnt/a/b\n" +
  1288. "/mnt/a/b/fs2\n" +
  1289. "/mnt/fs3\n" +
  1290. "/mnt/fs3/fifo\n" +
  1291. "/mnt/fs3/fifo_intermediate\n" +
  1292. "/mnt/fs3/fs4\n" +
  1293. "/mnt/fs3/fs4/dir4\n" +
  1294. "/mnt/fs3/fs4/dir4/child\n" +
  1295. "/mnt/fs3/fs4/dir4/emptyfile\n" +
  1296. "/mnt/fs3/fs4/dir4/file4\n" +
  1297. "/mnt/fs3/fs4/fs5\n" +
  1298. "/mnt/fs3/fs4/fs5/otherdir\n" +
  1299. "/mnt/tailpid\n");
  1300. done();
  1301. },
  1302. },
  1303. {
  1304. name: "Hard Links Mounted",
  1305. timeout: 60,
  1306. mounts:
  1307. [
  1308. { path: "/fs1a" },
  1309. { path: "/fs1a/fs2" },
  1310. { path: "/fs1b" },
  1311. ],
  1312. start: () =>
  1313. {
  1314. emulator.serial0_send("echo foobar > /mnt/fs1a/file\n");
  1315. emulator.serial0_send("echo start-capture;\\\n");
  1316. emulator.serial0_send("{ ln /mnt/fs1a/file /mnt/fs1a/fs2/link-child 2>/dev/null || \n");
  1317. emulator.serial0_send(" echo link at child fs - fails >> /mnt/fs1a/file; };\\\n");
  1318. emulator.serial0_send("{ ln /mnt/fs1a/file /mnt/link-parent 2>/dev/null || \n");
  1319. emulator.serial0_send(" echo link at parent fs - fails >> /mnt/fs1a/file; };\\\n");
  1320. emulator.serial0_send("ln /mnt/fs1a/file /mnt/fs1a/link;\\\n");
  1321. emulator.serial0_send("echo link at common fs >> /mnt/fs1a/link;\\\n");
  1322. emulator.serial0_send("mv /mnt/fs1a/link /mnt/fs1a/link2;\\\n");
  1323. emulator.serial0_send("echo rename >> /mnt/fs1a/link2;\\\n");
  1324. emulator.serial0_send("{ mv /mnt/fs1a/link2 /mnt/link3 2>/dev/null || \n");
  1325. emulator.serial0_send(" echo jump to parent - fails >> /mnt/fs1a/link2; };\\\n");
  1326. emulator.serial0_send("{ mv /mnt/fs1a/link2 /mnt/fs1b/link3 2>/dev/null || \n");
  1327. emulator.serial0_send(" echo jump outside - fails >> /mnt/fs1a/link2; };\\\n");
  1328. emulator.serial0_send("cat /mnt/fs1a/file;\\\n");
  1329. emulator.serial0_send("echo done-hard-links-mounted\n");
  1330. },
  1331. capture_trigger: "start-capture",
  1332. end_trigger: "done-hard-links-mounted",
  1333. end: (capture, done) =>
  1334. {
  1335. assert_equal(capture,
  1336. "foobar\n" +
  1337. "link at child fs - fails\n" +
  1338. "link at parent fs - fails\n" +
  1339. "link at common fs\n" +
  1340. "rename\n" +
  1341. "jump to parent - fails\n" +
  1342. "jump outside - fails\n");
  1343. done();
  1344. },
  1345. },
  1346. {
  1347. name: "Using '..' across filesystems",
  1348. timeout: 60,
  1349. mounts:
  1350. [
  1351. { path: "/a/fs2" },
  1352. ],
  1353. start: () =>
  1354. {
  1355. emulator.serial0_send("mkdir /mnt/a/fs2/c\n");
  1356. emulator.serial0_send("echo foobar > /mnt/a/fs2/../file\n");
  1357. emulator.serial0_send("cd /mnt/a/fs2/c\n");
  1358. emulator.serial0_send("echo baz >> ../../file\n");
  1359. emulator.serial0_send("mv /mnt/a/file ../../renamed\n");
  1360. emulator.serial0_send("cp /mnt/a/renamed ../../file\n");
  1361. emulator.serial0_send("echo start-capture;\\\n");
  1362. emulator.serial0_send("cat /mnt/a/file;\\\n");
  1363. emulator.serial0_send("cat /mnt/a/renamed;\\\n");
  1364. emulator.serial0_send("rm ../../renamed;\\\n");
  1365. emulator.serial0_send("test ! -e /mnt/a/renamed && echo removed;\\\n");
  1366. emulator.serial0_send("cd /;\\\n");
  1367. emulator.serial0_send("echo done-readdir-parent-mount\n");
  1368. },
  1369. capture_trigger: "start-capture",
  1370. end_trigger:"done-readdir-parent-mount",
  1371. end: (capture, done) =>
  1372. {
  1373. assert_equal(capture,
  1374. "foobar\n" +
  1375. "baz\n" +
  1376. "foobar\n" +
  1377. "baz\n" +
  1378. "removed\n");
  1379. done();
  1380. },
  1381. },
  1382. ];
  1383. let test_num = 0;
  1384. let test_timeout = 0;
  1385. let test_has_failed = false;
  1386. const failed_tests = [];
  1387. function test_fail()
  1388. {
  1389. if(!test_has_failed)
  1390. {
  1391. test_has_failed = true;
  1392. failed_tests.push(test_num);
  1393. }
  1394. }
  1395. const emulator = new V86({
  1396. bios: { url: __dirname + "/../../bios/seabios.bin" },
  1397. vga_bios: { url: __dirname + "/../../bios/vgabios.bin" },
  1398. cdrom: { url: __dirname + "/../../images/linux4.iso" },
  1399. autostart: true,
  1400. memory_size: 64 * 1024 * 1024,
  1401. filesystem: {
  1402. baseurl: __dirname + "/testfs/",
  1403. },
  1404. disable_jit: +process.env.DISABLE_JIT,
  1405. log_level: SHOW_LOGS ? 0x400000 : 0,
  1406. });
  1407. let ran_command = false;
  1408. let line = "";
  1409. let capturing = false;
  1410. let capture = "";
  1411. let next_trigger;
  1412. let next_trigger_handler;
  1413. async function prepare_test()
  1414. {
  1415. console.log("\nPreparing test #%d: %s", test_num, tests[test_num].name);
  1416. if(tests[test_num].timeout)
  1417. {
  1418. test_timeout = setTimeout(() =>
  1419. {
  1420. console.error("[-] Test #%d (%s) took longer than %s sec. Timing out and terminating.", test_num, tests[test_num].name, tests[test_num].timeout);
  1421. process.exit(1);
  1422. }, tests[test_num].timeout * 1000);
  1423. }
  1424. console.log(" Nuking /mnt");
  1425. emulator.fs9p.RecursiveDelete("");
  1426. if(tests[test_num].use_fsjson)
  1427. {
  1428. console.log(" Reloading files from json");
  1429. emulator.fs9p.load_from_json(testfsjson);
  1430. }
  1431. console.log(" Configuring mounts");
  1432. if(tests[test_num].mounts && tests[test_num].mounts.length > 0)
  1433. {
  1434. for(const { path, baseurl, basefs } of tests[test_num].mounts)
  1435. {
  1436. await async function() {
  1437. return new Promise((resolve, reject) => {
  1438. emulator.serial0_send("mkdir -p /mnt" + path + "\n");
  1439. emulator.serial0_send("rmdir /mnt" + path + "\n");
  1440. emulator.serial0_send("echo done-premount\n");
  1441. next_trigger = "done-premount";
  1442. next_trigger_handler = resolve;
  1443. });
  1444. }();
  1445. emulator.mount_fs(path, baseurl, basefs);
  1446. }
  1447. }
  1448. console.log(" Loading additional files");
  1449. if(tests[test_num].files)
  1450. {
  1451. let remaining = tests[test_num].files.length;
  1452. for(const f of tests[test_num].files)
  1453. {
  1454. await emulator.create_file(f.file, f.data);
  1455. }
  1456. }
  1457. console.log("Starting test #%d: %s", test_num, tests[test_num].name);
  1458. capture = "";
  1459. tests[test_num].start();
  1460. if(tests[test_num].capture_trigger)
  1461. {
  1462. next_trigger = tests[test_num].capture_trigger;
  1463. next_trigger_handler = start_capture;
  1464. }
  1465. else
  1466. {
  1467. next_trigger = tests[test_num].end_trigger;
  1468. next_trigger_handler = end_test;
  1469. }
  1470. }
  1471. function start_capture()
  1472. {
  1473. console.log("Capturing...");
  1474. capture = "";
  1475. capturing = true;
  1476. next_trigger = tests[test_num].end_trigger;
  1477. next_trigger_handler = end_test;
  1478. }
  1479. function end_test()
  1480. {
  1481. capturing = false;
  1482. if(tests[test_num].timeout)
  1483. {
  1484. clearTimeout(test_timeout);
  1485. }
  1486. tests[test_num].end(capture, report_test);
  1487. }
  1488. function report_test()
  1489. {
  1490. if(!test_has_failed)
  1491. {
  1492. console.log("[+] Test #%d passed: %s", test_num, tests[test_num].name);
  1493. }
  1494. else
  1495. {
  1496. if(tests[test_num].allow_failure)
  1497. {
  1498. console.warn("Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name);
  1499. }
  1500. else
  1501. {
  1502. console.error("[-] Test #%d failed: %s", test_num, tests[test_num].name);
  1503. if(STOP_ON_FIRST_FAILURE)
  1504. {
  1505. finish_tests();
  1506. }
  1507. }
  1508. test_has_failed = false;
  1509. }
  1510. test_num++;
  1511. if(test_num < tests.length)
  1512. {
  1513. prepare_test();
  1514. }
  1515. else
  1516. {
  1517. finish_tests();
  1518. }
  1519. }
  1520. function finish_tests()
  1521. {
  1522. emulator.stop();
  1523. console.log("\nTests finished.");
  1524. if(failed_tests.length === 0)
  1525. {
  1526. console.log("All tests passed");
  1527. }
  1528. else
  1529. {
  1530. let unallowed_failure = false;
  1531. console.error("[-] Failed %d out of %d tests:", failed_tests.length, tests.length);
  1532. for(const num of failed_tests)
  1533. {
  1534. if(tests[num].allow_failure)
  1535. {
  1536. console.warn("#%d %s (failure allowed)", num, tests[num].name);
  1537. }
  1538. else
  1539. {
  1540. unallowed_failure = true;
  1541. console.error("[-] #%d %s", num, tests[num].name);
  1542. }
  1543. }
  1544. if(unallowed_failure)
  1545. {
  1546. process.exit(1);
  1547. }
  1548. }
  1549. }
  1550. emulator.bus.register("emulator-started", function()
  1551. {
  1552. console.log("Booting now, please stand by");
  1553. });
  1554. emulator.add_listener("serial0-output-byte", function(byte)
  1555. {
  1556. var chr = String.fromCharCode(byte);
  1557. if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~")
  1558. {
  1559. return;
  1560. }
  1561. let new_line = "";
  1562. let is_new_line = false;
  1563. if(chr === "\n")
  1564. {
  1565. is_new_line = true;
  1566. new_line = line;
  1567. line = "";
  1568. }
  1569. else
  1570. {
  1571. line += chr;
  1572. }
  1573. if(!ran_command && line.endsWith("~% "))
  1574. {
  1575. ran_command = true;
  1576. prepare_test();
  1577. }
  1578. else if(new_line === next_trigger)
  1579. {
  1580. next_trigger_handler();
  1581. }
  1582. else if(is_new_line && capturing)
  1583. {
  1584. capture += new_line + "\n";
  1585. console.log(" Captured: %s", new_line);
  1586. }
  1587. else if(is_new_line)
  1588. {
  1589. console.log(" Serial: %s", new_line);
  1590. }
  1591. });