summaryrefslogtreecommitdiff
path: root/kernel/vfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/vfile.cpp')
-rw-r--r--kernel/vfile.cpp192
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;
+
+ }
+
+}