From 288ef5f03ac940ef41b3cd7a09abce09d0708f09 Mon Sep 17 00:00:00 2001 From: osmarks Date: Tue, 3 Sep 2024 12:00:47 +0100 Subject: [PATCH] cleaner VFS abstraction --- manifest | 4 +- src/lib/sandboxlib.lua | 5 + src/lib/yafss.lua | 220 +++++++++++++++++++++++------------------ src/main.lua | 49 +++++---- src/potatobios.lua | 8 +- 5 files changed, 163 insertions(+), 123 deletions(-) diff --git a/manifest b/manifest index 0ebdf54..bde6995 100644 --- a/manifest +++ b/manifest @@ -1,2 +1,2 @@ -{"build":784,"description":"misc fixes to ctime, filesystem, overlays","files":{"LICENSES":"f3549d84d66eb53dd4a421a4341d77d3d217c1b117d67e3be8f5211adcda0952","autorun.lua":"484dfd347bd77a90a5de12267d22167dbaa293a5f634bbd59293e76e916dccd9","bin/5rot26.lua":"417891a232e325476f980d31d88edc486d526611a6350ce47fd29cca464ebf2c","bin/BSOD.lua":"a2ea9bf1e64dbc2c314e3be71f46e973c0bc2b9c482395120f5d152c6d231e86","bin/b.lua":"5123c6d1bb2b3d6c8e7b4d1b94e60d47c3b6c64c5a0fe8bda8481b718ba00602","bin/build.lua":"a990239e1db05176dd0ba56bc0179eecccd8473d88c6c618d16e72ede270e4c2","bin/ccemux.lua":"239476f58835b86bbcac31ce8af3c3acd3d198a55ab9ada78c62fbf358625a98","bin/chronometer.lua":"db5363993a04382145aef7db2fbe262f0bf10697a589e1e2d2f9ce0f87430dd8","bin/chuck.lua":"571a1eacde435bbd9f54b493149339ac48972a3a4550635b9107da0091fe888a","bin/clear_space.lua":"fc3d52563adaf0491b71227f8b6f615d604063a89830a2ab86a20d10e1a07e97","bin/ctime.lua":"c64022ef0ac641ea531567d770adb306d0d632ff7bd2b692f4915021ed6787c0","bin/est.lua":"88ab488c2ded31d67816da8c309a63ac455b461368b3b943aeeb5106b1051cc0","bin/exorcise.lua":"ae25e5939ce52620b0a1e64679a1ac47bcaaff9e323d29c6651bce24a3e58116","bin/factor.lua":"39c2f1709a4258d754278860177c7bb2ea336a8b8392a4c3017c849705e63926","bin/fortune.lua":"64a595afb9ffbaa39622b32379d8dc40d4f1fd36d38027382ed0ae130ad0c59d","bin/game_mode.lua":"f1519969a83e6c7c001d4092cbc7806ea489652ea19022425a893fb4d153572f","bin/grep.lua":"1509bc267867b933e528ab74cfbc2a15fa2df0ec7389df4f9033194ab9037865","bin/hacker.lua":"889e1a47c5ff7470ddf61d87eb7cca9750193976139d513fbe67b7625195d65a","bin/id.lua":"82131679ee35c705458660ac31ab4f5f90169b43f2377fe420a99b1d4c03a4dd","bin/init-screens.lua":"c586a7704030dc1917262f04350e094e1e0ab084793eb1643222498f6436b597","bin/intelligence.lua":"0f14f5a5fb2c6053c19c7a8fa0ba335a69a6fb3b49941c3234dac59795cd3850","bin/kristminer.lua":"7e7f9fe2a6493d584ad6926cda915e02c1c3d800dc209680898ce930d0bb0e6f","bin/livegps.lua":"c3d17d495cda01aa1261e4c4fcd43439b29af422671972117ec34f68e32c5bba","bin/lmatrix.lua":"9d5728b93069d2b763bb8e1dd2a5542995c47daca61f385644431bfc315bc2c4","bin/loading.lua":"c85f7aa1765170325155b921c1fceeb62643f552f12d41b529a22af3a67f5a97","bin/log.lua":"8a553607f81b45e3e0e6d3087735c75fbb38e585d7d1f7a9451b3cb5d0dac330","bin/lyr.lua":"5b17b8cf560ac5dbe4f458d36dff853f51103492952048f233fcad8b319c04e6","bin/maxim.lua":"a68abcb1afae04c9e2177459cd6cb35cf417e4dc80a5bc4580e7cd9b05a44602","bin/norris.lua":"e3105b98d6ac2ba038847fe4a8977db6fbf513b5de6ca3052e7ce20f79d4a314","bin/potatoflight.lua":"2fbb0b6f8d78728d8cb0ec64af1bc598bd00cb55f202378e7acdb86bba71efd1","bin/potatonet.lua":"d58e6aee25190e62a826cc9d195c4aa7e91ac43147d9a8bb8e86d139c7c5bde9","bin/potatoplex.lua":"4399d7cc33004fb21be5a0e2ab8405b8e454c004395844ce7ec42a19965fd415","bin/regset.lua":"423879f14de9efb8192ee718a1d5e129e21f50c50799651b2dcff65287808807","bin/relay.lua":"261ae6c220b83506e3326e8f2b091d246baae458ff0d2ee87512be2c4e35a75d","bin/tau.lua":"45626e749b8734bf466b89f769ec3e5544983a55086c6a165eaabdc0b010b6ac","bin/threat_update.lua":"8f186d04e0845e77a6b5049a8cd182b68f1a5f3c4eb4b3e14ad6a3563ac8dd1e","bin/tryhaskell.lua":"07810d85145da65a3e434154c79d5a9d72f2dcbe59c8d6829040fb925df878ec","bin/uninstall.lua":"12744da9213a7dba4e72c3dd0a2ac8b84c3a8315247ee4c9aa9af392dfa50b82","bin/upd.lua":"9ce75f3d428f99263392814596b4f782ea17e7f6096dad6038eac65c3fd68cd5","bin/very-uninstall.lua":"90ff8362f85e0acefff011241b0161499a3dc47fa4f2a4c21f6f0789ab9f19b5","bin/viewsource.lua":"02b4dcdb3cf064e7018117fc68a574b260e21561f1813951e4e1de8f0c9420a6","bin/wipe.lua":"2dbc079215c0c06fda182b8878f533631708d95d6148c65f24c606bc3786e2fb","bin/workspace.lua":"acc8bb6f08b243378b68ab5f611e9a6cc8216b0713343dc93ddaa9101f07ffc5","potatobios.lua":"cddb8d9db864db346f6f8864bff7bdf56dea72c730d985e625157f289bed763f","signing-key.tbl":"b32af5229c23af3bc03d538e42751b26044e404a7b1af064ed89894efe421607","startup":"a1008f5e11a1c8ec015ac4da3b783b8fb32c08a78f9dfab2b986c44e29b476ea","stdlib.hvl":"a6fd2620068f47794a9bbeed77bee3fd4962f848e6dd7c75137b30cd5665272e","update-key.hex":"8d8afb7a45833bb7d68f929421ad60a211d4d73e0ee03b24dc0106ba1de2e1a0","xlib/00_cbor.lua":"cb00cf146c439edc4caf3a6d0913f6454aa421a85b5b40d7b7f4de5cd7f16a80","xlib/01_skynet.lua":"9cb565d639a0acd7c763c3e7422482532cd0bda0cdfcc720089ab4a87e551339","xlib/02_heavlisp.lua":"82cdabd5286058c0ea4f27956f8c1144e198769c8b8ce9e91b26c930d711f710","xlib/03_lolcrypt.lua":"0ca423837248e405898c436fd7f39c1fff63ba1a1c5610f3e9fb36151a698ff5"},"sizes":{"LICENSES":4725,"autorun.lua":110077,"bin/5rot26.lua":1661,"bin/BSOD.lua":104,"bin/b.lua":34,"bin/build.lua":639,"bin/ccemux.lua":1673,"bin/chronometer.lua":1152,"bin/chuck.lua":29,"bin/clear_space.lua":105,"bin/ctime.lua":1255,"bin/est.lua":787,"bin/exorcise.lua":208,"bin/factor.lua":4244,"bin/fortune.lua":24,"bin/game_mode.lua":226,"bin/grep.lua":1196,"bin/hacker.lua":8021,"bin/id.lua":548,"bin/init-screens.lua":36,"bin/intelligence.lua":309,"bin/kristminer.lua":5566,"bin/livegps.lua":980,"bin/lmatrix.lua":1005,"bin/loading.lua":7707,"bin/log.lua":379,"bin/lyr.lua":72,"bin/maxim.lua":22,"bin/norris.lua":45,"bin/potatoflight.lua":3417,"bin/potatonet.lua":19,"bin/potatoplex.lua":6584,"bin/regset.lua":314,"bin/relay.lua":3075,"bin/tau.lua":124,"bin/threat_update.lua":1353,"bin/tryhaskell.lua":1867,"bin/uninstall.lua":166,"bin/upd.lua":16,"bin/very-uninstall.lua":80,"bin/viewsource.lua":1275,"bin/wipe.lua":73,"bin/workspace.lua":42971,"potatobios.lua":43059,"signing-key.tbl":190,"startup":13489,"stdlib.hvl":851,"update-key.hex":44,"xlib/00_cbor.lua":15281,"xlib/01_skynet.lua":3286,"xlib/02_heavlisp.lua":15643,"xlib/03_lolcrypt.lua":3206},"timestamp":1725356070} -{"hash":"9331d6eb6e7813d7b86843411411c73ba51c6816b185a3407a8bc68eb16c5302","sig":"e62538937494b1a2cf29eee636e36181e31b229613f3bcf58c66b822d7d6a4c54f13f18203f6b37e6d05"} \ No newline at end of file +{"build":803,"description":"vfs test","files":{"LICENSES":"f3549d84d66eb53dd4a421a4341d77d3d217c1b117d67e3be8f5211adcda0952","autorun.lua":"68bf1a093b1c3047f195cfd13dd1b0e23e5475e8a12985525e58c50d9cefbcad","bin/5rot26.lua":"417891a232e325476f980d31d88edc486d526611a6350ce47fd29cca464ebf2c","bin/BSOD.lua":"a2ea9bf1e64dbc2c314e3be71f46e973c0bc2b9c482395120f5d152c6d231e86","bin/b.lua":"5123c6d1bb2b3d6c8e7b4d1b94e60d47c3b6c64c5a0fe8bda8481b718ba00602","bin/build.lua":"a990239e1db05176dd0ba56bc0179eecccd8473d88c6c618d16e72ede270e4c2","bin/ccemux.lua":"239476f58835b86bbcac31ce8af3c3acd3d198a55ab9ada78c62fbf358625a98","bin/chronometer.lua":"db5363993a04382145aef7db2fbe262f0bf10697a589e1e2d2f9ce0f87430dd8","bin/chuck.lua":"571a1eacde435bbd9f54b493149339ac48972a3a4550635b9107da0091fe888a","bin/clear_space.lua":"fc3d52563adaf0491b71227f8b6f615d604063a89830a2ab86a20d10e1a07e97","bin/ctime.lua":"c64022ef0ac641ea531567d770adb306d0d632ff7bd2b692f4915021ed6787c0","bin/est.lua":"88ab488c2ded31d67816da8c309a63ac455b461368b3b943aeeb5106b1051cc0","bin/exorcise.lua":"ae25e5939ce52620b0a1e64679a1ac47bcaaff9e323d29c6651bce24a3e58116","bin/factor.lua":"39c2f1709a4258d754278860177c7bb2ea336a8b8392a4c3017c849705e63926","bin/fortune.lua":"64a595afb9ffbaa39622b32379d8dc40d4f1fd36d38027382ed0ae130ad0c59d","bin/game_mode.lua":"f1519969a83e6c7c001d4092cbc7806ea489652ea19022425a893fb4d153572f","bin/grep.lua":"1509bc267867b933e528ab74cfbc2a15fa2df0ec7389df4f9033194ab9037865","bin/hacker.lua":"889e1a47c5ff7470ddf61d87eb7cca9750193976139d513fbe67b7625195d65a","bin/id.lua":"82131679ee35c705458660ac31ab4f5f90169b43f2377fe420a99b1d4c03a4dd","bin/init-screens.lua":"c586a7704030dc1917262f04350e094e1e0ab084793eb1643222498f6436b597","bin/intelligence.lua":"0f14f5a5fb2c6053c19c7a8fa0ba335a69a6fb3b49941c3234dac59795cd3850","bin/kristminer.lua":"7e7f9fe2a6493d584ad6926cda915e02c1c3d800dc209680898ce930d0bb0e6f","bin/livegps.lua":"c3d17d495cda01aa1261e4c4fcd43439b29af422671972117ec34f68e32c5bba","bin/lmatrix.lua":"9d5728b93069d2b763bb8e1dd2a5542995c47daca61f385644431bfc315bc2c4","bin/loading.lua":"c85f7aa1765170325155b921c1fceeb62643f552f12d41b529a22af3a67f5a97","bin/log.lua":"8a553607f81b45e3e0e6d3087735c75fbb38e585d7d1f7a9451b3cb5d0dac330","bin/lyr.lua":"5b17b8cf560ac5dbe4f458d36dff853f51103492952048f233fcad8b319c04e6","bin/maxim.lua":"a68abcb1afae04c9e2177459cd6cb35cf417e4dc80a5bc4580e7cd9b05a44602","bin/norris.lua":"e3105b98d6ac2ba038847fe4a8977db6fbf513b5de6ca3052e7ce20f79d4a314","bin/potatoflight.lua":"2fbb0b6f8d78728d8cb0ec64af1bc598bd00cb55f202378e7acdb86bba71efd1","bin/potatonet.lua":"d58e6aee25190e62a826cc9d195c4aa7e91ac43147d9a8bb8e86d139c7c5bde9","bin/potatoplex.lua":"4399d7cc33004fb21be5a0e2ab8405b8e454c004395844ce7ec42a19965fd415","bin/regset.lua":"423879f14de9efb8192ee718a1d5e129e21f50c50799651b2dcff65287808807","bin/relay.lua":"261ae6c220b83506e3326e8f2b091d246baae458ff0d2ee87512be2c4e35a75d","bin/tau.lua":"45626e749b8734bf466b89f769ec3e5544983a55086c6a165eaabdc0b010b6ac","bin/threat_update.lua":"8f186d04e0845e77a6b5049a8cd182b68f1a5f3c4eb4b3e14ad6a3563ac8dd1e","bin/tryhaskell.lua":"07810d85145da65a3e434154c79d5a9d72f2dcbe59c8d6829040fb925df878ec","bin/uninstall.lua":"12744da9213a7dba4e72c3dd0a2ac8b84c3a8315247ee4c9aa9af392dfa50b82","bin/upd.lua":"9ce75f3d428f99263392814596b4f782ea17e7f6096dad6038eac65c3fd68cd5","bin/very-uninstall.lua":"90ff8362f85e0acefff011241b0161499a3dc47fa4f2a4c21f6f0789ab9f19b5","bin/viewsource.lua":"02b4dcdb3cf064e7018117fc68a574b260e21561f1813951e4e1de8f0c9420a6","bin/wipe.lua":"2dbc079215c0c06fda182b8878f533631708d95d6148c65f24c606bc3786e2fb","bin/workspace.lua":"acc8bb6f08b243378b68ab5f611e9a6cc8216b0713343dc93ddaa9101f07ffc5","potatobios.lua":"fb1dca42861ac01df033e172c6a612321455d32b7dbe2d7dd3351ada4552a78f","signing-key.tbl":"b32af5229c23af3bc03d538e42751b26044e404a7b1af064ed89894efe421607","startup":"a1008f5e11a1c8ec015ac4da3b783b8fb32c08a78f9dfab2b986c44e29b476ea","stdlib.hvl":"a6fd2620068f47794a9bbeed77bee3fd4962f848e6dd7c75137b30cd5665272e","xlib/00_cbor.lua":"cb00cf146c439edc4caf3a6d0913f6454aa421a85b5b40d7b7f4de5cd7f16a80","xlib/01_skynet.lua":"9cb565d639a0acd7c763c3e7422482532cd0bda0cdfcc720089ab4a87e551339","xlib/02_heavlisp.lua":"82cdabd5286058c0ea4f27956f8c1144e198769c8b8ce9e91b26c930d711f710","xlib/03_lolcrypt.lua":"0ca423837248e405898c436fd7f39c1fff63ba1a1c5610f3e9fb36151a698ff5"},"sizes":{"LICENSES":4725,"autorun.lua":216952,"bin/5rot26.lua":1661,"bin/BSOD.lua":104,"bin/b.lua":34,"bin/build.lua":639,"bin/ccemux.lua":1673,"bin/chronometer.lua":1152,"bin/chuck.lua":29,"bin/clear_space.lua":105,"bin/ctime.lua":1255,"bin/est.lua":787,"bin/exorcise.lua":208,"bin/factor.lua":4244,"bin/fortune.lua":24,"bin/game_mode.lua":226,"bin/grep.lua":1196,"bin/hacker.lua":8021,"bin/id.lua":548,"bin/init-screens.lua":36,"bin/intelligence.lua":309,"bin/kristminer.lua":5566,"bin/livegps.lua":980,"bin/lmatrix.lua":1005,"bin/loading.lua":7707,"bin/log.lua":379,"bin/lyr.lua":72,"bin/maxim.lua":22,"bin/norris.lua":45,"bin/potatoflight.lua":3417,"bin/potatonet.lua":19,"bin/potatoplex.lua":6584,"bin/regset.lua":314,"bin/relay.lua":3075,"bin/tau.lua":124,"bin/threat_update.lua":1353,"bin/tryhaskell.lua":1867,"bin/uninstall.lua":166,"bin/upd.lua":16,"bin/very-uninstall.lua":80,"bin/viewsource.lua":1275,"bin/wipe.lua":73,"bin/workspace.lua":42971,"potatobios.lua":69364,"signing-key.tbl":190,"startup":13489,"stdlib.hvl":851,"xlib/00_cbor.lua":15281,"xlib/01_skynet.lua":3286,"xlib/02_heavlisp.lua":15643,"xlib/03_lolcrypt.lua":3206},"timestamp":1725361194} +{"hash":"ee839c6a5a2b7a8286fce64ff18018759d0b3b38ab1a8200939c47e5588f7e72"} \ No newline at end of file diff --git a/src/lib/sandboxlib.lua b/src/lib/sandboxlib.lua index b6e0922..8ab0a0a 100644 --- a/src/lib/sandboxlib.lua +++ b/src/lib/sandboxlib.lua @@ -13,6 +13,11 @@ end function sandboxlib.dispatch_if_restricted(rkey, original, restricted) local out = {} + for k, v in pairs(restricted) do + if not original[k] then + out[k] = v + end + end for k, v in pairs(original) do out[k] = function(...) if processrestriction(rkey) then diff --git a/src/lib/yafss.lua b/src/lib/yafss.lua index b08fe4f..34d268a 100644 --- a/src/lib/yafss.lua +++ b/src/lib/yafss.lua @@ -77,31 +77,10 @@ local function add_to_table(t1, t2) end end +local fscombine, fsgetname, fsgetdir = fs.combine, fs.getName, fs.getDir -- Convert path to canonical form local function canonicalize(path) - return fs.combine(path, "") -end - --- Checks whether a path is in a directory -local function path_in(p, dir) - return starts_with(canonicalize(p), canonicalize(dir)) -end - -local function make_mappings(root) - return { - ["/disk"] = "/disk", - ["/rom"] = "/rom", - default = root - } -end - -local function get_root(path, mappings) - for mapfrom, mapto in pairs(mappings) do - if path_in(path, mapfrom) then - return mapto, mapfrom - end - end - return mappings.default, "/" + return fscombine(path, "") end -- Escapes lua patterns in a string. Should not be needed, but lua is stupid so the only string.replace thing is gsub @@ -114,19 +93,12 @@ local function strip(p, root) return p:gsub("^" .. escape(canonicalize(root)), "") end -local function resolve_path(path, mappings) - local root, to_strip = get_root(path, mappings) - local newpath = strip(fs.combine(root, path), to_strip) - if path_in(newpath, root) then return newpath end - return resolve_path(newpath, mappings) -end - local function segments(path) local segs, rest = {}, canonicalize(path) if rest == "" then return {} end -- otherwise we'd get "root" and ".." for some broken reason repeat - table.insert(segs, 1, fs.getName(rest)) - rest = fs.getDir(rest) + table.insert(segs, 1, fsgetname(rest)) + rest = fsgetdir(rest) until rest == "" return segs end @@ -134,7 +106,7 @@ end local function combine(segs) local out = "" for _, p in pairs(segs) do - out = fs.combine(out, p) + out = fscombine(out, p) end return out end @@ -179,98 +151,154 @@ end local this_level_env = _G --- Create a modified FS table which confines you to root and has some extra read-only pseudofiles. -local function create_FS(root, overlay) - local fs = fs - local mappings = make_mappings(root) - - local vfstree = { - mount = "potatOS", - children = { - ["disk"] = { mount = "disk" }, - ["rom"] = { mount = "rom" }, - --["virtual_test"] = { virtual = "bees" } - } +-- make virtual filesystem from files (no nested directories for simplicity) +local function vfs_from_files(files) + return { + list = function(path) + if path ~= "" then return {} end + local out = {} + for k, v in pairs(files) do + table.insert(out, k) + end + return out + end, + open = function(path, mode) + return make_handle(files[path]) + end, + exists = function(path) + return files[path] ~= nil or path == "" + end, + isReadOnly = function(path) + return true + end, + isDir = function(path) + if path == "" then return true end + return false + end, + getDrive = function(_) return "memory" end, + getSize = function(path) + return #files[path] + end, + getFreeSpace = function() return 0 end, + makeDir = function() end, + delete = function() end, + move = function() end, + copy = function() end, + attributes = function(path) + return { + size = #files[path], + modification = os.epoch "utc" + } + end } +end + +local function create_FS(vfstree) + local fs = fs + + local function is_usable_node(node) + return node.mount or node.vfs + end local function resolve(sandbox_path) local segs = segments(sandbox_path) local current_tree = vfstree + + local last_usable_node, last_segs = nil, nil + while true do + if is_usable_node(current_tree) then + last_usable_node = current_tree + last_segs = copy(segs) + end local seg = segs[1] - if current_tree.children and current_tree.children[seg] then + if seg and current_tree.children and current_tree.children[seg] then table.remove(segs, 1) current_tree = current_tree.children[seg] else break end end + return last_usable_node, last_segs end - local new_overlay = {} - for k, v in pairs(overlay) do - new_overlay[canonicalize(k)] = v + local function resolve_path(sandbox_path) + local node, segs = resolve(sandbox_path) + if node.mount then return fs, fscombine(node.mount, combine(segs)) end + return node.vfs, combine(segs) end local function lift_to_sandbox(f, n) - return function(...) - local args = map(function(x) return resolve_path(x, mappings) end, {...}) - return f(table.unpack(args)) + return function(path) + local vfs, path = resolve_path(path) + return vfs[n](path) end end local new = copy_some_keys {"getDir", "getName", "combine", "complete"} (fs) - function new.isReadOnly(path) - return path_in_overlay(new_overlay, path) or starts_with(canonicalize(path), "rom") - end - function new.open(path, mode) if (contains(mode, "w") or contains(mode, "a")) and new.isReadOnly(path) then error "Access denied" else - local overlay_data = path_in_overlay(new_overlay, path) - if overlay_data then - if type(overlay_data) == "function" then overlay_data = overlay_data(this_level_env) end - return make_handle(overlay_data), "YAFSS overlay" - end - return fs.open(resolve_path(path, mappings), mode) + local vfs, path = resolve_path(path) + return vfs.open(path, mode) end end - function new.exists(path) - if path_in_overlay(new_overlay, path) ~= nil then return true end - return fs.exists(resolve_path(path, mappings)) - end - - function new.overlay() - return map(function(x) - if type(x) == "function" then return x(this_level_env) - else return x end - end, new_overlay) - end - - function new.list(dir) - local sdir = canonicalize(resolve_path(dir, mappings)) - local ocontents = {} - for opath in pairs(new_overlay) do - if fs.getDir(opath) == sdir then - table.insert(ocontents, fs.getName(opath)) - end - end - local ok, contents = pcall(fs.list, sdir) - -- in case of error (nonexistent dir, probably) return overlay contents - -- very awful temporary hack until I can get a nicer treeized VFS done - if not ok then - if #ocontents > 0 then return ocontents end - error(contents) + function new.move(src, dest) + local src_vfs, src_path = resolve_path(src) + local dest_vfs, dest_path = resolve_path(dest) + if src_vfs == dest_vfs then + return src_vfs.move(src_path, dest_path) else - for _, v in pairs(ocontents) do - table.insert(contents, v) - end - return contents + if src_vfs.isReadOnly(src_path) then error "Access denied" end + new.copy(src, dest) + src_vfs.delete(src_path) end end - add_to_table(new, map(lift_to_sandbox, copy_some_keys {"isDir", "getDrive", "getSize", "getFreeSpace", "makeDir", "move", "copy", "delete", "isDriveRoot"} (fs))) + function new.copy(src, dest) + local src_vfs, src_path = resolve_path(src) + local dest_vfs, dest_path = resolve_path(dest) + if src_vfs == dest_vfs then + return src_vfs.copy(src_path, dest_path) + else + if src_vfs.isDir(src_path) then + dest_vfs.makeDir(dest_path) + for _, f in pairs(src_vfs.list(src_path)) do + new.copy(fscombine(src, f), fscombine(dest, f)) + end + else + local dest_fh = dest_vfs.open(dest_path, "wb") + local src_fh = src_vfs.open(src_path, "rb") + dest_fh.write(src_fh.readAll()) + src_fh.close() + dest_fh.close() + end + end + end + + function new.mountVFS(path, vfs) + local path = canonicalize(path) + local node, relpath = resolve(path) + while #relpath > 0 do + local seg = table.remove(relpath, 1) + if not node.children then node.children = {} end + if not node.children[seg] then node.children[seg] = {} end + node = node.children[seg] + end + node.vfs = vfs + end + + function new.unmountVFS(path) + local node, relpath = resolve(path) + if #relpath == 0 then + node.vfs = nil + else + error "Not a mountpoint" + end + end + + add_to_table(new, map(lift_to_sandbox, copy_some_keys {"isDir", "getDrive", "getSize", "getFreeSpace", "makeDir", "delete", "isDriveRoot", "exists", "isReadOnly", "list", "attributes"} (fs))) function new.find(wildcard) local function recurse_spec(results, path, spec) -- From here: https://github.com/Sorroko/cclite/blob/62677542ed63bd4db212f83da1357cb953e82ce3/src/emulator/native_api.lua @@ -310,7 +338,7 @@ local function create_FS(root, overlay) to_add.c = new.dump(path) to_add.t = "d" else - local fh = new.open(path, "r") + local fh = new.open(path, "rb") to_add.c = fh.readAll() fh.close() end @@ -327,7 +355,7 @@ local function create_FS(root, overlay) new.makeDir(path) new.load(f.c, path) else - local fh = new.open(path, "w") + local fh = new.open(path, "wb") fh.write(f.c) fh.close() end @@ -465,4 +493,4 @@ local function run(API_overrides, init, logger) end end -return { run = run, create_FS = create_FS } +return { run = run, create_FS = create_FS, vfs_from_files = vfs_from_files } diff --git a/src/main.lua b/src/main.lua index 2453e7a..1eb9045 100644 --- a/src/main.lua +++ b/src/main.lua @@ -1297,25 +1297,34 @@ local function run_with_sandbox() end or v end - -- Provide many, many useful or not useful programs to the potatOS shell. - local FS_overlay = { - ["secret/.pkey"] = fproxy "signing-key.tbl", - ["secret/log"] = function() return potatOS_proxy.get_log() end, - -- The API backing this no longer exists due to excessive server load. - -----["/rom/programs/dwarf.lua"] = "print(potatOS.dwarf())", - ["/secret/processes"] = function() - return tostring(process.list()) - end, - ["/rom/heavlisp_lib/stdlib.hvl"] = fproxy "stdlib.hvl" - } - - for _, file in pairs(fs.list "bin") do - FS_overlay[fs.combine("rom/programs", file)] = fproxy(fs.combine("bin", file)) - end + local yafss = require "yafss" - for _, file in pairs(fs.list "xlib") do - FS_overlay[fs.combine("rom/potato_xlib", file)] = fproxy(fs.combine("xlib", file)) - end + local vfstree = { + mount = "potatOS", + children = { + ["rom"] = { + mount = "rom", + children = { + ["potatOS_xlib"] = { mount = "/xlib" }, + programs = { + children = { + ["potatOS"] = { mount = "/bin" } + } + }, + ["autorun"] = { + vfs = yafss.vfs_from_files { + ["fix_path.lua"] = [[shell.setPath("/rom/programs/potatOS:"..shell.path())]], + } + }, + ["heavlisp_lib"] = { + vfs = yafss.vfs_from_files { + ["stdlib.hvl"] = fproxy "stdlib.hvl" + } + } + } + } + } + } local API_overrides = { process = process, @@ -1400,11 +1409,9 @@ local function run_with_sandbox() require "metatable_improvements"(potatOS_proxy.add_log, potatOS_proxy.report_incident) - local yafss = require "yafss" - local fss_sentinel = sandboxlib.create_sentinel "fs-sandbox" local debug_sentinel = sandboxlib.create_sentinel "constrained-debug" - local sandbox_filesystem = yafss.create_FS("potatOS", FS_overlay) + local sandbox_filesystem = yafss.create_FS(vfstree) _G.fs = sandboxlib.dispatch_if_restricted(fss_sentinel, _G.fs, sandbox_filesystem) _G.debug = sandboxlib.allow_whitelisted(debug_sentinel, _G.debug, { "traceback", diff --git a/src/potatobios.lua b/src/potatobios.lua index 3fd5b33..410ac89 100644 --- a/src/potatobios.lua +++ b/src/potatobios.lua @@ -573,7 +573,7 @@ local function boot_require(package) return pkg end local npackage = package:gsub("%.", "/") - for _, search_path in next, {"/", "lib", "rom/modules/main", "rom/modules/turtle", "rom/modules/command", "rom/potato_xlib"} do + for _, search_path in next, {"/", "lib", "rom/modules/main", "rom/modules/turtle", "rom/modules/command", "rom/potatOS_xlib"} do local path = try_paths(search_path, {npackage, npackage .. ".lua"}) if path then local ok, res = pcall(dofile, path) @@ -589,7 +589,7 @@ _G.require = boot_require _ENV.require = boot_require local libs = {} -for _, f in pairs(fs.list "rom/potato_xlib") do +for _, f in pairs(fs.list "rom/potatOS_xlib") do table.insert(libs, f) end table.sort(libs) @@ -1759,7 +1759,7 @@ if potatOS.registry.get "potatOS.immutable_global_scope" then end process.spawn(keyboard_shortcuts, "kbsd") -if http.websocket then process.spawn(skynet.listen, "skynetd") process.spawn(potatoNET, "systemd-potatod") end +if skynet and http.websocket then process.spawn(skynet.listen, "skynetd") process.spawn(potatoNET, "systemd-potatod") end local autorun = potatOS.registry.get "potatOS.autorun" if type(autorun) == "string" then autorun = load(autorun) @@ -1781,7 +1781,7 @@ local function run_shell() term.clear() term.setCursorPos(1, 1) potatOS.add_log "starting user shell" - os.run( {}, sShell ) + os.run({}, sShell ) end if potatOS.registry.get "potatOS.extended_monitoring" then process.spawn(excessive_monitoring, "extended_monitoring") end