diff options
Diffstat (limited to 'kernel/storage/fs/tarfs.cpp')
-rw-r--r-- | kernel/storage/fs/tarfs.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/kernel/storage/fs/tarfs.cpp b/kernel/storage/fs/tarfs.cpp new file mode 100644 index 0000000..4b752a2 --- /dev/null +++ b/kernel/storage/fs/tarfs.cpp @@ -0,0 +1,254 @@ +#include <mercury/kernel/storage/fs/tarfs.hpp> + +//in tarfs_instance, node_id_t and directory_iter_t refer to the number +//of bytes into the block device that the info sector is located. + +namespace mercury::kernel::storage::fs { + +#define BD_TO_FS(expr) \ + { \ + bd_result _result = expr; \ + if (_result == bd_result::out_of_bounds) \ + return fs_result::fs_corrupt; \ + if (_result == bd_result::device_error) \ + return fs_result::device_error; \ + } + +#define FS_TO_FS(expr) \ + { \ + fs_result _result = expr; \ + if (_result != fs_result::success) \ + return _result; \ + } + + tarfs_instance::tarfs_instance(block_device *bd) : bd(bd) {} + + fs_result tarfs_instance::next_node( + node_id_t node, std::optional<node_id_t> &out + ) { + + uint64_t bytes; + FS_TO_FS(read_num(node + 124, 12, bytes)) + out = node + ((bytes - 1) / 512 + 2) * 512; + + uint8_t sector[512]; + BD_TO_FS(bd->read_bytes(node, 512, sector)) + for (unsigned i = 0; i < 512; ++i) + if (sector[i] != 0) + return fs_result::success; + + out = {}; + return fs_result::success; + + } + + fs_result tarfs_instance::read_full_name( + node_id_t node, utility::string &out + ) { + + out.count = 0; + out.verify_buffer_len(155); + BD_TO_FS(bd->read_bytes(node + 345, 155, out.buffer)) + + while (out.count < 155 && out.buffer[out.count] != '\0') + ++out.count; + + unsigned new_max = out.count + 100; + out.verify_buffer_len(new_max); + BD_TO_FS(bd->read_bytes(node, 100, out.buffer + out.count)) + + while (out.count < 255 && out.buffer[out.count] != '\0') + ++out.count; + + return fs_result::success; + + } + + //len <= 12. + fs_result tarfs_instance::read_num( + uint64_t offset, unsigned len, uint64_t &out + ) { + + char buf[12]; + BD_TO_FS(bd->read_bytes(offset, len, buf)) + + out = 0; + for (unsigned i = 0; i < len && buf[i] != '\0'; ++i) { + if (buf[i] < '0' || buf[i] > '7') + return fs_result::fs_corrupt; + out = out * 8 + buf[i] - '0'; + } + + return fs_result::success; + + } + + fs_result tarfs_instance::first_child_starting_at( + node_id_t parent, node_id_t start, std::optional<node_id_t> &out + ) { + + utility::string parent_full_name; + FS_TO_FS(read_full_name(parent, parent_full_name)) + + utility::string child_full_name; + out = start; + + do { + + FS_TO_FS(read_full_name(*out, child_full_name)) + + if (child_full_name.count > parent_full_name.count && + child_full_name.starts_with(parent_full_name) + ) { + if (child_full_name.buffer[child_full_name.count - 1] == '/') + --child_full_name.count; + for (unsigned i = parent_full_name.count; + i < child_full_name.count; ++i) + if (child_full_name.buffer[i] == '/') + goto next; + return fs_result::success; + } + + next: + next_node(*out, out); + + } while (out); + + return fs_result::success; + + } + + fs_result tarfs_instance::get_dir_entry(node_id_t node, dir_entry &entry) { + + utility::string full_name; + read_full_name(node, full_name); + + if (full_name.count == 2) + entry.name.count = 0; + + else { + + if (full_name.buffer[full_name.count - 1] == '/') + --full_name.count; + + unsigned last_slash = + utility::find_last(full_name.buffer, full_name.count, '/'); + entry.name.count = full_name.count - last_slash - 1; + entry.name.verify_buffer_len(entry.name.count); + + for (unsigned i = 0; i < entry.name.count; ++i) + entry.name.buffer[i] = full_name.buffer[last_slash + 1 + i]; + + } + + entry.node = node; + + char ch; + BD_TO_FS(bd->read_bytes(node + 156, 1, &ch)); + switch (ch) { + case '0': + entry.type = file_type::regular_file; + break; + case '2': + entry.type = file_type::symlink; + break; + case '5': + entry.type = file_type::directory; + break; + default: + return fs_result::fs_corrupt; + } + + if (entry.type == file_type::regular_file) { + uint64_t length; + FS_TO_FS(read_num(node + 124, 12, length)) + entry.length = length; + } + + else if (entry.type == file_type::symlink) { + utility::string target; + target.verify_buffer_len(100); + BD_TO_FS(bd->read_bytes(node + 157, 100, target.buffer)) + while (target.count < 100 && target.buffer[target.count] != '\0') + ++target.count; + entry.target = std::move(target); + } + + return fs_result::success; + + } + + fs_result tarfs_instance::get_root_node(node_id_t &out) { + + utility::string full_name; + std::optional<node_id_t> on = 0; + + do { + + FS_TO_FS(read_full_name(*on, full_name)) + if (full_name.count == 2) { + out = *on; + return fs_result::success; + } + next_node(*on, on); + + } while (on); + + return fs_result::fs_corrupt; + + } + + fs_result tarfs_instance::get_first_child( + node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter_out + ) { + + std::optional<node_id_t> child; + FS_TO_FS(first_child_starting_at(node, 0, child)) + if (!child) { + out = {}; + return fs_result::success; + } + + dir_entry entry; + FS_TO_FS(get_dir_entry(*child, entry)) + out = std::move(entry); + iter_out = (directory_iter_t)*child; + return fs_result::success; + + } + + fs_result tarfs_instance::get_next_child( + node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter + ) { + + std::optional<node_id_t> start; + FS_TO_FS(next_node((node_id_t)iter, start)) + if (!start) { + out = {}; + return fs_result::success; + } + + std::optional<node_id_t> child; + FS_TO_FS(first_child_starting_at(node, *start, child)) + if (!child) { + out = {}; + return fs_result::success; + } + + dir_entry entry; + FS_TO_FS(get_dir_entry(*child, entry)) + out = std::move(entry); + iter = (directory_iter_t)*child; + return fs_result::success; + + } + + fs_result tarfs_instance::read_bytes_from_file( + node_id_t node, uint64_t start, uint64_t count, void *into + ) { + BD_TO_FS(bd->read_bytes(node + 512 + start, count, into)) + return fs_result::success; + } + + +} |