diff options
author | Benji Dial <benji@benjidial.net> | 2024-01-12 20:39:21 -0500 |
---|---|---|
committer | Benji Dial <benji@benjidial.net> | 2024-01-12 20:39:21 -0500 |
commit | 882e74b2191c059a9226cbd8bcb51c97da36247c (patch) | |
tree | 3ecc05882a9097a85749902130849be65911e684 | |
parent | c4ab2f6f440f060b1686991b24379a4998aa55a9 (diff) | |
download | hilbert-os-882e74b2191c059a9226cbd8bcb51c97da36247c.tar.gz |
rewrite file system layer
-rw-r--r-- | documentation/storage.txt | 6 | ||||
-rw-r--r-- | include/mercury/kernel/bd/memory.hpp | 23 | ||||
-rw-r--r-- | include/mercury/kernel/fs/tarfs.hpp | 45 | ||||
-rw-r--r-- | include/mercury/kernel/storage.hpp | 107 | ||||
-rw-r--r-- | include/mercury/kernel/storage/bd/memory.hpp | 22 | ||||
-rw-r--r-- | include/mercury/kernel/storage/fs/tarfs.hpp | 32 | ||||
-rw-r--r-- | include/mercury/kernel/terminal.hpp | 4 | ||||
-rw-r--r-- | include/mercury/kernel/utility.hpp | 108 | ||||
-rw-r--r-- | include/mercury/kernel/vfile.hpp | 62 | ||||
-rw-r--r-- | kernel/bd/memory.cpp | 27 | ||||
-rw-r--r-- | kernel/entry.cpp | 151 | ||||
-rw-r--r-- | kernel/fs/tarfs.cpp | 296 | ||||
-rw-r--r-- | kernel/storage.cpp | 225 | ||||
-rw-r--r-- | kernel/storage/bd/memory.cpp | 21 | ||||
-rw-r--r-- | kernel/storage/fs/tarfs.cpp | 254 | ||||
-rw-r--r-- | kernel/terminal.cpp | 6 | ||||
-rw-r--r-- | kernel/vfile.cpp | 192 | ||||
-rw-r--r-- | makefile | 5 |
18 files changed, 750 insertions, 836 deletions
diff --git a/documentation/storage.txt b/documentation/storage.txt index 2d92dc7..4fe8ea3 100644 --- a/documentation/storage.txt +++ b/documentation/storage.txt @@ -2,6 +2,6 @@ a block_device is a block device such as a disk or a partition that has been seen since the last boot. when a driver detects a device, it should check if a block_device with that id has already been created, and if so, reuse it. -eventually, i would like to implement kernel-space exceptions and use those -instead of having functions return an io_result, since it's a bit unwieldy -to propogate those results as is. +eventually, i would like to implement kernel-space exceptions and +use those instead of having functions return bd_result or fs_result, +since it's a bit unwieldy to propogate those results as is. diff --git a/include/mercury/kernel/bd/memory.hpp b/include/mercury/kernel/bd/memory.hpp deleted file mode 100644 index 079abee..0000000 --- a/include/mercury/kernel/bd/memory.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef MERCURY_KERNEL_BD_MEMORY_HPP -#define MERCURY_KERNEL_BD_MEMORY_HPP - -#include <mercury/kernel/storage.hpp> - -namespace mercury::kernel::bd { - - class memory : public storage::block_device { - - private: - uint8_t *buffer; - - public: - memory(void *buffer, size_t buffer_len); - - storage::io_result read_blocks_no_cache(uint64_t start, uint64_t count, void *into) override; - storage::io_result write_blocks_no_cache(uint64_t start, uint64_t count, const void *from) override; - - }; - -} - -#endif diff --git a/include/mercury/kernel/fs/tarfs.hpp b/include/mercury/kernel/fs/tarfs.hpp deleted file mode 100644 index 64ee481..0000000 --- a/include/mercury/kernel/fs/tarfs.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef MERCURY_KERNEL_FS_TARFS_HPP -#define MERCURY_KERNEL_FS_TARFS_HPP - -#include <mercury/kernel/storage.hpp> - -namespace mercury::kernel::fs { - - class tarfs_instance : public storage::file_system_instance { - - private: - storage::block_device *bd; - - storage::io_result next_node(storage::node_id_t &node); - //name_buf must be at least 255 chars long. - storage::io_result read_name(storage::node_id_t node, char *name_buf, unsigned &name_len_out); - //len <= 12 - storage::io_result read_num(uint64_t offset, unsigned len, uint64_t &out); - - public: - tarfs_instance(storage::block_device *bd); - - storage::io_result get_root_node(storage::node_id_t &out) override; - storage::io_result get_first_child( - storage::node_id_t node, storage::node_id_t &out, storage::directory_iter_t &iter_out) override; - storage::io_result get_next_child( - storage::node_id_t node, storage::node_id_t &out, storage::directory_iter_t &iter) override; - storage::io_result get_child( - storage::node_id_t node, storage::node_id_t &out, const char *name, unsigned name_len) override; - storage::io_result get_name_length(storage::node_id_t node, unsigned &length_out) override; - storage::io_result get_name(storage::node_id_t node, char *buffer, unsigned &length_out) override; - storage::io_result get_file_length(storage::node_id_t node, uint64_t &length_out) override; - storage::io_result get_file_type(storage::node_id_t node, storage::file_type &out) override; - storage::io_result resize_file(storage::node_id_t node, uint64_t new_length) override; - storage::io_result read_bytes_from_file( - storage::node_id_t node, uint64_t start, uint64_t count, void *into) override; - storage::io_result write_bytes_into_file( - storage::node_id_t node, uint64_t start, uint64_t count, const void *from) override; - - }; - - storage::io_result tarfs_mounter(storage::block_device *bd, storage::file_system_instance *&fs_out); - -} - -#endif diff --git a/include/mercury/kernel/storage.hpp b/include/mercury/kernel/storage.hpp index 0df0829..61e7d3b 100644 --- a/include/mercury/kernel/storage.hpp +++ b/include/mercury/kernel/storage.hpp @@ -9,22 +9,32 @@ namespace mercury::kernel::storage { typedef uint64_t node_id_t; typedef uint64_t directory_iter_t; - enum file_type { + enum class file_type { regular_file, directory, symlink }; - enum io_result { + enum class bd_result { success, - mount_point_already_used, - not_a_directory, - not_supported, out_of_bounds, + device_error + }; + + enum class fs_result { + success, device_error, - fs_corrupt, - not_found, - bad_path, + fs_corrupt + }; + + struct dir_entry { + utility::string name; + node_id_t node; + file_type type; + //only used if type is regular_file. + uint64_t length; + //only used if type is symlink. + utility::string target; }; class file_system_instance { @@ -32,27 +42,21 @@ namespace mercury::kernel::storage { public: virtual ~file_system_instance() {} - virtual io_result get_root_node(node_id_t &out) = 0; + virtual fs_result get_root_node(node_id_t &out) = 0; - //get_first_child sets iter_out to a "directory iterator" representing the - //first child of this node. get_next_child gets the child after the child - //represented by the value of iter passed in, and changes iter to represent - //that next child. for an empty directory node, get_first_child returns - //io_result::not_found. when iter represents the last child of a node, - //get_next_child also returns io_result::not_found. - virtual io_result get_first_child(node_id_t node, node_id_t &out, directory_iter_t &iter_out) = 0; - virtual io_result get_next_child(node_id_t node, node_id_t &out, directory_iter_t &iter) = 0; - virtual io_result get_child(node_id_t node, node_id_t &out, const char *name, unsigned name_len) = 0; + //it is assumed that this is a directory. sets iter_out + //to a value that can be passed to subsequent calls + //to get_next_child with the same argument for node. + virtual fs_result get_first_child(node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter_out) = 0; - virtual io_result get_name_length(node_id_t node, unsigned &length_out) = 0; - //buffer is assumed to be long enough - call get_name_length first. - virtual io_result get_name(node_id_t node, char *buffer, unsigned &length_out) = 0; - virtual io_result get_file_length(node_id_t node, uint64_t &length_out) = 0; - virtual io_result get_file_type(node_id_t node, file_type &out) = 0; + //it is assumed that this is a directory. sets iter_out + //to a value that can be passed to subsequent calls + //to get_next_child with the same argument for node. + virtual fs_result get_next_child(node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter) = 0; - virtual io_result resize_file(node_id_t node, uint64_t new_length) = 0; - virtual io_result read_bytes_from_file(node_id_t node, uint64_t start, uint64_t count, void *into) = 0; - virtual io_result write_bytes_into_file(node_id_t node, uint64_t start, uint64_t count, const void *from) = 0; + //it is assumed that this is a regular file and that + //the region requested is within the bounds of the file. + virtual fs_result read_bytes_from_file(node_id_t node, uint64_t start, uint64_t count, void *into) = 0; }; @@ -61,27 +65,29 @@ namespace mercury::kernel::storage { private: uint8_t *block_cache; uint64_t block_cache_i; - bool block_cache_dirty; - io_result load_cache_block(uint64_t i); + //it is assumed that this block is within the bounds of the device. + bd_result load_cache_block(uint64_t i); protected: - //implemented in driver, bounds not checked - virtual io_result read_blocks_no_cache(uint64_t start, uint64_t count, void *into) = 0; - virtual io_result write_blocks_no_cache(uint64_t start, uint64_t count, const void *from) = 0; + //implemented in driver. it is assumed that the + //blocks are within the bounds of the device. + virtual bd_result read_blocks_no_cache(uint64_t start, uint64_t count, void *into) = 0; //it is assumed that this is only called once, by the driver, after //block_size and block_count have been set, and before read_bytes or - //write_bytes can be called (e.g. in the derived class's constructor) + //write_bytes can be called (e.g. in the derived class's constructor). void allocate_block_cache(); public: //set by storage component file_system_instance *mounted_as; + //set by storage component utility::uuid id; //set by driver uint64_t block_size; + //set by driver uint64_t block_count; virtual ~block_device() { @@ -89,45 +95,10 @@ namespace mercury::kernel::storage { delete block_cache; } - io_result read_bytes(uint64_t start, uint64_t count, void *into); - io_result write_bytes(uint64_t start, uint64_t count, const void *from); + bd_result read_bytes(uint64_t start, uint64_t count, void *into); }; - typedef io_result (*file_system_mounter)(block_device *bd, file_system_instance *&fs_out); - - extern utility::list<block_device *> *block_devices; - - void init_storage(); - - //a canon path contains no . or empty directory names, and - //contains no .. except for at the start of a relative path. - struct canon_path { - - bool absolute; - unsigned parent_count; - utility::vector<utility::string> segments; - - void parent(); - void rel(const canon_path &r); - - utility::string to_string(bool trailing_slash) const; - - }; - - void canonize_path(const char *str, unsigned len, canon_path &out); - - //path must be absolute. - io_result mount_device(block_device *bd, const canon_path &path, file_system_mounter mounter); - - //path must be absolute. symlinks are always resolved on nodes other than the - //final one. they are resolved on the final node if and only if resolve final - //node is set to true. mount points are always traversed, including on the - //final node. this assumes that the path is under some mount point, and the - //same for any symlinks traversed (e.g. / is mounted). - 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); - } #endif diff --git a/include/mercury/kernel/storage/bd/memory.hpp b/include/mercury/kernel/storage/bd/memory.hpp new file mode 100644 index 0000000..e12d565 --- /dev/null +++ b/include/mercury/kernel/storage/bd/memory.hpp @@ -0,0 +1,22 @@ +#ifndef MERCURY_KERNEL_STORAGE_BD_MEMORY_HPP +#define MERCURY_KERNEL_STORAGE_BD_MEMORY_HPP + +#include <mercury/kernel/storage.hpp> + +namespace mercury::kernel::storage::bd { + + class memory : public block_device { + + private: + uint8_t *buffer; + + public: + memory(void *buffer, uint64_t buffer_len); + + bd_result read_blocks_no_cache(uint64_t start, uint64_t count, void *into) override; + + }; + +} + +#endif diff --git a/include/mercury/kernel/storage/fs/tarfs.hpp b/include/mercury/kernel/storage/fs/tarfs.hpp new file mode 100644 index 0000000..9e16207 --- /dev/null +++ b/include/mercury/kernel/storage/fs/tarfs.hpp @@ -0,0 +1,32 @@ +#ifndef MERCURY_KERNEL_STORAGE_FS_TARFS_HPP +#define MERCURY_KERNEL_STORAGE_FS_TARFS_HPP + +#include <mercury/kernel/storage.hpp> + +namespace mercury::kernel::storage::fs { + + class tarfs_instance : public file_system_instance { + + private: + block_device *bd; + + fs_result next_node(node_id_t node, std::optional<node_id_t> &out); + fs_result read_full_name(node_id_t node, utility::string &out); + //len <= 12. + fs_result read_num(uint64_t offset, unsigned len, uint64_t &out); + fs_result first_child_starting_at(node_id_t parent, node_id_t start, std::optional<node_id_t> &out); + fs_result get_dir_entry(node_id_t node, dir_entry &entry); + + public: + tarfs_instance(block_device *bd); + + fs_result get_root_node(node_id_t &out); + fs_result get_first_child(node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter_out); + fs_result get_next_child(node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter); + fs_result read_bytes_from_file(node_id_t node, uint64_t start, uint64_t count, void *into); + + }; + +} + +#endif diff --git a/include/mercury/kernel/terminal.hpp b/include/mercury/kernel/terminal.hpp index 776ff64..8efbcac 100644 --- a/include/mercury/kernel/terminal.hpp +++ b/include/mercury/kernel/terminal.hpp @@ -1,6 +1,8 @@ #ifndef MERCURY_KERNEL_TERMINAL_HPP #define MERCURY_KERNEL_TERMINAL_HPP +#include <mercury/kernel/framebuffer.hpp> +#include <mercury/kernel/utility.hpp> #include <cstddef> #include <cstdint> @@ -21,7 +23,7 @@ namespace mercury::kernel::terminal { extern framebuffer::color fg_color; void put_char(char ch); - void put_string(const char *str, size_t len); + void put_string(const utility::string &str); void put_string_sz(const char *str); void put_int_decimal(uint64_t n, bool with_commas = true); diff --git a/include/mercury/kernel/utility.hpp b/include/mercury/kernel/utility.hpp index 1648e67..3edd7d4 100644 --- a/include/mercury/kernel/utility.hpp +++ b/include/mercury/kernel/utility.hpp @@ -106,6 +106,18 @@ namespace mercury::kernel::utility { last = new_last; } + void remove(node *n) { + if (n->prev) + n->prev->next = n->next; + else + first = n->next; + if (n->next) + n->next->prev = n->prev; + else + last = n->prev; + delete n; + } + }; template <class value_t> @@ -124,6 +136,14 @@ namespace mercury::kernel::utility { buffer[i] = copy_from[i]; } + vector(const vector<value_t> &other) + : buffer(new value_t[other.buffer_len]), + buffer_len(other.buffer_len), count(other.count) + { + for (unsigned i = 0; i < count; ++i) + buffer[i] = other.buffer[i]; + } + ~vector() { if (buffer) delete[] buffer; @@ -134,6 +154,7 @@ namespace mercury::kernel::utility { delete[] buffer; buffer = new value_t[other.buffer_len]; buffer_len = other.buffer_len; + count = other.count; for (unsigned i = 0; i < other.count; ++i) buffer[i] = other.buffer[i]; return *this; @@ -185,92 +206,19 @@ namespace mercury::kernel::utility { ++count; } - }; - - typedef vector<char> string; - - template <class key_component_t, class value_t> - struct trie { - - typedef vector<key_component_t> key_t; - - struct child { - key_component_t component; - trie tr; - }; - - //really this should be a hashmap or something - vector<child> children; - - trie *try_get_child(const key_component_t &component) const { - for (unsigned i = 0; i < children.count; ++i) - if (children.buffer[i].component == component) - return &children.buffer[i].tr; - return 0; - } - - std::optional<value_t> value_here; - - trie() : children(0) {} - - //prefix length is in key components, with the root node having a prefix - //length of 0. if no prefix of key has a value, then this just returns the - //root node and sets prefix length to 0. - const trie &longest_prefix_with_value( - const key_t &key, unsigned &prefix_length_out - ) const { - - const trie *on = this; - unsigned with_i = 0; - - const trie *longest_found = this; - prefix_length_out = 0; - - while (true) { - if (with_i == key.count) - return *longest_found; - on = on->try_get_child(key.buffer[with_i]); - if (!on) - return *longest_found; - ++with_i; - if (on->value_here) { - longest_found = on; - prefix_length_out = with_i; - } - } - - } - - bool has_key(const key_t &key) { - const trie *on = this; - for (unsigned i = 0; i < key.count; ++i) { - on = on->try_get_child(key.buffer[i]); - if (!on) - return false; - } - return on->value_here.has_value(); - } - - //returns false if this key is already in use. - bool try_insert(const key_t &key, value_t value) { - trie *on = this; - for (unsigned i = 0; i < key.count; ++i) { - on = on->try_get_child(key.buffer[i]); - if (!on) { - child ch; - ch.component = key.buffer[i]; - on->children.add_end(std::move(ch)); - on = &on->children.buffer[on->children.count - 1].tr; - } - } - if (on->value_here) + bool starts_with(const vector<value_t> &other) { + if (count < other.count) return false; - *on->value_here = value; + for (unsigned i = 0; i < other.count; ++i) + if (buffer[i] != other.buffer[i]) + return false; return true; } }; + typedef vector<char> string; + } #endif diff --git a/include/mercury/kernel/vfile.hpp b/include/mercury/kernel/vfile.hpp new file mode 100644 index 0000000..43dff96 --- /dev/null +++ b/include/mercury/kernel/vfile.hpp @@ -0,0 +1,62 @@ +#ifndef MERCURY_KERNEL_VFILE_HPP +#define MERCURY_KERNEL_VFILE_HPP + +#include <mercury/kernel/storage.hpp> +#include <mercury/kernel/utility.hpp> + +//TODO: mounts points. +//maybe a two-way map between mount points and targets? one mount point per +//target and vice versa. only directories may be mount points, and only file +//system roots (which must be directories) may be targets. + +namespace mercury::kernel::vfile { + + //a canon path contains no . or empty directory names, and + //contains no .. except for at the start of a relative path. + struct canon_path { + + bool absolute; + unsigned parent_count; + utility::vector<utility::string> segments; + + canon_path() : absolute(false), parent_count(0), segments(8) {} + + void parent(); + void rel(const canon_path &r); + + utility::string to_string(bool trailing_slash) const; + + }; + + void canonize_path(const utility::string &name, canon_path &out); + + struct vfile { + + //on followed symlinks, path is the source of the symlink and the + //rest of these are the target. in any case, path should be absolute. + canon_path path; + storage::block_device *bd; + storage::dir_entry dir_entry; + + //TODO: instead of passing root, use saved mount points. + storage::fs_result follow_symlinks( + const vfile &root, std::optional<vfile> &out) const; + + //dir_entry.type is assumed to be directory. + storage::fs_result get_child( + std::optional<vfile> &out, const utility::string &name) const; + //dir_entry.type is assumed to be directory. out must be empty on entry. + storage::fs_result get_children(utility::vector<vfile> &out) const; + + }; + + //path must be absolute. follows symlinks on all but the last node. + //relative_to should be a directory to do the lookup inside; e.g., + //if relative_to is /a/b and path is c, then out becomes /a/b/c. + //TODO: instead of passing root, use saved mount points. + storage::fs_result lookup_path( + const vfile &root, const canon_path &path, std::optional<vfile> &out); + +} + +#endif 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 <mercury/kernel/bd/memory.hpp> - -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 <mercury/kernel/storage/bd/memory.hpp> +#include <mercury/kernel/storage/fs/tarfs.hpp> #include <mercury/kernel/framebuffer.hpp> -#include <mercury/kernel/bd/memory.hpp> -#include <mercury/kernel/fs/tarfs.hpp> #include <mercury/kernel/terminal.hpp> #include <mercury/kernel/limine.hpp> #include <mercury/kernel/paging.hpp> +#include <mercury/kernel/vfile.hpp> 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<vfile::vfile> followed; + utility::vector<vfile::vfile> 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 <mercury/kernel/fs/tarfs.hpp> - -//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 <mercury/kernel/storage.hpp> -#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_device *> *block_devices; - - static utility::trie<utility::string, block_device *> *mounted_devices; - - void init_storage() { - block_devices = new utility::list<block_device *>(); - mounted_devices = new utility::trie<utility::string, block_device *>(); - } - - 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 <mercury/kernel/storage/bd/memory.hpp> + +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 <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; + } + + +} 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 <mercury/kernel/vfile.hpp> + +//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<vfile> &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<vfile> 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<vfile> &out, const utility::string &name + ) const { + + std::optional<storage::dir_entry> 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<vfile> &out) const { + + std::optional<storage::dir_entry> 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<vfile> &out + ) { + + //assume path is absolute. + + out = root; + for (unsigned i = 0; i < path.segments.count; ++i) { + + std::optional<vfile> 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; + + } + +} @@ -1,6 +1,6 @@ GPP_ARGS = -Wall -Wextra -O3 -ggdb -I include -ffreestanding -fno-exceptions -fno-rtti -mno-sse -KERNEL_OBJECTS = allocator.cpp entry.cpp framebuffer.cpp paging.asm \ - paging.cpp storage.cpp terminal.cpp utility.cpp bd/memory.cpp fs/tarfs.cpp +KERNEL_OBJECTS = allocator.cpp entry.cpp framebuffer.cpp paging.asm paging.cpp storage.cpp \ + storage/bd/memory.cpp storage/fs/tarfs.cpp terminal.cpp utility.cpp vfile.cpp all: out/disk.iso @@ -39,6 +39,7 @@ obj/initfs.tgz: @mkdir -p obj/initfs echo test > obj/initfs/test.txt mkdir -p obj/initfs/dir/dir2 + ln -s ../test.txt obj/initfs/dir/test2.txt tar czf obj/initfs.tgz -C obj/initfs . out/disk.iso: obj/kernel.elf obj/initfs.tgz limine |