#include #define RETURN_IF_NOT_SUCCESS(expr) \ { \ io_result _result = expr; \ if (_result != io_result::success) \ return _result; \ } namespace mercury::kernel::storage { io_result block_device::load_cache_block(uint64_t i) { 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_devices; static utility::trie *mounted_devices; void init_storage() { block_devices = new utility::list(); mounted_devices = new utility::trie(); } void canon_path::parent() { if (segments.count != 0) --segments.count; else if (!absolute) ++parent_count; } void canon_path::rel(const canon_path &r) { if (r.absolute) { segments.count = 0; absolute = true; parent_count = 0; } for (unsigned i = 0; i < r.parent_count; ++i) parent(); for (unsigned i = 0; i < r.segments.count; ++i) segments.add_end(r.segments.buffer[i]); } void canonize_path(const char *str, unsigned len, canon_path &out) { out.absolute = false; out.parent_count = 0; out.segments.count = 0; if (len == 0) return; if (len == 1 && str[0] == '/') { out.absolute = true; return; } if (str[0] == '/') { out.absolute = true; ++str; --len; } while (len != 0) { unsigned segment_len = utility::find(str, len, '/'); unsigned to_skip = segment_len == len ? segment_len : segment_len + 1; if (segment_len == 0) ; else if (segment_len == 1 && str[0] == '.') ; else if (segment_len == 2 && str[0] == '.' && str[1] == '.') out.parent(); else { utility::string segment(str, segment_len); out.segments.add_end(std::move(segment)); } str += to_skip; len -= to_skip; } } io_result mount_device( block_device *bd, const canon_path &path, file_system_mounter mounter ) { if (!path.absolute) return io_result::bad_path; if (mounted_devices->has_key(path.segments)) return io_result::mount_point_already_used; file_system_instance *fs; RETURN_IF_NOT_SUCCESS(mounter(bd, fs)); bd->mounted_as = fs; mounted_devices->try_insert(path.segments, bd); return io_result::success; } static io_result symlink_contents( block_device *bd, node_id_t node, canon_path &out ) { //TODO while (1) ; (void)bd; (void)node; (void)out; } static io_result resolve_symlinks( block_device *&bd, node_id_t &node, canon_path &path ) { file_type ft; RETURN_IF_NOT_SUCCESS(bd->mounted_as->get_file_type(node, ft)) if (ft != file_type::symlink) return io_result::success; canon_path contents; RETURN_IF_NOT_SUCCESS(symlink_contents(bd, node, contents)) path.parent(); path.rel(contents); return look_up_absolute_path(path, bd, node, true, path); } io_result look_up_absolute_path(const canon_path &path, block_device *&bd_out, node_id_t &node_out, bool resolve_final_node, canon_path &path_without_symlinks_out ) { if (!path.absolute) return io_result::bad_path; unsigned prefix_length; bd_out = *mounted_devices->longest_prefix_with_value(path.segments, prefix_length) .value_here; RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_root_node(node_out)) path_without_symlinks_out.absolute = true; path_without_symlinks_out.parent_count = 0; path_without_symlinks_out.segments.count = 0; for (unsigned i = 0; i < prefix_length; ++i) path_without_symlinks_out.segments.add_end(path.segments.buffer[i]); if (path.segments.count == 0) goto on_final_node; for (unsigned i = prefix_length; i < path.segments.count - 1; ++i) { path_without_symlinks_out.segments.add_end(path.segments.buffer[i]); RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_child(node_out, node_out, path.segments.buffer[i].buffer, path.segments.buffer[i].count)) RETURN_IF_NOT_SUCCESS(resolve_symlinks( bd_out, node_out, path_without_symlinks_out)) file_type ft; RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_file_type(node_out, ft)) if (ft != file_type::directory) return io_result::not_a_directory; } { //in a block so that compiler sees last_segment //isn't needed after this and allows the goto above. const utility::string &last_segment = path.segments.buffer[path.segments.count - 1]; path_without_symlinks_out.segments.add_end(last_segment); RETURN_IF_NOT_SUCCESS(bd_out->mounted_as->get_child( node_out, node_out, last_segment.buffer, last_segment.count)) } on_final_node: if (resolve_final_node) RETURN_IF_NOT_SUCCESS(resolve_symlinks( bd_out, node_out, path_without_symlinks_out)) return io_result::success; } }