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; };