diff options
| author | hachem <im@hachem.wtf> | 2026-10-11 02:16:56 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-10-11 02:16:56 +0200 |
| commit | 59794d0b999e544a94230d9562bf2b99d2ed74c7 (patch) | |
| tree | a001db5b5ebb9453d6a0e96f56b36e6ec3ccdd51 /kernel | |
| parent | 88750ed7132c08bcfb24e878c893ef7185c3c0b0 (diff) | |
feat: actual real vfs, not a faked one lmao
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/io.rv | 12 | ||||
| -rw-r--r-- | kernel/seed.rv | 13 | ||||
| -rw-r--r-- | kernel/shell.rv | 4 | ||||
| -rw-r--r-- | kernel/vfs.rv | 345 |
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, +} |
