summaryrefslogtreecommitdiff
path: root/bfs/file.ha
diff options
context:
space:
mode:
Diffstat (limited to 'bfs/file.ha')
-rw-r--r--bfs/file.ha172
1 files changed, 172 insertions, 0 deletions
diff --git a/bfs/file.ha b/bfs/file.ha
new file mode 100644
index 0000000..b8f91cb
--- /dev/null
+++ b/bfs/file.ha
@@ -0,0 +1,172 @@
+use io;
+
+// BFS file's implementation of stream
+const file_impl: io::vtable = io::vtable {
+ writer = &file_write,
+ reader = &file_read,
+ ...
+};
+
+// BFS file
+export type file = struct {
+ // File implements stream trait
+ stream_trait: io::stream,
+
+ // Filesystem that underlies the file
+ fs: *bfs,
+
+ // Inode ID associated with file
+ inid: u64,
+
+ // Inode associated with file (owned)
+ in: *inode,
+
+ // Seeking offset in BFS file
+ off: io::off,
+
+ // Block IDs of the successive pages leading to
+ // the resolution of the file content at specified index
+ page_addrs: [16]u64,
+
+ // Indices of entry in each of the successive pages
+ page_idxs: [16]u64
+};
+
+fn file_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
+ return io::EOF;
+};
+
+fn file_write(s: *io::stream, buf: const []u8) (size | io::error) = {
+ return 0z;
+};
+
+fn file_seek(s: *io::stream, off: io::off, w: io::whence) (io::off | io::error) = {
+ let file = s: *file;
+
+ switch (w) {
+ case io::whence::SET =>
+ file.off = off;
+ case io::whence::CUR =>
+ file.off += off;
+ case io::whence::END =>
+ file.off = file.in.length: io::off + off;
+ };
+
+ return file.off;
+};
+
+// Builds the indices to reach an address
+export fn file_mkindices(self: *file, off: u64) size = {
+ const endshift = self.fs.bootsector.blocksize;
+ const pageshift = endshift - 3;
+
+ const endmask = masklsb(pageshift);
+ const pagemask = endmask >> pageshift;
+
+ self.page_idxs[0] = off & endmask;
+ off >>= endshift;
+
+ let different = 0z;
+
+ for (let i = 1z; i < len(self.page_idxs); i += 1) {
+ const idx = off & pagemask;
+ off >> pageshift;
+
+ if (self.page_idxs[i] != idx)
+ different = i;
+ };
+
+ return different;
+};
+
+// Translate a file offset into a device offset
+export fn file_translate(self: *file, off: u64) (u64 | error | io::error) = {
+ // Calculate the indices
+ const different = file_mkindices(self, off);
+
+ // Calculate the level implied by given address
+ let level = 0u8;
+ for (let i = 0u8; i < 16; i += 1)
+ if (self.page_idxs[i] != 0)
+ level = i;
+
+ // If it is higher than map level of inode, miss
+ if (level > self.in.map_levels)
+ return filehole;
+
+ // Set the level address to be the base
+ self.page_addrs[self.in.map_levels] = self.in.block;
+
+ // DEBUG:
+ const different = self.in.map_levels;
+
+ // Resolve every successive page from
+ // the highest changed index in the address
+ const sz = exp2(self.fs.bootsector.blocksize);
+ let next = self.page_addrs[different];
+
+ for (let i = different; i > 0; i -= 1) {
+ self.page_addrs[i] = next;
+
+ const loc = next * sz + self.page_idxs[i]*8;
+ pread(self.fs.device, &next: *[8]u8, loc: io::off)?;
+
+ if (next == 0)
+ return filehole;
+ };
+
+ if (next == 0)
+ return filehole;
+
+ // Set the block id of final page
+ self.page_addrs[0] = next;
+
+ // Finally, return the offset in terms of physical
+ // address
+ return next * sz + self.page_idxs[0];
+};
+
+export fn file_map(self: *file, off: u64) (void | error | io::error) = {
+ file_mkindices(self, off);
+ let level = 0u8;
+ for (let i = 0u8; i < 16; i += 1)
+ if (self.page_idxs[i] != 0)
+ level = i;
+
+ // Add as many paging levels as necessary
+ for (let i = level; i < self.in.map_levels; i += 1)
+ file_levelup(self)!;
+};
+
+// Increment the level of pages used to address
+// file contents by one
+export fn file_levelup(self: *file) (void | io::error) = {
+ // Allocate new base block
+ const base = allocblock(self.fs)?;
+
+ // Write the address of previous base
+ // as first entry of new base block
+ const sz = exp2(self.fs.bootsector.blocksize);
+ pwrite(self.fs.device, &self.page_addrs[self.in.map_levels]: *[8]u8, (base * sz): io::off)!;
+
+ // Update inode
+ self.in.block = base;
+ self.in.map_levels += 1;
+ setinode(self.fs, self.inid, self.in)!;
+};
+
+// Decrement the amount of pages used to address
+// file contents by one
+export fn file_leveldown(self: *file) (void | io::error) = {
+ return;
+};
+
+// Return 1 << n
+fn exp2(n: uint) uint = {
+ return 1u << n;
+};
+
+// Create a mask with the n least significant bits on
+fn masklsb(n: u64) u64 = {
+ return ~(-1i64 << n: i64): u64;
+};