summaryrefslogtreecommitdiff
path: root/kernel/storage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/storage.cpp')
-rw-r--r--kernel/storage.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/kernel/storage.cpp b/kernel/storage.cpp
new file mode 100644
index 0000000..ff86896
--- /dev/null
+++ b/kernel/storage.cpp
@@ -0,0 +1,246 @@
+#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) {
+
+ if (block_cache_i == i)
+ return io_result::success;
+
+ if (block_cache_dirty) {
+ RETURN_IF_NOT_SUCCESS(
+ write_blocks_no_cache(block_cache_i, 1, block_cache))
+ block_cache_dirty = false;
+ }
+
+ io_result result = read_blocks_no_cache(i, 1, block_cache);
+
+ if (result != io_result::success) {
+ block_cache_i = block_count;
+ return result;
+ }
+
+ block_cache_i = i;
+ return io_result::success;
+
+ }
+
+ io_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;
+
+ 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))
+ for (uint64_t i = 0; i < prefix_len; ++i)
+ into_u8[i] = block_cache[start % block_size + i];
+ into_u8 += prefix_len;
+ start += prefix_len;
+ count -= prefix_len;
+ }
+
+ 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))
+ 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))
+ 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, size_t 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) {
+
+ size_t segment_len = utility::find(str, len, '/');
+ size_t to_skip = segment_len == len ? len : 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)
+ ;
+ }
+
+ 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]);
+
+ 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;
+
+ }
+
+ 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))
+
+ if (resolve_final_node)
+ RETURN_IF_NOT_SUCCESS(resolve_symlinks(
+ bd_out, node_out, path_without_symlinks_out))
+
+ return io_result::success;
+
+ }
+
+}