summaryrefslogtreecommitdiff
path: root/kernel/source/vfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/source/vfile.cpp')
-rw-r--r--kernel/source/vfile.cpp206
1 files changed, 206 insertions, 0 deletions
diff --git a/kernel/source/vfile.cpp b/kernel/source/vfile.cpp
new file mode 100644
index 0000000..89c95e6
--- /dev/null
+++ b/kernel/source/vfile.cpp
@@ -0,0 +1,206 @@
+#include <hilbert/kernel/vfile.hpp>
+
+//TODO: handle symlink loops nicely in vfile::get_child,
+// vfile::get_children, and lookup_path.
+
+namespace hilbert::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(utility::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(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);
+
+ vfile next;
+ RET_NOT_SUC(lookup_path(full_path, next, false))
+
+ next.path = path;
+ return next.follow_symlinks(out);
+
+ }
+
+ storage::fs_result vfile::get_child(
+ vfile &out, const utility::string &name
+ ) const {
+
+ storage::dir_entry entry;
+ storage::directory_iter_t iter;
+
+ RET_NOT_SUC(bd->mounted_as->get_first_child(dir_entry.node, entry, iter))
+
+ while (true) {
+
+ if (entry.name == name) {
+
+ vfile vf;
+ vf.bd = bd;
+ vf.dir_entry = utility::move(entry);
+ vf.path = path;
+ vf.path.segments.add_end(name);
+ out = utility::move(vf);
+ return storage::fs_result::success;
+
+ }
+
+ RET_NOT_SUC(bd->mounted_as->get_next_child(dir_entry.node, entry, iter))
+
+ }
+
+ }
+
+ storage::fs_result vfile::get_children(utility::vector<vfile> &out) const {
+
+ storage::dir_entry entry;
+ storage::directory_iter_t iter;
+
+ storage::fs_result result =
+ bd->mounted_as->get_first_child(dir_entry.node, entry, iter);
+ if (result == storage::fs_result::does_not_exist)
+ return storage::fs_result::success;
+ else if (result != storage::fs_result::success)
+ return result;
+
+ while (true) {
+
+ vfile vf;
+ vf.bd = bd;
+ vf.path = path;
+ vf.path.segments.add_end(entry.name);
+ vf.dir_entry = utility::move(entry);
+ out.add_end(utility::move(vf));
+
+ result = bd->mounted_as->get_next_child(dir_entry.node, entry, iter);
+ if (result == storage::fs_result::does_not_exist)
+ return storage::fs_result::success;
+ else if (result != storage::fs_result::success)
+ return result;
+
+ }
+
+ }
+
+ storage::fs_result vfile::read_file(
+ uint64_t start, uint64_t length, void *into
+ ) const {
+ return bd->mounted_as->read_bytes_from_file(
+ dir_entry.node, start, length, into);
+ }
+
+ //TODO: see comment at top of vfile.hpp.
+ static const vfile *root;
+
+ void set_root(const vfile &root) {
+ kernel::vfile::root = new vfile(root);
+ }
+
+ storage::fs_result lookup_path(
+ const canon_path &path, vfile &out, bool follow_final_symlink
+ ) {
+
+ //assume path is absolute.
+
+ out = *root;
+ for (unsigned i = 0; i < path.segments.count; ++i) {
+
+ vfile result;
+ RET_NOT_SUC(out.follow_symlinks(result))
+ out = utility::move(result);
+
+ RET_NOT_SUC(out.get_child(result, path.segments.buffer[i]))
+ out = utility::move(result);
+
+ }
+
+ if (follow_final_symlink) {
+ vfile result;
+ RET_NOT_SUC(out.follow_symlinks(result))
+ out = utility::move(result);
+ }
+
+ return storage::fs_result::success;
+
+ }
+
+}