#include //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 &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; } }