diff options
Diffstat (limited to 'kernel/vfile.cpp')
-rw-r--r-- | kernel/vfile.cpp | 192 |
1 files changed, 192 insertions, 0 deletions
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; + + } + +} |