From 882e74b2191c059a9226cbd8bcb51c97da36247c Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Fri, 12 Jan 2024 20:39:21 -0500 Subject: rewrite file system layer --- kernel/bd/memory.cpp | 27 ---- kernel/entry.cpp | 151 ++++++++++------------ kernel/fs/tarfs.cpp | 296 ------------------------------------------- kernel/storage.cpp | 225 +++----------------------------- kernel/storage/bd/memory.cpp | 21 +++ kernel/storage/fs/tarfs.cpp | 254 +++++++++++++++++++++++++++++++++++++ kernel/terminal.cpp | 6 +- kernel/vfile.cpp | 192 ++++++++++++++++++++++++++++ 8 files changed, 558 insertions(+), 614 deletions(-) delete mode 100644 kernel/bd/memory.cpp delete mode 100644 kernel/fs/tarfs.cpp create mode 100644 kernel/storage/bd/memory.cpp create mode 100644 kernel/storage/fs/tarfs.cpp create mode 100644 kernel/vfile.cpp (limited to 'kernel') diff --git a/kernel/bd/memory.cpp b/kernel/bd/memory.cpp deleted file mode 100644 index 1015e40..0000000 --- a/kernel/bd/memory.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include - -namespace mercury::kernel::bd { - - memory::memory(void *buffer, size_t buffer_len) : buffer((uint8_t *)buffer) { - block_size = 1; - block_count = buffer_len; - //block cache will never be used, since the block size is 1. - } - - storage::io_result memory::read_blocks_no_cache( - uint64_t start, uint64_t count, void *into - ) { - for (uint64_t i = 0; i < count; ++i) - ((uint8_t *)into)[i] = buffer[start + i]; - return storage::io_result::success; - } - - storage::io_result memory::write_blocks_no_cache( - uint64_t start, uint64_t count, const void *into - ) { - for (uint64_t i = 0; i < count; ++i) - buffer[start + i] = ((uint8_t *)into)[i]; - return storage::io_result::success; - } - -} diff --git a/kernel/entry.cpp b/kernel/entry.cpp index aa6568a..445d560 100644 --- a/kernel/entry.cpp +++ b/kernel/entry.cpp @@ -1,9 +1,10 @@ +#include +#include #include -#include -#include #include #include #include +#include using namespace mercury::kernel; @@ -182,78 +183,67 @@ extern "C" [[noreturn]] void entry() { [[noreturn]] static void print_and_halt(const char *msg) { terminal::put_string_sz(msg); while (1) - ; + asm ("hlt"); +} + +static void draw_indent(unsigned indent) { + for (unsigned i = 0; i < indent; ++i) + terminal::put_char(' '); } static void dir_tree( - storage::block_device *bd, storage::node_id_t node, unsigned indent + const vfile::vfile &root, const vfile::vfile &f, unsigned indent ) { - storage::node_id_t child_node; - storage::directory_iter_t dir_iter; - storage::io_result result = - bd->mounted_as->get_first_child(node, child_node, dir_iter); - if (result == storage::io_result::not_found) { - for (unsigned i = 0; i < indent; ++i) - terminal::put_char(' '); - terminal::put_string_sz("[empty]\n"); - return; - } - else if (result) - print_and_halt("error getting first child."); - - while (true) { - - unsigned name_len; - if (bd->mounted_as->get_name_length(child_node, name_len)) - print_and_halt("failed to get name length."); - char *name_buf = new char[name_len]; - if (bd->mounted_as->get_name(child_node, name_buf, name_len)) - print_and_halt("failed to get name."); - - for (unsigned i = 0; i < indent; ++i) - terminal::put_char(' '); - terminal::put_string(name_buf, name_len); - terminal::put_string(":\n", 2); - - delete[] name_buf; - - storage::file_type type; - if (bd->mounted_as->get_file_type(child_node, type)) - print_and_halt("failed to get type."); - - if (type == storage::file_type::directory) - dir_tree(bd, child_node, indent + 2); - - else { - uint64_t len; - if (bd->mounted_as->get_file_length(child_node, len)) - print_and_halt("failed to get length."); - char *contents = new char[len]; - if (bd->mounted_as->read_bytes_from_file(child_node, 0, len, contents)) - print_and_halt("failed to read file."); - - if (contents[len - 1] == '\n') - len--; - - for (unsigned i = 0; i < indent + 2; ++i) - terminal::put_char(' '); - if (len == 0) + draw_indent(indent); + terminal::put_string(f.dir_entry.name); + terminal::put_char('\n'); + + std::optional followed; + utility::vector children; + + switch (f.dir_entry.type) { + + case storage::file_type::regular_file: + draw_indent(indent + 2); + terminal::put_string_sz("type: regular file\n"); + draw_indent(indent + 2); + terminal::put_string_sz("length: "); + terminal::put_int_decimal(f.dir_entry.length); + terminal::put_string_sz(" bytes\n"); + break; + + case storage::file_type::symlink: + draw_indent(indent + 2); + terminal::put_string_sz("type: symlink\n"); + draw_indent(indent + 2); + terminal::put_string_sz("target: "); + terminal::put_string(f.dir_entry.target); + terminal::put_char('\n'); + if (f.follow_symlinks(root, followed) != storage::fs_result::success) + print_and_halt("failed to follow symlink."); + if (!followed) { + draw_indent(indent + 4); + terminal::put_string_sz("[broken]\n"); + } + dir_tree(root, *followed, indent + 4); + break; + + case storage::file_type::directory: + if (f.get_children(children) != storage::fs_result::success) + print_and_halt("failed to get children."); + draw_indent(indent + 2); + terminal::put_string_sz("type: directory\n"); + draw_indent(indent + 2); + terminal::put_string_sz("children:\n"); + if (children.count == 0) { + draw_indent(indent + 4); terminal::put_string_sz("[empty]\n"); - else { - terminal::put_string(contents, len); - terminal::put_char('\n'); } - - delete[] contents; - } - - storage::io_result result - = bd->mounted_as->get_next_child(node, child_node, dir_iter); - if (result == storage::io_result::not_found) - return; - else if (result) - print_and_halt("error getting next child."); + else + for (unsigned i = 0; i < children.count; ++i) + dir_tree(root, children.buffer[i], indent + 4); + break; } @@ -262,16 +252,10 @@ static void dir_tree( [[noreturn]] static void with_kernel_p4() { terminal::init_terminal(); - storage::init_storage(); - - storage::block_device *initfs_bd = new bd::memory(initfs, initfs_len); - storage::block_devices->insert_end(initfs_bd); - storage::canon_path root; - storage::canonize_path("/", 1, root); - - if (storage::mount_device(initfs_bd, root, &fs::tarfs_mounter)) - print_and_halt("failed to mount initfs."); + storage::bd::memory initfs_bd(initfs, initfs_len); + storage::fs::tarfs_instance initfs_fs(&initfs_bd); + initfs_bd.mounted_as = &initfs_fs; terminal::put_string_sz("kernel initialization complete.\n"); @@ -283,12 +267,17 @@ static void dir_tree( terminal::put_int_decimal(free_pram_kib); terminal::put_string_sz(" kiB physical memory free.\n"); - storage::node_id_t root_node; - if (initfs_bd->mounted_as->get_root_node(root_node)) - print_and_halt("failed to get root initfs node."); + vfile::vfile root; + root.bd = &initfs_bd; + root.dir_entry.type = storage::file_type::directory; + root.path.absolute = true; + + if (initfs_fs.get_root_node(root.dir_entry.node) != + storage::fs_result::success) + print_and_halt("failed to get root node."); - terminal::put_string_sz("initfs:\n"); - dir_tree(initfs_bd, root_node, 2); + terminal::put_string_sz("initfs"); + dir_tree(root, root, 0); print_and_halt("halting."); } diff --git a/kernel/fs/tarfs.cpp b/kernel/fs/tarfs.cpp deleted file mode 100644 index 7508c03..0000000 --- a/kernel/fs/tarfs.cpp +++ /dev/null @@ -1,296 +0,0 @@ -#include - -//in fs::tarfs_instance, storage::node_id_t refers to the number -//of bytes into the block device that the info sector is located - -namespace mercury::kernel::fs { - - storage::io_result tarfs_mounter( - storage::block_device *bd, storage::file_system_instance *&fs_out - ) { - fs_out = new tarfs_instance(bd); - return storage::io_result::success; - } - - tarfs_instance::tarfs_instance(storage::block_device *bd) : bd(bd) {} - - storage::io_result tarfs_instance::next_node(storage::node_id_t &node) { - uint64_t file_length; - storage::io_result result = read_num(node + 124, 12, file_length); - if (result != storage::io_result::success) - return result; - node += ((file_length - 1) / 512 + 2) * 512; - uint8_t sector[512]; - result = bd->read_bytes(node, 512, sector); - if (result != storage::io_result::success) - return result; - for (unsigned i = 0; i < 512; ++i) - if (sector[i] != 0) - return storage::io_result::success; - return storage::io_result::out_of_bounds; - } - - storage::io_result tarfs_instance::read_name( - storage::node_id_t node, char *name_buf, unsigned &name_len_out - ) { - name_len_out = 0; - storage::io_result result = bd->read_bytes(node + 345, 155, name_buf); - if (result != storage::io_result::success) - return result; - while (name_buf[name_len_out] && name_len_out < 155) - ++name_len_out; - result = bd->read_bytes(node, 100, name_buf + name_len_out); - if (result != storage::io_result::success) - return result; - unsigned new_limit = name_len_out + 100; - while (name_buf[name_len_out] && name_len_out < new_limit) - ++name_len_out; - return storage::io_result::success; - } - - storage::io_result tarfs_instance::read_num( - uint64_t offset, unsigned len, uint64_t &out - ) { - - //len <= 12 - char buffer[12]; - storage::io_result result = bd->read_bytes(offset, len, buffer); - if (result != storage::io_result::success) - return result; - out = 0; - - for (unsigned i = 0; i < len; ++i) { - if (!buffer[i]) - return i == 0 ? storage::io_result::fs_corrupt - : storage::io_result::success; - if (buffer[i] < '0' || buffer[i] > '7') - return storage::io_result::fs_corrupt; - out = out * 8 + buffer[i] - '0'; - } - - return storage::io_result::success; - - } - -#define RETURN_MAYBE_NOT_FOUND(expr) \ - { \ - storage::io_result _result = expr; \ - if (_result == storage::io_result::out_of_bounds) \ - return storage::io_result::not_found; \ - if (_result != storage::io_result::success) \ - return _result; \ - } - -#define RETURN_NOT_SUCCESS(expr) \ - { \ - storage::io_result _result = expr; \ - if (_result != storage::io_result::success) \ - return _result; \ - } - - storage::io_result tarfs_instance::get_root_node(storage::node_id_t &out) { - out = 0; - while (true) { - char name_buf[255]; - unsigned name_len; - RETURN_MAYBE_NOT_FOUND(read_name(out, name_buf, name_len)) - if (name_len == 2 && name_buf[0] == '.' && name_buf[1] == '/') - return storage::io_result::success; - RETURN_MAYBE_NOT_FOUND(next_node(out)) - } - } - - storage::io_result tarfs_instance::get_first_child(storage::node_id_t node, - storage::node_id_t &out, storage::directory_iter_t &iter_out - ) { - - char name_buf[255]; - unsigned name_len; - RETURN_NOT_SUCCESS(read_name(node, name_buf, name_len)) - - out = 0; - while (true) { - - char cand_name_buf[255]; - unsigned cand_name_len; - RETURN_MAYBE_NOT_FOUND(read_name(out, cand_name_buf, cand_name_len)) - - if (cand_name_len > name_len && utility::starts_with( - cand_name_buf, cand_name_len, name_buf, name_len - )) { - const char *rem = cand_name_buf + name_len; - unsigned rem_len = cand_name_len - name_len; - unsigned slash = utility::find(rem, rem_len, '/'); - if (slash == rem_len - 1 || slash == rem_len) { - iter_out = out; - return storage::io_result::success; - } - } - - RETURN_MAYBE_NOT_FOUND(next_node(out)) - - } - - } - - storage::io_result tarfs_instance::get_next_child(storage::node_id_t node, - storage::node_id_t &out, storage::directory_iter_t &iter - ) { - - char name_buf[255]; - unsigned name_len; - RETURN_NOT_SUCCESS(read_name(node, name_buf, name_len)) - - out = iter; - - RETURN_MAYBE_NOT_FOUND(next_node(out)) - - while (true) { - - char cand_name_buf[255]; - unsigned cand_name_len; - RETURN_MAYBE_NOT_FOUND(read_name(out, cand_name_buf, cand_name_len)) - - if (cand_name_len > name_len && utility::starts_with( - cand_name_buf, cand_name_len, name_buf, name_len - )) { - const char *rem = cand_name_buf + name_len; - unsigned rem_len = cand_name_len - name_len; - unsigned slash = utility::find(rem, rem_len, '/'); - if (slash == rem_len - 1 || slash == rem_len) { - iter = out; - return storage::io_result::success; - } - } - - RETURN_MAYBE_NOT_FOUND(next_node(out)) - - } - - } - - storage::io_result tarfs_instance::get_child(storage::node_id_t node, - storage::node_id_t &out, const char *name, unsigned name_len - ) { - - char full_name[255]; - unsigned full_name_len; - RETURN_MAYBE_NOT_FOUND(read_name(node, full_name, full_name_len)) - - if (full_name_len + name_len > 255) - return storage::io_result::not_supported; - - for (unsigned i = 0; i < name_len; ++i) - full_name[full_name_len + i] = name[i]; - full_name_len += name_len; - - out = 0; - while (true) { - - char cand_name[255]; - unsigned cand_name_len; - RETURN_MAYBE_NOT_FOUND(read_name(out, cand_name, cand_name_len)) - - if (cand_name_len != full_name_len && cand_name_len != full_name_len + 1) - goto next_iter; - for (unsigned i = 0; i < full_name_len; ++i) - if (cand_name[i] != full_name[i]) - goto next_iter; - if (cand_name_len == full_name_len + 1 && - cand_name[full_name_len] != '/') - goto next_iter; - - return storage::io_result::success; - - next_iter: - RETURN_MAYBE_NOT_FOUND(next_node(out)) - - } - - } - - storage::io_result tarfs_instance::get_name_length( - storage::node_id_t node, unsigned &length_out - ) { - - char name_buf[255]; - return get_name(node, name_buf, length_out); - - } - - storage::io_result tarfs_instance::get_name( - storage::node_id_t node, char *buffer, unsigned &length_out - ) { - - unsigned full_length; - char full_buffer[255]; - RETURN_NOT_SUCCESS(read_name(node, full_buffer, full_length)) - if (full_length == 2) { - //full buffer must be ./ - length_out = 0; - return storage::io_result::success; - } - if (full_buffer[full_length - 1] == '/') - --full_length; - unsigned start = utility::find_last(full_buffer, full_length, '/') + 1; - length_out = full_length - start; - for (unsigned i = 0; i < length_out; ++i) - buffer[i] = full_buffer[start + i]; - return storage::io_result::success; - - } - - storage::io_result tarfs_instance::get_file_length( - storage::node_id_t node, uint64_t &length_out - ) { - - return read_num(node + 124, 12, length_out); - - } - - storage::io_result tarfs_instance::get_file_type( - storage::node_id_t node, storage::file_type &out - ) { - - uint64_t ft; - storage::io_result result = read_num(node + 156, 1, ft); - if (result != storage::io_result::success) - return result; - - switch (ft) { - case 0: - out = storage::file_type::regular_file; - return storage::io_result::success; - case 2: - out = storage::file_type::symlink; - return storage::io_result::success; - case 5: - out = storage::file_type::directory; - return storage::io_result::success; - default: - return storage::io_result::not_supported; - } - - } - - storage::io_result tarfs_instance::resize_file( - storage::node_id_t, uint64_t - ) { - //TODO: support resize if sector count isn't changed? - return storage::io_result::not_supported; - } - - storage::io_result tarfs_instance::read_bytes_from_file( - storage::node_id_t node, uint64_t start, uint64_t count, void *into - ) { - return bd->read_bytes(node + 512 + start, count, into); - } - - storage::io_result tarfs_instance::write_bytes_into_file( - storage::node_id_t, uint64_t, uint64_t, const void * - ) { - //TODO: support this. - return storage::io_result::not_supported; - } - -} diff --git a/kernel/storage.cpp b/kernel/storage.cpp index 97d89aa..f500f8e 100644 --- a/kernel/storage.cpp +++ b/kernel/storage.cpp @@ -1,49 +1,38 @@ #include -#define RETURN_IF_NOT_SUCCESS(expr) \ - { \ - io_result _result = expr; \ - if (_result != io_result::success) \ - return _result; \ - } - namespace mercury::kernel::storage { - io_result block_device::load_cache_block(uint64_t i) { + bd_result block_device::load_cache_block(uint64_t i) { if (block_cache_i == i) - return io_result::success; + return bd_result::success; - if (block_cache_dirty) { - RETURN_IF_NOT_SUCCESS( - write_blocks_no_cache(block_cache_i, 1, block_cache)) - block_cache_dirty = false; - } + bd_result result = read_blocks_no_cache(i, 1, block_cache); - io_result result = read_blocks_no_cache(i, 1, block_cache); - - if (result != io_result::success) { + if (result != bd_result::success) { block_cache_i = block_count; return result; } block_cache_i = i; - return io_result::success; + return bd_result::success; } - io_result block_device::read_bytes( + bd_result block_device::read_bytes( uint64_t start, uint64_t count, void *into ) { if (start + count > block_size * block_count) - return io_result::out_of_bounds; + return bd_result::out_of_bounds; uint8_t *into_u8 = (uint8_t *)into; if (start % block_size != 0) { uint64_t prefix_len = block_size - start % block_size; - RETURN_IF_NOT_SUCCESS(load_cache_block(start / block_size)) + bd_result result = load_cache_block(start / block_size); + if (result != bd_result::success) + return result; for (uint64_t i = 0; i < prefix_len; ++i) into_u8[i] = block_cache[start % block_size + i]; into_u8 += prefix_len; @@ -54,202 +43,24 @@ namespace mercury::kernel::storage { uint64_t postfix_start = ((start + count) / block_size) * block_size; if (postfix_start != start) { - RETURN_IF_NOT_SUCCESS(read_blocks_no_cache( - start / block_size, (postfix_start - start) / block_size, into_u8)) + bd_result result = read_blocks_no_cache( + start / block_size, (postfix_start - start) / block_size, into_u8); + if (result != bd_result::success) + return result; count -= postfix_start - start; into_u8 += postfix_start - start; start = postfix_start; } if (count != 0) { - RETURN_IF_NOT_SUCCESS(load_cache_block(start / block_size)) + bd_result result = load_cache_block(start / block_size); + if (result != bd_result::success) + return result; for (uint64_t i = 0; i < count; ++i) into_u8[i] = block_cache[i]; } - return io_result::success; - - } - - utility::list *block_devices; - - static utility::trie *mounted_devices; - - void init_storage() { - block_devices = new utility::list(); - mounted_devices = new utility::trie(); - } - - void canon_path::parent() { - if (segments.count != 0) - --segments.count; - else if (!absolute) - ++parent_count; - } - - void canon_path::rel(const canon_path &r) { - if (r.absolute) { - segments.count = 0; - absolute = true; - parent_count = 0; - } - for (unsigned i = 0; i < r.parent_count; ++i) - parent(); - for (unsigned i = 0; i < r.segments.count; ++i) - segments.add_end(r.segments.buffer[i]); - } - - void canonize_path(const char *str, unsigned len, canon_path &out) { - - out.absolute = false; - out.parent_count = 0; - out.segments.count = 0; - - if (len == 0) - return; - - if (len == 1 && str[0] == '/') { - out.absolute = true; - return; - } - - if (str[0] == '/') { - out.absolute = true; - ++str; - --len; - } - - while (len != 0) { - - unsigned segment_len = utility::find(str, len, '/'); - unsigned to_skip = segment_len == len ? segment_len : segment_len + 1; - - if (segment_len == 0) - ; - - else if (segment_len == 1 && str[0] == '.') - ; - - else if (segment_len == 2 && str[0] == '.' && str[1] == '.') - out.parent(); - - else { - utility::string segment(str, segment_len); - out.segments.add_end(std::move(segment)); - } - - str += to_skip; - len -= to_skip; - - } - - } - - io_result mount_device( - block_device *bd, const canon_path &path, file_system_mounter mounter - ) { - - if (!path.absolute) - return io_result::bad_path; - - if (mounted_devices->has_key(path.segments)) - return io_result::mount_point_already_used; - - file_system_instance *fs; - RETURN_IF_NOT_SUCCESS(mounter(bd, fs)); - bd->mounted_as = fs; - mounted_devices->try_insert(path.segments, bd); - return io_result::success; - - } - - static io_result symlink_contents( - block_device *bd, node_id_t node, canon_path &out - ) { - //TODO - while (1) - ; - (void)bd; - (void)node; - (void)out; - } - - static io_result resolve_symlinks( - block_device *&bd, node_id_t &node, canon_path &path - ) { - - file_type ft; - RETURN_IF_NOT_SUCCESS(bd->mounted_as->get_file_type(node, ft)) - - if (ft != file_type::symlink) - return io_result::success; - - canon_path contents; - RETURN_IF_NOT_SUCCESS(symlink_contents(bd, node, contents)) - path.parent(); - path.rel(contents); - - return look_up_absolute_path(path, bd, node, true, path); - - } - - io_result look_up_absolute_path(const canon_path &path, - block_device *&bd_out, node_id_t &node_out, bool resolve_final_node, - canon_path &path_without_symlinks_out - ) { - - if (!path.absolute) - return io_result::bad_path; - - unsigned prefix_length; - bd_out = - *mounted_devices->longest_prefix_with_value(path.segments, prefix_length) - .value_here; - RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_root_node(node_out)) - - path_without_symlinks_out.absolute = true; - path_without_symlinks_out.parent_count = 0; - path_without_symlinks_out.segments.count = 0; - - for (unsigned i = 0; i < prefix_length; ++i) - path_without_symlinks_out.segments.add_end(path.segments.buffer[i]); - - if (path.segments.count == 0) - goto on_final_node; - - for (unsigned i = prefix_length; i < path.segments.count - 1; ++i) { - - path_without_symlinks_out.segments.add_end(path.segments.buffer[i]); - - RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_child(node_out, node_out, - path.segments.buffer[i].buffer, path.segments.buffer[i].count)) - RETURN_IF_NOT_SUCCESS(resolve_symlinks( - bd_out, node_out, path_without_symlinks_out)) - - file_type ft; - RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_file_type(node_out, ft)) - - if (ft != file_type::directory) - return io_result::not_a_directory; - - } - - { - //in a block so that compiler sees last_segment - //isn't needed after this and allows the goto above. - const utility::string &last_segment = - path.segments.buffer[path.segments.count - 1]; - path_without_symlinks_out.segments.add_end(last_segment); - RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_child( - node_out, node_out, last_segment.buffer, last_segment.count)) - } - - on_final_node: - if (resolve_final_node) - RETURN_IF_NOT_SUCCESS(resolve_symlinks( - bd_out, node_out, path_without_symlinks_out)) - - return io_result::success; + return bd_result::success; } diff --git a/kernel/storage/bd/memory.cpp b/kernel/storage/bd/memory.cpp new file mode 100644 index 0000000..2f82099 --- /dev/null +++ b/kernel/storage/bd/memory.cpp @@ -0,0 +1,21 @@ +#include + +namespace mercury::kernel::storage::bd { + + memory::memory(void *buffer, uint64_t buffer_len) + : buffer((uint8_t *)buffer) + { + block_size = 1; + block_count = buffer_len; + //block cache will never be used, since the block size is 1. + } + + bd_result memory::read_blocks_no_cache( + uint64_t start, uint64_t count, void *into + ) { + for (uint64_t i = 0; i < count; ++i) + ((uint8_t *)into)[i] = buffer[start + i]; + return bd_result::success; + } + +} 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 + +//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 &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 &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 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 &out, directory_iter_t &iter_out + ) { + + std::optional 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 &out, directory_iter_t &iter + ) { + + std::optional start; + FS_TO_FS(next_node((node_id_t)iter, start)) + if (!start) { + out = {}; + return fs_result::success; + } + + std::optional 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; + } + + +} diff --git a/kernel/terminal.cpp b/kernel/terminal.cpp index 7a878ee..59e1ee5 100644 --- a/kernel/terminal.cpp +++ b/kernel/terminal.cpp @@ -74,9 +74,9 @@ namespace mercury::kernel::terminal { } } - void put_string(const char *str, size_t len) { - for (size_t i = 0; i < len; ++i) - put_char(str[i]); + void put_string(const utility::string &str) { + for (size_t i = 0; i < str.count; ++i) + put_char(str.buffer[i]); } void put_string_sz(const char *str) { diff --git a/kernel/vfile.cpp b/kernel/vfile.cpp new file mode 100644 index 0000000..9f549e5 --- /dev/null +++ b/kernel/vfile.cpp @@ -0,0 +1,192 @@ +#include + +//TODO: handle symlink loops nicely in vfile::get_child, +// vfile::get_children, and lookup_path. + +namespace mercury::kernel::vfile { + + void canon_path::parent() { + if (segments.count != 0) + --segments.count; + else if (!absolute) + ++parent_count; + } + + void canon_path::rel(const canon_path &r) { + if (r.absolute) { + segments.count = 0; + absolute = true; + parent_count = 0; + } + for (unsigned i = 0; i < r.parent_count; ++i) + parent(); + for (unsigned i = 0; i < r.segments.count; ++i) + segments.add_end(r.segments.buffer[i]); + } + + void canonize_path(const utility::string &name, canon_path &out) { + + out.absolute = false; + out.parent_count = 0; + out.segments.count = 0; + + const char *str = name.buffer; + unsigned len = name.count; + + if (len == 0) + return; + + if (len == 1 && str[0] == '/') { + out.absolute = true; + return; + } + + if (str[0] == '/') { + out.absolute = true; + ++str; + --len; + } + + while (len != 0) { + + unsigned segment_len = utility::find(str, len, '/'); + unsigned to_skip = segment_len == len ? segment_len : segment_len + 1; + + if (segment_len == 0) + ; + + else if (segment_len == 1 && str[0] == '.') + ; + + else if (segment_len == 2 && str[0] == '.' && str[1] == '.') + out.parent(); + + else { + utility::string segment(str, segment_len); + out.segments.add_end(std::move(segment)); + } + + str += to_skip; + len -= to_skip; + + } + + } + +#define RET_NOT_SUC(expr) \ + { \ + storage::fs_result _result = expr; \ + if (_result != storage::fs_result::success) \ + return _result; \ + } + + storage::fs_result vfile::follow_symlinks( + const vfile &root, std::optional &out + ) const { + + if (dir_entry.type != storage::file_type::symlink) { + out = *this; + return storage::fs_result::success; + } + + canon_path target_path; + canonize_path(dir_entry.target, target_path); + canon_path full_path = path; + full_path.parent(); + full_path.rel(target_path); + + std::optional next; + RET_NOT_SUC(lookup_path(root, full_path, next)) + if (!next) { + out = {}; + return storage::fs_result::success; + } + + next->path = path; + return next->follow_symlinks(root, out); + + } + + storage::fs_result vfile::get_child( + std::optional &out, const utility::string &name + ) const { + + std::optional entry; + storage::directory_iter_t iter; + + RET_NOT_SUC(bd->mounted_as->get_first_child(dir_entry.node, entry, iter)) + + while (entry) { + + if (entry->name == name) { + + vfile vf; + vf.bd = bd; + vf.dir_entry = std::move(*entry); + vf.path = path; + vf.path.segments.add_end(name); + out = std::move(vf); + return storage::fs_result::success; + + } + + RET_NOT_SUC(bd->mounted_as->get_next_child(dir_entry.node, entry, iter)) + + } + + out = {}; + return storage::fs_result::success; + + } + + storage::fs_result vfile::get_children(utility::vector &out) const { + + std::optional entry; + storage::directory_iter_t iter; + + RET_NOT_SUC(bd->mounted_as->get_first_child(dir_entry.node, entry, iter)) + + while (entry) { + + vfile vf; + vf.bd = bd; + vf.path = path; + vf.path.segments.add_end(entry->name); + vf.dir_entry = std::move(*entry); + out.add_end(std::move(vf)); + + RET_NOT_SUC(bd->mounted_as->get_next_child(dir_entry.node, entry, iter)) + + } + + return storage::fs_result::success; + + } + + storage::fs_result lookup_path( + const vfile &root, const canon_path &path, std::optional &out + ) { + + //assume path is absolute. + + out = root; + for (unsigned i = 0; i < path.segments.count; ++i) { + + std::optional result; + RET_NOT_SUC(out->follow_symlinks(root, result)) + out = std::move(result); + if (!out) + return storage::fs_result::success; + + RET_NOT_SUC(out->get_child(result, path.segments.buffer[i])) + out = std::move(result); + if (!out) + return storage::fs_result::success; + + } + + return storage::fs_result::success; + + } + +} -- cgit v1.2.3