summaryrefslogtreecommitdiff
path: root/bfs/bfs.ha
diff options
context:
space:
mode:
Diffstat (limited to 'bfs/bfs.ha')
-rw-r--r--bfs/bfs.ha284
1 files changed, 284 insertions, 0 deletions
diff --git a/bfs/bfs.ha b/bfs/bfs.ha
new file mode 100644
index 0000000..bff6ee1
--- /dev/null
+++ b/bfs/bfs.ha
@@ -0,0 +1,284 @@
+use io;
+
+// BFS state
+export type bfs = struct {
+ device: io::handle,
+ bootsector: *bootsector,
+};
+
+// Open a BFS from a device
+export fn open(device: io::handle) (*bfs | error | io::error) = {
+ // Allocate bootsector object
+ const bootsector = alloc(bootsector {
+ ...
+ });
+
+ // Allocate BFS object
+ const bfs = alloc(bfs {
+ device = device,
+ bootsector = bootsector,
+ });
+
+ // Pull the data
+ match (pull(bfs)) {
+ case let e: io::error =>
+ free(bootsector);
+ free(bfs);
+ return e;
+ case => void;
+ };
+
+ return bfs;
+};
+
+// Pull the bootsector from the device
+export fn pull(filesystem: *bfs) (void | io::error) = {
+ pread(filesystem.device, filesystem.bootsector: *[512]u8, 0)?;
+};
+
+// Commit the bootsector to the device
+export fn commit(filesystem: *bfs) (void | io::error) = {
+ pwrite(filesystem.device, filesystem.bootsector: *[512]u8, 0)?;
+};
+
+// Close the BFS
+export fn close(filesystem: *bfs) (void | io::error) = {
+ // Commit before closing
+ commit(filesystem)?;
+
+ // Freeing the underlying objects
+ free(filesystem.bootsector);
+ free(filesystem);
+};
+
+// Allocate a block
+export fn allocblock(self: *bfs) (u64 | io::error) = {
+ if (self.bootsector.lastblock == 0) {
+ const blockid = self.bootsector.freeblock;
+
+ self.bootsector.freeblock += 1;
+ commit(self)?;
+
+ return blockid;
+ };
+
+ const blockid = self.bootsector.lastblock;
+
+ let freeblock = free_block { ... };
+ getfreeblock(self, blockid, &freeblock)?;
+
+ if (freeblock.prevblock == 0)
+ self.bootsector.nextblock = 0;
+ self.bootsector.lastblock = freeblock.prevblock;
+
+ commit(self)?;
+
+ return blockid;
+};
+
+// Free a block given a block ID
+export fn freeblock(self: *bfs, blockid: u64) (void | io::error) = {
+ if (self.bootsector.nextblock != 0) {
+ const freeblock = free_block {
+ prevblock = blockid,
+ };
+
+ setfreeblock(self, self.bootsector.nextblock, &freeblock)?;
+ } else
+ self.bootsector.lastblock = blockid;
+
+ const freeblock = free_block { ... };
+ setfreeblock(self, blockid, &freeblock)?;
+ self.bootsector.nextblock = blockid;
+ commit(self)?;
+};
+
+// Clears an inode
+fn zeroinode(self: *bfs, inid: u64) (void | io::error) = {
+ const in = inode { ... };
+ setinode(self, inid, &in)?;
+};
+
+// Allocate a new inode
+export fn allocinode(self: *bfs) (u64 | io::error) = {
+ // If no currently free inode block,
+ if (self.bootsector.nextinblk == 0) {
+ // Allocate new inode block
+ const inblockid = allocblock(self)?;
+
+ // Initialize it
+ const inid = (inblockid << self.bootsector.blocksize) / size(inode): u64 + 1;
+ const inblock = inode_block {
+ freeinode = inid + 1,
+ freecount = (1: u64 << self.bootsector.blocksize) / size(inode): u64 - 2,
+ ...
+ };
+
+ // Insert it
+ insert_inodeblock(self, inblockid, &inblock)?;
+
+ // Write to it
+ setinblock(self, inblockid, &inblock)?;
+
+ // Zero the inode
+ zeroinode(self, inid)?;
+
+ return inid;
+ };
+
+ const inblockid = self.bootsector.nextinblk;
+
+ // Get inode block
+ let inblock = inode_block { ... };
+ getinblock(self, inblockid, &inblock)?;
+
+ // Check if inode in inode freelist
+ let inid = inblock.nextinode;
+ if (inblock.nextinode == 0) {
+ // No inode in freelist, bump freeinode
+ inid = inblock.freeinode;
+ inblock.freeinode += 1;
+ } else {
+ // Get the free inode's next inode (stored in block field)
+ let in = inode { ... };
+ getinode(self, inblock.nextinode, &in)?;
+
+ // Update the inode block first inode to it
+ inblock.nextinode = in.block;
+ };
+
+ // Decrement the inode block's freecount
+ inblock.freecount -= 1;
+
+ // If no free inode in the inode block, remove the inode block
+ // from the freelist
+ if (inblock.freecount == 0)
+ remove_inodeblock(self, inblockid, &inblock)?;
+
+ // Finally, set the block and zero the inode
+ setinblock(self, inblockid, &inblock)?;
+ zeroinode(self, inid)?;
+
+ return inid;
+};
+
+// Free an inode given its inode ID
+export fn freeinode(self: *bfs, inid: u64) (void | io::error) = {
+ const inblockid = inid >> (self.bootsector.blocksize - 6);
+
+ let inblock = inode_block { ... };
+ getinblock(self, inblockid, &inblock)?;
+
+ let in = inode { ... };
+ getinode(self, inid, &in)?;
+
+ in.block = inblock.nextinode;
+ inblock.nextinode = inid;
+ inblock.freecount += 1;
+
+ if (inblock.freecount == ((1: u64 << self.bootsector.blocksize) / size(inode): u64 - 1)) {
+ remove_inodeblock(self, inblockid, &inblock)?;
+ freeblock(self, inblockid)?;
+ return;
+ };
+
+ if (inblock.freecount == 1)
+ insert_inodeblock(self, inblockid, &inblock)?;
+
+ setinode(self, inid, &in)?;
+ setinblock(self, inblockid, &inblock)?;
+};
+
+fn insert_inodeblock(self: *bfs, inblockid: u64, inblock: *inode_block) (void | io::error) = {
+ inblock.prevblock = 0;
+
+ if (self.bootsector.nextinblk != 0) {
+ let nextinblock = inode_block { ... };
+ getinblock(self, self.bootsector.nextinblk, &nextinblock)?;
+
+ nextinblock.prevblock = inblockid;
+
+ setinblock(self, self.bootsector.nextinblk, &nextinblock)?;
+ };
+
+ inblock.nextblock = self.bootsector.nextinblk;
+ self.bootsector.nextinblk = inblockid;
+ commit(self)?;
+};
+
+fn remove_inodeblock(self: *bfs, inblockid: u64, inblock: *inode_block) (void | io::error) = {
+ if (inblock.prevblock != 0) {
+ let previnblock = inode_block { ... };
+ getinblock(self, inblock.prevblock, &previnblock)?;
+ previnblock.nextblock = inblock.nextblock;
+ setinblock(self, inblock.prevblock, &previnblock)?;
+ } else {
+ self.bootsector.nextinblk = inblock.nextblock;
+ commit(self)?;
+ };
+
+ if (inblock.nextblock != 0) {
+ let nextinblock = inode_block { ... };
+ getinblock(self, inblock.nextblock, &nextinblock)?;
+ nextinblock.prevblock = inblock.prevblock;
+ setinblock(self, inblock.nextblock, &nextinblock)?;
+ };
+
+ inblock.prevblock = 0;
+ inblock.nextblock = 0;
+};
+
+export fn open_file(self: *bfs, inid: u64) (*file | io::error) = {
+ let in = alloc(inode { ... });
+
+ match (getinode(self, inid, in)) {
+ case let e: io::error =>
+ free(in);
+ return e;
+ case => void;
+ };
+
+ return alloc(file {
+ stream_trait = &file_impl,
+ fs = self,
+ inid = inid,
+ in = in,
+ ...
+ });
+};
+
+export fn create_file(self: *bfs) (*file | io::error) = {
+ const inid = allocinode(self)?;
+
+ let file = match (open_file(self, inid)) {
+ case let e: io::error =>
+ freeinode(self, inid)!;
+ return e;
+ case let f: *file =>
+ yield f;
+ };
+
+ file.in.block = match (allocblock(self)) {
+ case let e: io::error =>
+ io::close(file)!;
+ freeinode(self, inid)!;
+ return e;
+ case let b: u64 =>
+ yield b;
+ };
+
+ return file;
+};
+
+export fn root(self: *bfs) (*file | io::error) = {
+ if (self.bootsector.rootinode == 0) {
+ const file = create_file(self)?;
+
+ self.bootsector.rootinode == file.inid;
+ commit(self)!;
+
+ return file;
+ };
+
+ return open_file(self, self.bootsector.rootinode)?;
+};