virtio_9p.js 67 KB

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