diff options
Diffstat (limited to 'kernel/storage.cpp')
-rw-r--r-- | kernel/storage.cpp | 246 |
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; + + } + +} |