forked from osmarks/potatOS
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
d3a43eab28 | |||
3542ab25c1 | |||
2453c7756a | |||
288ef5f03a | |||
4cbe8f81d3 | |||
233bd28aab | |||
88469da2cb |
@ -93,6 +93,7 @@ Unlike most "OS"es for CC (primarily excluding Opus OS, which is actually useful
|
|||||||
- Live threat updates using our advanced algorithms.
|
- Live threat updates using our advanced algorithms.
|
||||||
- PotatOS Epenthesis' rewritten security model fixes many exploits and adds others while reducing boot times.
|
- PotatOS Epenthesis' rewritten security model fixes many exploits and adds others while reducing boot times.
|
||||||
- IPC mechanism.
|
- IPC mechanism.
|
||||||
|
- Virtual filesystems abstraction.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
@ -1,3 +1,60 @@
|
|||||||
for _, info in pairs(process.list()) do
|
local prefixes = {
|
||||||
textutils.pagedPrint(("%s %f %f"):format(info.name or info.ID, info.execution_time, info.ctime))
|
{-12, "p"},
|
||||||
|
{-9, "n"},
|
||||||
|
{-6, "u"},
|
||||||
|
{-3, "m"},
|
||||||
|
{0, ""},
|
||||||
|
{3, "k"},
|
||||||
|
{6, "M"}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function SI_prefix(value, unit)
|
||||||
|
local x = math.log(value, 10)
|
||||||
|
local last
|
||||||
|
for _, t in ipairs(prefixes) do
|
||||||
|
if t[1] > x then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
last = t
|
||||||
|
end
|
||||||
|
local dp = 2 - math.floor(x - last[1])
|
||||||
|
return (("%%.%df%%s%%s"):format(dp)):format(value / 10^(last[1]), last[2], unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
local w = term.getSize()
|
||||||
|
local rows = {}
|
||||||
|
for _, info in pairs(process.list()) do
|
||||||
|
table.insert(rows, { info.name or tostring(info.ID), SI_prefix(info.execution_time, "s"), SI_prefix(info.ctime, "s") })
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_width_per_column = {}
|
||||||
|
|
||||||
|
for _, row in ipairs(rows) do
|
||||||
|
for i, cell in ipairs(row) do
|
||||||
|
max_width_per_column[i] = math.max(max_width_per_column[i] or 0, cell:len() + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local vw_width = 0
|
||||||
|
|
||||||
|
for i = #max_width_per_column, 1, -1 do
|
||||||
|
if i > 1 then
|
||||||
|
vw_width = vw_width + max_width_per_column[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local fw_start = w - vw_width
|
||||||
|
|
||||||
|
for _, row in ipairs(rows) do
|
||||||
|
local s
|
||||||
|
for i, cell in ipairs(row) do
|
||||||
|
if i == 1 then
|
||||||
|
s = cell:sub(1, fw_start - 1) .. (" "):rep((fw_start - 1) - cell:len())
|
||||||
|
else
|
||||||
|
cell = " " .. cell
|
||||||
|
s = s .. (" "):rep(max_width_per_column[i] - cell:len()) .. cell
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
textutils.pagedPrint(s)
|
||||||
end
|
end
|
@ -24,7 +24,7 @@ print()
|
|||||||
print(snd)
|
print(snd)
|
||||||
print()
|
print()
|
||||||
if arg == "headless" then
|
if arg == "headless" then
|
||||||
ccemux.echo "ready"
|
if ccemux then ccemux.echo "ready" end
|
||||||
while true do coroutine.yield() end
|
while true do coroutine.yield() end
|
||||||
else
|
else
|
||||||
print "Press a key to continue..."
|
print "Press a key to continue..."
|
||||||
|
@ -13,6 +13,11 @@ end
|
|||||||
|
|
||||||
function sandboxlib.dispatch_if_restricted(rkey, original, restricted)
|
function sandboxlib.dispatch_if_restricted(rkey, original, restricted)
|
||||||
local out = {}
|
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
|
for k, v in pairs(original) do
|
||||||
out[k] = function(...)
|
out[k] = function(...)
|
||||||
if processrestriction(rkey) then
|
if processrestriction(rkey) then
|
||||||
|
@ -77,31 +77,10 @@ local function add_to_table(t1, t2)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local fscombine, fsgetname, fsgetdir = fs.combine, fs.getName, fs.getDir
|
||||||
-- Convert path to canonical form
|
-- Convert path to canonical form
|
||||||
local function canonicalize(path)
|
local function canonicalize(path)
|
||||||
return fs.combine(path, "")
|
return fscombine(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, "/"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Escapes lua patterns in a string. Should not be needed, but lua is stupid so the only string.replace thing is gsub
|
-- 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)), "")
|
return p:gsub("^" .. escape(canonicalize(root)), "")
|
||||||
end
|
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 function segments(path)
|
||||||
local segs, rest = {}, canonicalize(path)
|
local segs, rest = {}, canonicalize(path)
|
||||||
if rest == "" then return {} end -- otherwise we'd get "root" and ".." for some broken reason
|
if rest == "" then return {} end -- otherwise we'd get "root" and ".." for some broken reason
|
||||||
repeat
|
repeat
|
||||||
table.insert(segs, 1, fs.getName(rest))
|
table.insert(segs, 1, fsgetname(rest))
|
||||||
rest = fs.getDir(rest)
|
rest = fsgetdir(rest)
|
||||||
until rest == ""
|
until rest == ""
|
||||||
return segs
|
return segs
|
||||||
end
|
end
|
||||||
@ -134,7 +106,7 @@ end
|
|||||||
local function combine(segs)
|
local function combine(segs)
|
||||||
local out = ""
|
local out = ""
|
||||||
for _, p in pairs(segs) do
|
for _, p in pairs(segs) do
|
||||||
out = fs.combine(out, p)
|
out = fscombine(out, p)
|
||||||
end
|
end
|
||||||
return out
|
return out
|
||||||
end
|
end
|
||||||
@ -179,98 +151,176 @@ end
|
|||||||
|
|
||||||
local this_level_env = _G
|
local this_level_env = _G
|
||||||
|
|
||||||
-- Create a modified FS table which confines you to root and has some extra read-only pseudofiles.
|
-- make virtual filesystem from files (no nested directories for simplicity)
|
||||||
local function create_FS(root, overlay)
|
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 fs = fs
|
||||||
local mappings = make_mappings(root)
|
|
||||||
|
|
||||||
local vfstree = {
|
local function is_usable_node(node)
|
||||||
mount = "potatOS",
|
return node.mount or node.vfs
|
||||||
children = {
|
end
|
||||||
["disk"] = { mount = "disk" },
|
|
||||||
["rom"] = { mount = "rom" },
|
|
||||||
--["virtual_test"] = { virtual = "bees" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
local function resolve(sandbox_path)
|
local function resolve(sandbox_path, ignore_usability)
|
||||||
local segs = segments(sandbox_path)
|
local segs = segments(sandbox_path)
|
||||||
local current_tree = vfstree
|
local current_tree = vfstree
|
||||||
|
|
||||||
|
local last_usable_node, last_segs = nil, nil
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
|
if is_usable_node(current_tree) then
|
||||||
|
last_usable_node = current_tree
|
||||||
|
last_segs = copy(segs)
|
||||||
|
end
|
||||||
local seg = segs[1]
|
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)
|
table.remove(segs, 1)
|
||||||
current_tree = current_tree.children[seg]
|
current_tree = current_tree.children[seg]
|
||||||
else break end
|
else break end
|
||||||
end
|
end
|
||||||
|
if ignore_usability then return current_tree, segs end
|
||||||
|
return last_usable_node, last_segs
|
||||||
end
|
end
|
||||||
|
|
||||||
local new_overlay = {}
|
local function resolve_node_segs(node, segs)
|
||||||
for k, v in pairs(overlay) do
|
if node.mount then return fs, fscombine(node.mount, combine(segs)) end
|
||||||
new_overlay[canonicalize(k)] = v
|
return node.vfs, combine(segs)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function resolve_path(sandbox_path)
|
||||||
|
local node, segs = resolve(sandbox_path)
|
||||||
|
return resolve_node_segs(node, segs)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function lift_to_sandbox(f, n)
|
local function lift_to_sandbox(f, n)
|
||||||
return function(...)
|
return function(path)
|
||||||
local args = map(function(x) return resolve_path(x, mappings) end, {...})
|
local vfs, path = resolve_path(path)
|
||||||
return f(table.unpack(args))
|
return vfs[n](path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local new = copy_some_keys {"getDir", "getName", "combine", "complete"} (fs)
|
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)
|
function new.open(path, mode)
|
||||||
if (contains(mode, "w") or contains(mode, "a")) and new.isReadOnly(path) then
|
if (contains(mode, "w") or contains(mode, "a")) and new.isReadOnly(path) then
|
||||||
error "Access denied"
|
error "Access denied"
|
||||||
else
|
else
|
||||||
local overlay_data = path_in_overlay(new_overlay, path)
|
local vfs, path = resolve_path(path)
|
||||||
if overlay_data then
|
return vfs.open(path, mode)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function new.exists(path)
|
function new.move(src, dest)
|
||||||
if path_in_overlay(new_overlay, path) ~= nil then return true end
|
local src_vfs, src_path = resolve_path(src)
|
||||||
return fs.exists(resolve_path(path, mappings))
|
local dest_vfs, dest_path = resolve_path(dest)
|
||||||
end
|
if src_vfs == dest_vfs then
|
||||||
|
return src_vfs.move(src_path, dest_path)
|
||||||
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)
|
|
||||||
else
|
else
|
||||||
for _, v in pairs(ocontents) do
|
if src_vfs.isReadOnly(src_path) then error "Access denied" end
|
||||||
table.insert(contents, v)
|
new.copy(src, dest)
|
||||||
end
|
src_vfs.delete(src_path)
|
||||||
return contents
|
|
||||||
end
|
end
|
||||||
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 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
|
||||||
|
|
||||||
|
function new.list(path)
|
||||||
|
local node, segs = resolve(path, true)
|
||||||
|
local vfs, path = resolve_path(path)
|
||||||
|
if #segs > 0 then return vfs.list(path) end
|
||||||
|
local out = {}
|
||||||
|
local seen = {}
|
||||||
|
for k, v in pairs(node.children or {}) do
|
||||||
|
table.insert(out, k)
|
||||||
|
seen[k] = true
|
||||||
|
end
|
||||||
|
for _, v in pairs(vfs.list(path)) do
|
||||||
|
if not seen[v] then
|
||||||
|
table.insert(out, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
add_to_table(new, map(lift_to_sandbox, copy_some_keys {"isDir", "getDrive", "getSize", "getFreeSpace", "makeDir", "delete", "isDriveRoot", "exists", "isReadOnly", "attributes"} (fs)))
|
||||||
|
|
||||||
function new.find(wildcard)
|
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
|
local function recurse_spec(results, path, spec) -- From here: https://github.com/Sorroko/cclite/blob/62677542ed63bd4db212f83da1357cb953e82ce3/src/emulator/native_api.lua
|
||||||
@ -310,7 +360,7 @@ local function create_FS(root, overlay)
|
|||||||
to_add.c = new.dump(path)
|
to_add.c = new.dump(path)
|
||||||
to_add.t = "d"
|
to_add.t = "d"
|
||||||
else
|
else
|
||||||
local fh = new.open(path, "r")
|
local fh = new.open(path, "rb")
|
||||||
to_add.c = fh.readAll()
|
to_add.c = fh.readAll()
|
||||||
fh.close()
|
fh.close()
|
||||||
end
|
end
|
||||||
@ -327,7 +377,7 @@ local function create_FS(root, overlay)
|
|||||||
new.makeDir(path)
|
new.makeDir(path)
|
||||||
new.load(f.c, path)
|
new.load(f.c, path)
|
||||||
else
|
else
|
||||||
local fh = new.open(path, "w")
|
local fh = new.open(path, "wb")
|
||||||
fh.write(f.c)
|
fh.write(f.c)
|
||||||
fh.close()
|
fh.close()
|
||||||
end
|
end
|
||||||
@ -405,30 +455,11 @@ local gf, sf = getfenv, setfenv
|
|||||||
-- a map of paths to either strings containing their contents or functions returning them
|
-- a map of paths to either strings containing their contents or functions returning them
|
||||||
-- and a table of extra APIs and partial overrides for existing APIs
|
-- and a table of extra APIs and partial overrides for existing APIs
|
||||||
local function make_environment(API_overrides, current_process)
|
local function make_environment(API_overrides, current_process)
|
||||||
local env_host = string.format("YAFSS on %s", _HOST)
|
|
||||||
local environment = copy_some_keys(allowed_APIs)(_G)
|
local environment = copy_some_keys(allowed_APIs)(_G)
|
||||||
-- if function is not from within the VM, return env from within sandbox
|
|
||||||
function environment.getfenv(arg)
|
|
||||||
local env
|
|
||||||
if type(arg) == "number" then return gf() end
|
|
||||||
if not env or type(env._HOST) ~= "string" or not env._HOST == env_host then
|
|
||||||
return gf()
|
|
||||||
else
|
|
||||||
return env
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
-- I sure hope this doesn't readd the security issues!
|
||||||
Fix PS#AD2A532C
|
environment.getfenv = getfenv
|
||||||
Allowing `setfenv` to operate on any function meant that privileged code could in some cases be manipulated to leak information or operate undesirably. Due to this, we restrict it, similarly to getfenv.
|
environment.setfenv = setfenv
|
||||||
]]
|
|
||||||
function environment.setfenv(fn, env)
|
|
||||||
local nenv = gf(fn)
|
|
||||||
if not nenv or type(nenv._HOST) ~= "string" or not nenv._HOST == env_host then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return sf(fn, env)
|
|
||||||
end
|
|
||||||
|
|
||||||
local load = load
|
local load = load
|
||||||
function environment.load(code, file, mode, env)
|
function environment.load(code, file, mode, env)
|
||||||
@ -437,7 +468,6 @@ Allowing `setfenv` to operate on any function meant that privileged code could i
|
|||||||
|
|
||||||
environment._G = environment
|
environment._G = environment
|
||||||
environment._ENV = environment
|
environment._ENV = environment
|
||||||
environment._HOST = env_host
|
|
||||||
|
|
||||||
function environment.os.shutdown()
|
function environment.os.shutdown()
|
||||||
process.IPC(current_process, "power_state", "shutdown")
|
process.IPC(current_process, "power_state", "shutdown")
|
||||||
@ -485,4 +515,4 @@ local function run(API_overrides, init, logger)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return { run = run, create_FS = create_FS }
|
return { run = run, create_FS = create_FS, vfs_from_files = vfs_from_files }
|
||||||
|
92
src/main.lua
92
src/main.lua
@ -1297,26 +1297,61 @@ local function run_with_sandbox()
|
|||||||
end or v
|
end or v
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Provide many, many useful or not useful programs to the potatOS shell.
|
local yafss = require "yafss"
|
||||||
local FS_overlay = {
|
|
||||||
["secret/.pkey"] = fproxy "signing-key.tbl",
|
local drive_mounts = {
|
||||||
["secret/log"] = function() return potatOS_proxy.get_log() end,
|
children = {},
|
||||||
-- The API backing this no longer exists due to excessive server load.
|
vfs = {}
|
||||||
-----["/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
|
function drive_mounts.vfs.list(path)
|
||||||
FS_overlay[fs.combine("rom/programs", file)] = fproxy(fs.combine("bin", file))
|
local out = {}
|
||||||
|
for k, v in pairs(drive_mounts.children) do
|
||||||
|
table.insert(out, k)
|
||||||
|
end
|
||||||
|
return out
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, file in pairs(fs.list "xlib") do
|
function drive_mounts.vfs.exists(path)
|
||||||
FS_overlay[fs.combine("rom/potato_xlib", file)] = fproxy(fs.combine("xlib", file))
|
return drive_mounts.children[path] ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function drive_mounts.vfs.isDir(path) return true end
|
||||||
|
function drive_mounts.vfs.getDrive(path) return "disks" end
|
||||||
|
function drive_mounts.vfs.getSize(path) return 0 end
|
||||||
|
function drive_mounts.vfs.getFreeSpace(path) return 0 end
|
||||||
|
function drive_mounts.vfs.makeDir(path) end
|
||||||
|
function drive_mounts.vfs.delete(path) end
|
||||||
|
function drive_mounts.vfs.isReadOnly(path) return true 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
["disks"] = drive_mounts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
local API_overrides = {
|
local API_overrides = {
|
||||||
process = process,
|
process = process,
|
||||||
json = json,
|
json = json,
|
||||||
@ -1353,6 +1388,18 @@ local function run_with_sandbox()
|
|||||||
local computers = {}
|
local computers = {}
|
||||||
local compcount = 0
|
local compcount = 0
|
||||||
local signs = {}
|
local signs = {}
|
||||||
|
|
||||||
|
local function mount_disk(drive_name)
|
||||||
|
local mountpoint = peripheral.call(drive_name, "getMountPath")
|
||||||
|
if mountpoint then
|
||||||
|
drive_mounts.children[drive_name] = { mount = mountpoint }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unmount_disk(drive_name)
|
||||||
|
drive_mounts.children[drive_name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
local function add_peripheral(name)
|
local function add_peripheral(name)
|
||||||
local typ = peripheral.getType(name)
|
local typ = peripheral.getType(name)
|
||||||
if typ == "modem" then
|
if typ == "modem" then
|
||||||
@ -1362,10 +1409,16 @@ local function run_with_sandbox()
|
|||||||
compcount = compcount + 1
|
compcount = compcount + 1
|
||||||
elseif typ == "minecraft:sign" then
|
elseif typ == "minecraft:sign" then
|
||||||
signs[name] = true
|
signs[name] = true
|
||||||
|
elseif typ == "drive" then
|
||||||
|
if peripheral.call(name, "isDiskPresent") then
|
||||||
|
mount_disk(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for _, name in pairs(peripheral.getNames()) do add_peripheral(name) end
|
for _, name in pairs(peripheral.getNames()) do add_peripheral(name) end
|
||||||
local timer = os.startTimer(1)
|
local timer = os.startTimer(1)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local e, name, channel, _, message = os.pullEvent()
|
local e, name, channel, _, message = os.pullEvent()
|
||||||
if e == "peripheral" then add_peripheral(name)
|
if e == "peripheral" then add_peripheral(name)
|
||||||
@ -1373,7 +1426,8 @@ local function run_with_sandbox()
|
|||||||
local typ = peripheral.getType(name)
|
local typ = peripheral.getType(name)
|
||||||
if typ == "computer" then computers[name] = nil compcount = compcount - 1
|
if typ == "computer" then computers[name] = nil compcount = compcount - 1
|
||||||
elseif typ == "modem" then modems[name] = nil
|
elseif typ == "modem" then modems[name] = nil
|
||||||
elseif typ == "minecraft:sign" then signs[name] = nil end
|
elseif typ == "minecraft:sign" then signs[name] = nil
|
||||||
|
elseif typ == "drive" then unmount_disk(name) end
|
||||||
elseif e == "modem_message" then
|
elseif e == "modem_message" then
|
||||||
if channel == 62381 and type(message) == "string" then
|
if channel == 62381 and type(message) == "string" then
|
||||||
add_log("netd message %s", message)
|
add_log("netd message %s", message)
|
||||||
@ -1394,17 +1448,19 @@ local function run_with_sandbox()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
timer = os.startTimer(1 + math.random(0, compcount * 2))
|
timer = os.startTimer(1 + math.random(0, compcount * 2))
|
||||||
|
elseif e == "disk" then
|
||||||
|
mount_disk(name)
|
||||||
|
elseif e == "disk_eject" then
|
||||||
|
unmount_disk(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end, "netd", { grants = { [notermsentinel] = true }, restrictions = {} })
|
end, "netd", { grants = { [notermsentinel] = true }, restrictions = {} })
|
||||||
|
|
||||||
require "metatable_improvements"(potatOS_proxy.add_log, potatOS_proxy.report_incident)
|
require "metatable_improvements"(potatOS_proxy.add_log, potatOS_proxy.report_incident)
|
||||||
|
|
||||||
local yafss = require "yafss"
|
|
||||||
|
|
||||||
local fss_sentinel = sandboxlib.create_sentinel "fs-sandbox"
|
local fss_sentinel = sandboxlib.create_sentinel "fs-sandbox"
|
||||||
local debug_sentinel = sandboxlib.create_sentinel "constrained-debug"
|
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.fs = sandboxlib.dispatch_if_restricted(fss_sentinel, _G.fs, sandbox_filesystem)
|
||||||
_G.debug = sandboxlib.allow_whitelisted(debug_sentinel, _G.debug, {
|
_G.debug = sandboxlib.allow_whitelisted(debug_sentinel, _G.debug, {
|
||||||
"traceback",
|
"traceback",
|
||||||
|
@ -459,13 +459,18 @@ local old_error = error
|
|||||||
local old_os_shutdown = os.shutdown
|
local old_os_shutdown = os.shutdown
|
||||||
local old_term_redirect = term.redirect
|
local old_term_redirect = term.redirect
|
||||||
local old_term_native = term.native
|
local old_term_native = term.native
|
||||||
|
local old_printError = printError
|
||||||
function error() end
|
function error() end
|
||||||
function term.redirect() end
|
function term.redirect() end
|
||||||
function term.native() end
|
function term.native() end
|
||||||
|
function printError() end
|
||||||
function os.shutdown()
|
function os.shutdown()
|
||||||
error = old_error
|
error = old_error
|
||||||
_G.error = old_error
|
_G.error = old_error
|
||||||
_ENV.error = old_error
|
_ENV.error = old_error
|
||||||
|
printError = old_printError
|
||||||
|
_G.printError = old_printError
|
||||||
|
_ENV.printError = old_printError
|
||||||
term.native = old_term_native
|
term.native = old_term_native
|
||||||
term.redirect = old_term_redirect
|
term.redirect = old_term_redirect
|
||||||
os.shutdown = old_os_shutdown
|
os.shutdown = old_os_shutdown
|
||||||
|
@ -391,6 +391,12 @@ do
|
|||||||
local native = term.native()
|
local native = term.native()
|
||||||
local last_redirected
|
local last_redirected
|
||||||
|
|
||||||
|
-- horrors
|
||||||
|
local idmap = {}
|
||||||
|
local function termid(t)
|
||||||
|
return idmap[tostring(t.blit)]
|
||||||
|
end
|
||||||
|
|
||||||
local ix = 0
|
local ix = 0
|
||||||
process.spawn(function()
|
process.spawn(function()
|
||||||
while true do
|
while true do
|
||||||
@ -408,17 +414,11 @@ do
|
|||||||
ix = ix + 1
|
ix = ix + 1
|
||||||
process.queue_in(process.get_running().parent, "term_resize", true)
|
process.queue_in(process.get_running().parent, "term_resize", true)
|
||||||
elseif ev == "ipc" and arg2 == "redraw_native" then
|
elseif ev == "ipc" and arg2 == "redraw_native" then
|
||||||
potatOS.framebuffers[native.id].redraw()
|
potatOS.framebuffers[termid(native)].redraw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end, "termd")
|
end, "termd")
|
||||||
|
|
||||||
-- horrors
|
|
||||||
local idmap = {}
|
|
||||||
local function termid(t)
|
|
||||||
return idmap[tostring(t.blit)]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function assignid(t)
|
local function assignid(t)
|
||||||
if not termid(t) then idmap[tostring(t.blit)] = potatOS.gen_uuid() end
|
if not termid(t) then idmap[tostring(t.blit)] = potatOS.gen_uuid() end
|
||||||
end
|
end
|
||||||
@ -573,7 +573,7 @@ local function boot_require(package)
|
|||||||
return pkg
|
return pkg
|
||||||
end
|
end
|
||||||
local npackage = package:gsub("%.", "/")
|
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"})
|
local path = try_paths(search_path, {npackage, npackage .. ".lua"})
|
||||||
if path then
|
if path then
|
||||||
local ok, res = pcall(dofile, path)
|
local ok, res = pcall(dofile, path)
|
||||||
@ -589,7 +589,7 @@ _G.require = boot_require
|
|||||||
_ENV.require = boot_require
|
_ENV.require = boot_require
|
||||||
|
|
||||||
local libs = {}
|
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)
|
table.insert(libs, f)
|
||||||
end
|
end
|
||||||
table.sort(libs)
|
table.sort(libs)
|
||||||
@ -1245,7 +1245,8 @@ function potatOS.llm(prompt, max_tokens, stop_sequences)
|
|||||||
local res, err = http.post("https://gpt.osmarks.net/v1/completions", json.encode {
|
local res, err = http.post("https://gpt.osmarks.net/v1/completions", json.encode {
|
||||||
prompt = prompt,
|
prompt = prompt,
|
||||||
max_tokens = max_tokens,
|
max_tokens = max_tokens,
|
||||||
stop = stop_sequences
|
stop = stop_sequences,
|
||||||
|
client = "potatOS"
|
||||||
}, {["content-type"]="application/json"}, true)
|
}, {["content-type"]="application/json"}, true)
|
||||||
if err then
|
if err then
|
||||||
error("Server error: " .. err) -- is this right? I forgot.
|
error("Server error: " .. err) -- is this right? I forgot.
|
||||||
@ -1608,7 +1609,7 @@ function potatOS.threat_update()
|
|||||||
table.insert(out, description)
|
table.insert(out, description)
|
||||||
table.insert(out, "")
|
table.insert(out, "")
|
||||||
end
|
end
|
||||||
return (potatOS.llm(table.concat(out, "\n"), 100, {"\n\n"}):gsub("^\n", ""):gsub("\n$", ""))
|
return "current threat level is" .. (potatOS.llm(table.concat(out, "\n") .. "\ncurrent threat level is", 100, {"\n\n"}):gsub("^\n", ""):gsub("\n$", ""))
|
||||||
end
|
end
|
||||||
|
|
||||||
local fixed_context = {
|
local fixed_context = {
|
||||||
@ -1758,7 +1759,7 @@ if potatOS.registry.get "potatOS.immutable_global_scope" then
|
|||||||
end
|
end
|
||||||
|
|
||||||
process.spawn(keyboard_shortcuts, "kbsd")
|
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"
|
local autorun = potatOS.registry.get "potatOS.autorun"
|
||||||
if type(autorun) == "string" then
|
if type(autorun) == "string" then
|
||||||
autorun = load(autorun)
|
autorun = load(autorun)
|
||||||
@ -1780,7 +1781,7 @@ local function run_shell()
|
|||||||
term.clear()
|
term.clear()
|
||||||
term.setCursorPos(1, 1)
|
term.setCursorPos(1, 1)
|
||||||
potatOS.add_log "starting user shell"
|
potatOS.add_log "starting user shell"
|
||||||
os.run( {}, sShell )
|
os.run({}, sShell )
|
||||||
end
|
end
|
||||||
|
|
||||||
if potatOS.registry.get "potatOS.extended_monitoring" then process.spawn(excessive_monitoring, "extended_monitoring") end
|
if potatOS.registry.get "potatOS.extended_monitoring" then process.spawn(excessive_monitoring, "extended_monitoring") end
|
||||||
|
@ -57,7 +57,7 @@ button {
|
|||||||
<h1>Welcome to PotatOS!</h1>
|
<h1>Welcome to PotatOS!</h1>
|
||||||
<img src="/potatos.gif" id="im">
|
<img src="/potatos.gif" id="im">
|
||||||
<div>
|
<div>
|
||||||
Current build: <code>361bc871</code> (adjust LLM interface), version 771, built 2024-02-28 19:50:26 (UTC).
|
Current build: <code>9b5e8950</code> (new VFS layer), version 815, built 2024-09-03 11:37:01 (UTC).
|
||||||
</div>
|
</div>
|
||||||
<p>"PotatOS" stands for "PotatOS Otiose Transformative Advanced Technology Or Something".
|
<p>"PotatOS" stands for "PotatOS Otiose Transformative Advanced Technology Or Something".
|
||||||
<a href="https://git.osmarks.net/osmarks/potatOS">This repository</a> contains the source code for the latest version of PotatOS, "PotatOS Epenthesis".
|
<a href="https://git.osmarks.net/osmarks/potatOS">This repository</a> contains the source code for the latest version of PotatOS, "PotatOS Epenthesis".
|
||||||
@ -132,6 +132,7 @@ AI will transform the ways we work, live, play, think, become paperclips, breath
|
|||||||
<li>Live threat updates using our advanced algorithms.</li>
|
<li>Live threat updates using our advanced algorithms.</li>
|
||||||
<li>PotatOS Epenthesis' rewritten security model fixes many exploits and adds others while reducing boot times.</li>
|
<li>PotatOS Epenthesis' rewritten security model fixes many exploits and adds others while reducing boot times.</li>
|
||||||
<li>IPC mechanism.</li>
|
<li>IPC mechanism.</li>
|
||||||
|
<li>Virtual filesystems abstraction.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2>Architecture</h2>
|
<h2>Architecture</h2>
|
||||||
<p>PotatOS is internally fairly complex and somewhat eldritch.
|
<p>PotatOS is internally fairly complex and somewhat eldritch.
|
||||||
|
Loading…
Reference in New Issue
Block a user