summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenji Dial <benji@benjidial.net>2024-01-12 20:39:21 -0500
committerBenji Dial <benji@benjidial.net>2024-01-12 20:39:21 -0500
commit882e74b2191c059a9226cbd8bcb51c97da36247c (patch)
tree3ecc05882a9097a85749902130849be65911e684
parentc4ab2f6f440f060b1686991b24379a4998aa55a9 (diff)
downloadhilbert-os-882e74b2191c059a9226cbd8bcb51c97da36247c.tar.gz
rewrite file system layer
-rw-r--r--documentation/storage.txt6
-rw-r--r--include/mercury/kernel/bd/memory.hpp23
-rw-r--r--include/mercury/kernel/fs/tarfs.hpp45
-rw-r--r--include/mercury/kernel/storage.hpp107
-rw-r--r--include/mercury/kernel/storage/bd/memory.hpp22
-rw-r--r--include/mercury/kernel/storage/fs/tarfs.hpp32
-rw-r--r--include/mercury/kernel/terminal.hpp4
-rw-r--r--include/mercury/kernel/utility.hpp108
-rw-r--r--include/mercury/kernel/vfile.hpp62
-rw-r--r--kernel/bd/memory.cpp27
-rw-r--r--kernel/entry.cpp151
-rw-r--r--kernel/fs/tarfs.cpp296
-rw-r--r--kernel/storage.cpp225
-rw-r--r--kernel/storage/bd/memory.cpp21
-rw-r--r--kernel/storage/fs/tarfs.cpp254
-rw-r--r--kernel/terminal.cpp6
-rw-r--r--kernel/vfile.cpp192
-rw-r--r--makefile5
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;
+
+ }
+
+}
diff --git a/makefile b/makefile
index 464d423..82734bc 100644
--- a/makefile
+++ b/makefile
@@ -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