diff options
| author | hachem <im@hachem.wtf> | 2026-10-04 03:05:36 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-10-04 03:05:36 +0200 |
| commit | 5cb9c6c03270487ee65a36828098f4f25f874bb6 (patch) | |
| tree | fcd4cb432faad25fbc8152ba4ee680fbc1af8bb1 /kernel/fs.rv | |
| parent | 7c1bd03a71cccfcf48e79d241787021b0de98e73 (diff) | |
chore: cleanup
Diffstat (limited to 'kernel/fs.rv')
| -rw-r--r-- | kernel/fs.rv | 436 |
1 files changed, 223 insertions, 213 deletions
diff --git a/kernel/fs.rv b/kernel/fs.rv index 5d72bc1..0c98c36 100644 --- a/kernel/fs.rv +++ b/kernel/fs.rv @@ -1,4 +1,4 @@ -# revelfs, a tiny fat style filesystem +# revelfs, a tiny fat style filesystem, now an object you talk to as fs:method() # # on disk layout for the 16 mib test disk (32768 sectors): # sector 0 superblock (magic + total sectors) @@ -19,242 +19,252 @@ const NAME_MAX = 24 const MAGIC = 0x53465652 # "RVFS" little endian const END = 0xffff # end of chain marker in the fat +# pure index math, no state, so theyre plain helpers not methods +fn dir_lba(i) DIR_LBA + (i shr 4) +fn dir_off(i) (i band 15) * 32 + # buf is a scratch sector for the superblock/directory/data, fat is a separate # scratch sector so fat lookups dont trample a directory sector were mid edit -global fs = { buf = 0, fat = 0, ready = :false, total = 0 } +global fs = { + buf = 0, fat = 0, ready = :false, total = 0, -fn fat_get(block) do - disk_read(FAT_LBA + (block shr 8), fs.fat) - mem_read(fs.fat + (block band 255) * 2, 2) -end + fat_get = fn(self, block) do + disk_read(FAT_LBA + (block shr 8), self.fat) + mem_read(self.fat + (block band 255) * 2, 2) + end, -fn fat_set(block, val) do - const lba = FAT_LBA + (block shr 8) - disk_read(lba, fs.fat) - mem_write(fs.fat + (block band 255) * 2, val, 2) - disk_write(lba, fs.fat) -end + fat_set = fn(self, block, val) do + const lba = FAT_LBA + (block shr 8) + disk_read(lba, self.fat) + mem_write(self.fat + (block band 255) * 2, val, 2) + disk_write(lba, self.fat) + end, -# first free data block, or 0 if the disk is full. scans a whole fat sector per -# read instead of one entry at a time so this isnt snails pace -fn fat_alloc() do - let block = DATA_LBA - while block < fs.total do - disk_read(FAT_LBA + (block shr 8), fs.fat) - let off = block band 255 - while (off < 256 and block < fs.total) do - if mem_read(fs.fat + off * 2, 2) == 0 return block - off += 1 - block += 1 + # first free data block, or 0 if full. scans a whole fat sector per read so this + # isnt snails pace + fat_alloc = fn(self) do + let block = DATA_LBA + while block < self.total do + disk_read(FAT_LBA + (block shr 8), self.fat) + let off = block band 255 + while (off < 256 and block < self.total) do + if mem_read(self.fat + off * 2, 2) == 0 return block + off += 1 + block += 1 + end end - end - 0 -end - -fn free_chain(first) do - let block = first - while (block != 0 and block != END) do - const nxt = fat_get(block) - fat_set(block, 0) - block = nxt - end -end + 0 + end, -fn dir_lba(i) DIR_LBA + (i shr 4) -fn dir_off(i) (i band 15) * 32 + free_chain = fn(self, first) do + let block = first + while (block != 0 and block != END) do + const nxt = self:fat_get(block) + self:fat_set(block, 0) + block = nxt + end + end, -# dir entry, 32 bytes: +0 used, +1 name len, +2 first block, +4 size, +8 name bytes -fn name_at(base, len) do - let out = "" - for k in 0..len do out = out ~ string.of_ascii(mem_read(base + 8 + k, 1)) end - out -end + # dir entry is 32 bytes: + # +0 used + # +1 name len + # +2 first block + # +4 size + # +8 name + name_at = fn(self, base, len) do + let out = "" + for k in 0..len do out = out ~ string.of_ascii(mem_read(base + 8 + k, 1)) end + out + end, -fn name_matches(base, target) do - const len = mem_read(base + 1, 1) - if len != target:len() return :false - for k in 0..len do - if mem_read(base + 8 + k, 1) != target[k]:ascii() return :false - end - :true -end + name_matches = fn(self, base, target) do + const len = mem_read(base + 1, 1) + if len != target:len() return :false + return self:name_at(base, len) == target + end, -fn fs_find(name) do - for i in 0..DIR_ENTRIES do - disk_read(dir_lba(i), fs.buf) - const base = fs.buf + dir_off(i) - if mem_read(base, 1) == 1 and name_matches(base, name) return i - end - return -1 -end + find = fn(self, name) do + for i in 0..DIR_ENTRIES do + disk_read(dir_lba(i), self.buf) + const base = self.buf + dir_off(i) + if mem_read(base, 1) == 1 and self:name_matches(base, name) return i + end + return -1 + end, -fn dir_alloc() do - for i in 0..DIR_ENTRIES do - disk_read(dir_lba(i), fs.buf) - if mem_read(fs.buf + dir_off(i), 1) == 0 return i - end - return -1 -end + dir_alloc = fn(self) do + for i in 0..DIR_ENTRIES do + disk_read(dir_lba(i), self.buf) + if mem_read(self.buf + dir_off(i), 1) == 0 return i + end + return -1 + end, -fn fs_init() do - fs.buf = frame_alloc() - fs.fat = frame_alloc() - fs.total = disk_sectors() - if fs.total == 0 do fs.ready = :false return :nil end - disk_read(SB_LBA, fs.buf) - fs.ready = if mem_read(fs.buf, 4) == MAGIC :true else :false -end + mount = fn(self) do + self.buf = frame_alloc() + self.fat = frame_alloc() + self.total = disk_sectors() + if self.total == 0 do self.ready = :false return :nil end + disk_read(SB_LBA, self.buf) + self.ready = if mem_read(self.buf, 4) == MAGIC :true else :false + end, -fn fs_ready() do - if fs.ready return :true - puts("no filesystem, run mkfs first\n") - :false -end + # ready is a field so the gate cant be called `ready`, this complains if theres no fs + ensure = fn(self) do + if self.ready return :true + puts("no filesystem, run mkfs first\n") + :false + end, -fn fs_format() do - if disk_sectors() == 0 do puts("mkfs: no disk\n") return :nil end - mem_zero(fs.buf, BLOCK) - mem_write(fs.buf, MAGIC, 4) - mem_write(fs.buf + 4, fs.total, 4) - disk_write(SB_LBA, fs.buf) - mem_zero(fs.buf, BLOCK) - for s in 0..DIR_SECTORS do disk_write(DIR_LBA + s, fs.buf) end - for s in 0..FAT_SECTORS do disk_write(FAT_LBA + s, fs.buf) end - fs.ready = :true - puts("formatted " ~ fs.total ~ " sectors, " ~ (fs.total shr 1) ~ " KiB\n") -end + format = fn(self) do + if disk_sectors() == 0 do puts("mkfs: no disk\n") return :nil end + mem_zero(self.buf, BLOCK) + mem_write(self.buf, MAGIC, 4) + mem_write(self.buf + 4, self.total, 4) + disk_write(SB_LBA, self.buf) + mem_zero(self.buf, BLOCK) + for s in 0..DIR_SECTORS do disk_write(DIR_LBA + s, self.buf) end + for s in 0..FAT_SECTORS do disk_write(FAT_LBA + s, self.buf) end + self.ready = :true + puts("formatted #{self.total} sectors, #{(self.total shr 1)} KiB\n") + end, -# silent core of fs_write, global so the bridge can reach it via getGlobal -global fs_store = fn(name, text) do - if fs_ready() == :false return 0 - if name:len() > NAME_MAX do puts("name too long (max " ~ NAME_MAX ~ ")\n") return 0 end - let idx = fs_find(name) - if idx >= 0 do - disk_read(dir_lba(idx), fs.buf) - free_chain(mem_read(fs.buf + dir_off(idx) + 2, 2)) - end - else do - idx = dir_alloc() - if idx < 0 do puts("directory full\n") return 0 end - end + # silent core of write, returns 1 ok / 0 fail + store = fn(self, name, text) do + if self:ensure() == :false return 0 + if name:len() > NAME_MAX do puts("name too long (max #{NAME_MAX})\n") return 0 end + let idx = self:find(name) + if idx >= 0 do + disk_read(dir_lba(idx), self.buf) + self:free_chain(mem_read(self.buf + dir_off(idx) + 2, 2)) + end + else do + idx = self:dir_alloc() + if idx < 0 do puts("directory full\n") return 0 end + end - const size = text:len() - let first = 0 - let prev = 0 - let written = 0 - while written < size do - const block = fat_alloc() - if block == 0 do puts("disk full\n") return 0 end - fat_set(block, END) # reserve - if first == 0 do first = block end - else do fat_set(prev, block) end - mem_zero(fs.buf, BLOCK) - let k = 0 - while (k < BLOCK and written < size) do - mem_write(fs.buf + k, text[written]:ascii(), 1) - k += 1 - written += 1 + const size = text:len() + let first = 0 + let prev = 0 + let written = 0 + while written < size do + const block = self:fat_alloc() + if block == 0 do puts("disk full\n") return 0 end + self:fat_set(block, END) # reserve + if first == 0 do first = block end + else do self:fat_set(prev, block) end + mem_zero(self.buf, BLOCK) + let k = 0 + while (k < BLOCK and written < size) do + mem_write(self.buf + k, text[written]:ascii(), 1) + k += 1 + written += 1 + end + disk_write(block, self.buf) + prev = block end - disk_write(block, fs.buf) - prev = block - end - disk_read(dir_lba(idx), fs.buf) - const base = fs.buf + dir_off(idx) - mem_write(base, 1, 1) - mem_write(base + 1, name:len(), 1) - mem_write(base + 2, first, 2) - mem_write(base + 4, size, 4) - for k in 0..name:len() do mem_write(base + 8 + k, name[k]:ascii(), 1) end - disk_write(dir_lba(idx), fs.buf) - return 1 -end + disk_read(dir_lba(idx), self.buf) + const base = self.buf + dir_off(idx) + mem_write(base, 1, 1) + mem_write(base + 1, name:len(), 1) + mem_write(base + 2, first, 2) + mem_write(base + 4, size, 4) + for k in 0..name:len() do mem_write(base + 8 + k, name[k]:ascii(), 1) end + disk_write(dir_lba(idx), self.buf) + return 1 + end, -# write a file, gee i wonder what a function called `fs_write` does -fn fs_write(name, text) do - if fs_store(name, text) == 1 puts("wrote " ~ name ~ " (" ~ text:len() ~ " bytes)\n") -end + # write a file, gee i wonder what a function called `write` does + write = fn(self, name, text) do + if self:store(name, text) == 1 puts("wrote #{name} (#{text:len()} bytes)\n") + end, -global fs_size = fn(name) do - const idx = fs_find(name) - if idx < 0 return -1 - disk_read(dir_lba(idx), fs.buf) - return mem_read(fs.buf + dir_off(idx) + 4, 4) -end + size = fn(self, name) do + const idx = self:find(name) + if idx < 0 return -1 + disk_read(dir_lba(idx), self.buf) + return mem_read(self.buf + dir_off(idx) + 4, 4) + end, -# read up to len bytes from offset, returned as a string (shorter at eof) -global fs_read_bytes = fn(name, offset, len) do - const idx = fs_find(name) - if idx < 0 return "" - disk_read(dir_lba(idx), fs.buf) - const base = fs.buf + dir_off(idx) - let block = mem_read(base + 2, 2) - const size = mem_read(base + 4, 4) - let stop = offset + len - if stop > size do stop = size end - let pos = 0 - let out = "" - while (block != 0 and block != END and pos < stop) do - const nxt = fat_get(block) - if (pos + BLOCK > offset and pos < stop) do - disk_read(block, fs.buf) - let k = 0 - while (k < BLOCK) do - const abs = pos + k - if (abs >= offset and abs < stop) do out = out ~ string.of_ascii(mem_read(fs.buf + k, 1)) end - k += 1 + # read up to len bytes from offset, returned as a string (shorter at eof) + read_bytes = fn(self, name, offset, len) do + const idx = self:find(name) + if idx < 0 return "" + disk_read(dir_lba(idx), self.buf) + const base = self.buf + dir_off(idx) + let block = mem_read(base + 2, 2) + const size = mem_read(base + 4, 4) + let stop = offset + len + if stop > size do stop = size end + let pos = 0 + let out = "" + while (block != 0 and block != END and pos < stop) do + const nxt = self:fat_get(block) + if (pos + BLOCK > offset and pos < stop) do + disk_read(block, self.buf) + let k = 0 + while (k < BLOCK) do + const abs = pos + k + if (abs >= offset and abs < stop) do out = out ~ string.of_ascii(mem_read(self.buf + k, 1)) end + k += 1 + end end + pos += BLOCK + block = nxt end - pos += BLOCK - block = nxt - end - return out -end + return out + end, -fn fs_cat(name) do - if fs_ready() == :false return :nil - const idx = fs_find(name) - if idx < 0 do puts("cat: " ~ name ~ ": no such file\n") return :nil end - disk_read(dir_lba(idx), fs.buf) - const base = fs.buf + dir_off(idx) - let block = mem_read(base + 2, 2) - let remaining = mem_read(base + 4, 4) - let out = "" - while (block != 0 and block != END and remaining > 0) do - const nxt = fat_get(block) # grab next before disk_read reuses fs.buf - disk_read(block, fs.buf) - const chunk = if remaining > BLOCK BLOCK else remaining - for k in 0..chunk do out = out ~ string.of_ascii(mem_read(fs.buf + k, 1)) end - remaining -= chunk - block = nxt - end - puts(out) - puts("\n") -end + cat = fn(self, name) do + if self:ensure() == :false return :nil + const idx = self:find(name) + if idx < 0 do puts("cat: #{name}: no such file\n") return :nil end + disk_read(dir_lba(idx), self.buf) + const base = self.buf + dir_off(idx) + let block = mem_read(base + 2, 2) + let remaining = mem_read(base + 4, 4) + let out = "" + while (block != 0 and block != END and remaining > 0) do + const nxt = self:fat_get(block) # grab next before disk_read reuses self.buf + disk_read(block, self.buf) + const chunk = if remaining > BLOCK BLOCK else remaining + for k in 0..chunk do out = out ~ string.of_ascii(mem_read(self.buf + k, 1)) end + remaining -= chunk + block = nxt + end + puts(out) + puts("\n") + end, -fn fs_ls() do - if fs_ready() == :false return :nil - let count = 0 - for i in 0..DIR_ENTRIES do - disk_read(dir_lba(i), fs.buf) - const base = fs.buf + dir_off(i) - if mem_read(base, 1) == 1 do - puts(" " ~ name_at(base, mem_read(base + 1, 1)) ~ " " ~ mem_read(base + 4, 4) ~ " bytes\n") - count += 1 + ls = fn(self) do + if self:ensure() == :false return :nil + let count = 0 + for i in 0..DIR_ENTRIES do + disk_read(dir_lba(i), self.buf) + const base = self.buf + dir_off(i) + if mem_read(base, 1) == 1 do + puts(" #{self:name_at(base, mem_read(base + 1, 1))} #{mem_read(base + 4, 4)} bytes\n") + count += 1 + end end - end - if count == 0 puts(" (empty)\n") -end + if count == 0 puts(" (empty)\n") + end, + + rm = fn(self, name) do + if self:ensure() == :false return :nil + const idx = self:find(name) + if idx < 0 do puts("rm: #{name}: no such file\n") return :nil end + disk_read(dir_lba(idx), self.buf) + const base = self.buf + dir_off(idx) + self:free_chain(mem_read(base + 2, 2)) + disk_read(dir_lba(idx), self.buf) + mem_write(self.buf + dir_off(idx), 0, 1) + disk_write(dir_lba(idx), self.buf) + puts("removed #{name}\n") + end, +} -fn fs_rm(name) do - if fs_ready() == :false return :nil - const idx = fs_find(name) - if idx < 0 do puts("rm: " ~ name ~ ": no such file\n") return :nil end - disk_read(dir_lba(idx), fs.buf) - const base = fs.buf + dir_off(idx) - free_chain(mem_read(base + 2, 2)) - disk_read(dir_lba(idx), fs.buf) - mem_write(fs.buf + dir_off(idx), 0, 1) - disk_write(dir_lba(idx), fs.buf) - puts("removed " ~ name ~ "\n") -end +global fs_store = fn(name, text) fs:store(name, text) +global fs_size = fn(name) fs:size(name) +global fs_read_bytes = fn(name, offset, len) fs:read_bytes(name, offset, len) |
