hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/io.rv12
-rw-r--r--kernel/seed.rv13
-rw-r--r--kernel/shell.rv4
-rw-r--r--kernel/vfs.rv345
4 files changed, 323 insertions, 51 deletions
diff --git a/kernel/io.rv b/kernel/io.rv
index af28403..43d43a9 100644
--- a/kernel/io.rv
+++ b/kernel/io.rv
@@ -3,12 +3,12 @@
global io = {
fn open(self, path, mode) -> { path: string, offset: num } do
if mode == "w" do
- fs:store(path, "")
+ vfs:store(path, "")
end
let offset = 0
if mode == "a" do
- const size = fs:size(path)
+ const size = vfs:size(path)
if (size > 0)
offset = size
end
@@ -17,23 +17,23 @@ global io = {
end,
fn read(self, handle, count) -> string do
- const data = fs:read_bytes(handle.path, handle.offset, count)
+ const data = vfs:read_bytes(handle.path, handle.offset, count)
handle.offset += data:len()
return data
end,
fn read_all(self, handle) -> string do
- const size = fs:size(handle.path)
+ const size = vfs:size(handle.path)
if (size <= handle.offset)
return ""
- const data = fs:read_bytes(handle.path, handle.offset, size - handle.offset)
+ const data = vfs:read_bytes(handle.path, handle.offset, size - handle.offset)
handle.offset += data:len()
return data
end,
fn write(self, handle, data) -> num do
- fs:write_at(handle.path, handle.offset, data)
+ vfs:write_at(handle.path, handle.offset, data)
handle.offset += data:len()
return data:len()
end,
diff --git a/kernel/seed.rv b/kernel/seed.rv
index 9dc2dc5..95ed632 100644
--- a/kernel/seed.rv
+++ b/kernel/seed.rv
@@ -23,16 +23,9 @@ fn seed_fs() do
fs:mkdir("/etc")
fs:mkdir("/home")
- fs:store("/etc/motd", "welcome to revel")
- fs:store("/etc/profile", """
- # /etc/profile runs once at shell startup, keep it quick
-
- const motd_size = fs:size("/etc/motd")
- if motd_size > 0 do
- puts(fs:read_bytes("/etc/motd", 0, motd_size))
- puts("\n")
- end
- """)
+ for index in 0..etc_count() do
+ fs:store("/etc/#{etc_name(index)}", etc_src(index))
+ end
fs:store("/home/balls.txt", "its lowk insane how big my balls are lol jk\n\t- hachem")
fs:store("/home/i have big balls.txt", "dude my balls are actually massive lol jk\n\t- hachem")
diff --git a/kernel/shell.rv b/kernel/shell.rv
index e016d18..e5f4caf 100644
--- a/kernel/shell.rv
+++ b/kernel/shell.rv
@@ -128,9 +128,7 @@ fn cmd_cd(context) do
const target = if (arg == "..") parent_of(wd.path) else expand_path(arg)
- if target == "/proc"
- or target == "/bin"
- or fs:is_dir(target) do
+ if vfs:is_dir(target) do
wd.path = target
end else do
fail("cd: #{arg}: not a directory\n")
diff --git a/kernel/vfs.rv b/kernel/vfs.rv
index 6df71cc..6547595 100644
--- a/kernel/vfs.rv
+++ b/kernel/vfs.rv
@@ -1,12 +1,25 @@
-# shitty vfs implementation
-# proc and /bin arent on disk, theyre completely virtual
+# vfs mount table
+#
+# tools only ever touch vfs not fs now since they never know or care whether a path is a real
+# file on disk or something, so they can read/write to shit like /dev/null for example
-const TASK_STATES = { "free", "ready", "dead", "blocked" }
+const T_FILE = 1
+const T_DIR = 2
+const T_CHAR = 3 # stream
+
+fn parent_dir(path) -> string do
+ const parts = path:split("/")
+ let out = ""
+ for index in 0..(parts:len() - 1) do
+ if (parts[index] != "")
+ out = out ~ "/" ~ parts[index]
+ end
+ return if (out == "") "/" else out
+end
-global vfs_owns = fn(path) -> bool do
- if (path == "/proc" or path:starts_with?("/proc/"))
- return :true
- return :false
+fn leaf(path) -> string do
+ const parts = path:split("/")
+ return parts[parts:len() - 1]
end
global parse_pid = fn(text) -> num do
@@ -24,39 +37,307 @@ global parse_pid = fn(text) -> num do
return number
end
-global vfs_ls = fn(path) do
- if path == "/proc" do
- puts(" uptime\n meminfo\n")
+const TASK_STATES = { "free", "ready", "dead", "blocked" }
+
+const procfs = {
+ writable = :false,
+
+ fn body(self, path) -> string | :nil do
+ if (path == "/proc/uptime")
+ return "#{math.floor(uptime() / 100)}s\n"
+ if (path == "/proc/meminfo")
+ return "#{(mem_free() shr 20)} MiB free / #{(mem_total() shr 20)} MiB total\n"
+
+ if path:starts_with?("/proc/") do
+ const pid = parse_pid(path[6..])
+ if (pid >= 0 and pid < task_max() and task_state(pid) != 0)
+ return "pid #{pid}: #{TASK_STATES[task_state(pid)]}\n"
+ end
+
+ return :nil
+ end,
+
+ fn entries(self, path) -> table | :nil do
+ if (path != "/proc")
+ return :nil
+
+ let out = {
+ { name = "uptime", type = T_FILE, size = self:size("/proc/uptime") },
+ { name = "meminfo", type = T_FILE, size = self:size("/proc/meminfo") },
+ }
for pid in 0..task_max() do
if (task_state(pid) != 0)
- puts(" #{pid}\n")
+ out:push({ name = "#{pid}", type = T_FILE, size = self:size("/proc/#{pid}") })
end
- end else do
- puts("ls: #{path}: no such directory\n")
- end
+
+ return out
+ end,
+
+ fn is_dir(self, path) -> bool do
+ return path == "/proc"
+ end,
+
+ fn size(self, path) -> num do
+ const b = self:body(path)
+ if (b == :nil)
+ return -1
+ return b:len()
+ end,
+
+ fn read_bytes(self, path, offset, length) -> string do
+ const b = self:body(path)
+ if (b == :nil)
+ return ""
+
+ let stop = offset + length
+ if (stop > b:len())
+ stop = b:len()
+ if (offset >= stop)
+ return ""
+
+ return b[offset..stop]
+ end,
+}
+
+const DEV_SAMPLE = 64
+
+fn dev_size(name) -> num do
+ if (name == "zero" or name == "random")
+ return DEV_SAMPLE
+ if (name == "null" or name == "full")
+ return 0
+ return -1
end
-global vfs_cat = fn(path) do
- if path == "/proc/uptime" do
- puts("#{math.floor(uptime() / 100)}s\n")
- return :nil
- end
+global devfs = {
+ writable = :true,
+ rng = 0,
- if path == "/proc/meminfo" do
- puts("#{(mem_free() shr 20)} MiB free / #{(mem_total() shr 20)} MiB total\n")
- return :nil
- end
+ # random bs go
+ fn next_byte(self) -> num do
+ if (self.rng == 0)
+ self.rng = (uptime() band 0xffffffff) bor 1
+
+ let x = self.rng
+ x = x bxor ((x shl 13) band 0xffffffff)
+ x = x bxor (x shr 17)
+ x = x bxor ((x shl 5) band 0xffffffff)
+ self.rng = x band 0xffffffff
- if path:starts_with?("/proc/") do
- const pid = parse_pid(path[6..])
- if pid >= 0 and pid < task_max() and task_state(pid) != 0 do
- puts("pid #{pid}: #{TASK_STATES[task_state(pid)]}\n")
+ return self.rng band 0xff
+ end,
+
+ fn entries(self, path) -> table | :nil do
+ if (path != "/dev")
return :nil
+
+ const names = { "null", "zero", "random", "full" }
+ let out = {}
+ for index in 0..names:len() do
+ const name = names[index]
+ out:push({ name = name, type = T_CHAR, size = dev_size(name) })
end
- puts("cat: #{path}: no such file\n")
- return :nil
- end
+ return out
+ end,
- puts("cat: #{path}: no such file\n")
-end
+ fn is_dir(self, path) -> bool do
+ return path == "/dev"
+ end,
+
+ fn size(self, path) -> num do
+ return dev_size(leaf(path))
+ end,
+
+ fn read_bytes(self, path, offset, length) -> string do
+ const name = leaf(path)
+
+ if name == "zero" do
+ let out = ""
+ for index in 0..length do out = out ~ string.of_ascii(0) end
+ return out
+ end
+
+ if name == "random" do
+ let out = ""
+ # 33..126 is the printable ascii range
+ for index in 0..length do out = out ~ string.of_ascii(33 + (self:next_byte() band 0x3f)) end
+ return out
+ end
+
+ return "" # null and full read as empty
+ end,
+
+ fn store(self, path, text) -> num do
+ const name = leaf(path)
+ if (name == "null")
+ return 1
+ if name == "full" do
+ fail("#{path}: no space left on device\n")
+ return 0
+ end
+
+ fail("#{path}: read-only device\n")
+ return 0
+ end,
+
+ fn write_at(self, path, offset, text) do
+ self:store(path, text)
+ end,
+}
+
+global vfs = {
+ mounts = {
+ { at = "/proc", fs = procfs },
+ { at = "/dev", fs = devfs },
+ },
+
+ fn mount(self, at, backend) do
+ self.mounts:push({ at = at, fs = backend })
+ end,
+
+ fn backend(self, path) do
+ let best = :nil
+ for index in 0..self.mounts:len() do
+ const m = self.mounts[index]
+ if (path == m.at or path:starts_with?(m.at ~ "/")) do
+ if (best == :nil or m.at:len() > best.at:len())
+ best = m
+ end
+ end
+ return if (best == :nil) fs else best.fs
+ end,
+
+ fn children_of(self, dir) -> table do
+ let out = {}
+ for index in 0..self.mounts:len() do
+ const at = self.mounts[index].at
+ if (parent_dir(at) == dir)
+ out:push(leaf(at))
+ end
+ return out
+ end,
+
+ fn entries(self, path) -> table | :nil do
+ let out = self:backend(path):entries(path)
+
+ const kids = self:children_of(path)
+ if kids:len() > 0 do
+ if (out == :nil)
+ out = {}
+ for index in 0..kids:len() do
+ out:push({ name = kids[index], type = T_DIR, size = 0 })
+ end
+ end
+
+ return out
+ end,
+
+ fn is_dir(self, path) -> bool do
+ for index in 0..self.mounts:len() do
+ if (self.mounts[index].at == path)
+ return :true
+ end
+ return self:backend(path):is_dir(path)
+ end,
+
+ fn size(self, path) -> num do
+ return self:backend(path):size(path)
+ end,
+
+ fn read_bytes(self, path, offset, length) -> string do
+ return self:backend(path):read_bytes(path, offset, length)
+ end,
+
+ fn store(self, path, text) -> num do
+ const b = self:backend(path)
+ if (b == fs)
+ return fs:store(path, text)
+ if b.writable == :false do
+ fail("#{path}: read-only filesystem\n")
+ return 0
+ end
+ return b:store(path, text)
+ end,
+
+ fn write_at(self, path, offset, text) do
+ const b = self:backend(path)
+ if b == fs do
+ fs:write_at(path, offset, text)
+ return :nil
+ end
+ if b.writable == :false do
+ fail("#{path}: read-only filesystem\n")
+ return :nil
+ end
+ b:write_at(path, offset, text)
+ end,
+
+ fn mkdir(self, path) do
+ if self:backend(path) != fs do
+ fail("#{path}: read-only filesystem\n")
+ return :nil
+ end
+ fs:mkdir(path)
+ end,
+
+ fn unlink(self, path) do
+ if self:backend(path) != fs do
+ fail("#{path}: read-only filesystem\n")
+ return :nil
+ end
+ fs:unlink(path)
+ end,
+
+ fn rm(self, path) do
+ if self:backend(path) != fs do
+ fail("rm: #{path}: read-only filesystem\n")
+ return :nil
+ end
+ fs:rm(path)
+ end,
+
+ fn cat(self, path) do
+ const b = self:backend(path)
+ if b == fs do
+ fs:cat(path)
+ return :nil
+ end
+
+ const sz = b:size(path)
+ if sz < 0 do
+ fail("cat: #{path}: no such file\n")
+ return :nil
+ end
+
+ puts(b:read_bytes(path, 0, sz))
+ end,
+
+ fn ls(self, path) do
+ const items = self:entries(path)
+
+ if items == :nil do
+ const sz = self:size(path)
+ if (sz >= 0)
+ puts(" #{leaf(path)} #{sz} bytes\n")
+ else
+ fail("ls: #{path}: no such directory\n")
+ return :nil
+ end
+
+ if items:len() == 0 do
+ puts(" (empty)\n")
+ return :nil
+ end
+
+ for index in 0..items:len() do
+ const entry = items[index]
+ if (entry.type == T_DIR)
+ puts(" #{entry.name}/\n")
+ else if (entry.type == T_CHAR)
+ puts(" #{entry.name}\n")
+ else
+ puts(" #{entry.name} #{entry.size} bytes\n")
+ end
+ end,
+}