This repository has been archived on 2025-02-26. You can view files and clone it, but cannot push or open issues or pull requests.
hilbert-os/kernel/vfile.cpp
2024-01-13 16:43:49 -05:00

199 lines
4.4 KiB
C++

#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 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);
}
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;
}
}