diff options
Diffstat (limited to 'bfs/bfs.ha')
| -rw-r--r-- | bfs/bfs.ha | 284 |
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)?; +}; |
