summaryrefslogtreecommitdiff
path: root/kernel/storage/fs
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/storage/fs')
-rw-r--r--kernel/storage/fs/tarfs.cpp254
1 files changed, 254 insertions, 0 deletions
diff --git a/kernel/storage/fs/tarfs.cpp b/kernel/storage/fs/tarfs.cpp
new file mode 100644
index 0000000..4b752a2
--- /dev/null
+++ b/kernel/storage/fs/tarfs.cpp
@@ -0,0 +1,254 @@
+#include <mercury/kernel/storage/fs/tarfs.hpp>
+
+//in tarfs_instance, node_id_t and directory_iter_t refer to the number
+//of bytes into the block device that the info sector is located.
+
+namespace mercury::kernel::storage::fs {
+
+#define BD_TO_FS(expr) \
+ { \
+ bd_result _result = expr; \
+ if (_result == bd_result::out_of_bounds) \
+ return fs_result::fs_corrupt; \
+ if (_result == bd_result::device_error) \
+ return fs_result::device_error; \
+ }
+
+#define FS_TO_FS(expr) \
+ { \
+ fs_result _result = expr; \
+ if (_result != fs_result::success) \
+ return _result; \
+ }
+
+ tarfs_instance::tarfs_instance(block_device *bd) : bd(bd) {}
+
+ fs_result tarfs_instance::next_node(
+ node_id_t node, std::optional<node_id_t> &out
+ ) {
+
+ uint64_t bytes;
+ FS_TO_FS(read_num(node + 124, 12, bytes))
+ out = node + ((bytes - 1) / 512 + 2) * 512;
+
+ uint8_t sector[512];
+ BD_TO_FS(bd->read_bytes(node, 512, sector))
+ for (unsigned i = 0; i < 512; ++i)
+ if (sector[i] != 0)
+ return fs_result::success;
+
+ out = {};
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::read_full_name(
+ node_id_t node, utility::string &out
+ ) {
+
+ out.count = 0;
+ out.verify_buffer_len(155);
+ BD_TO_FS(bd->read_bytes(node + 345, 155, out.buffer))
+
+ while (out.count < 155 && out.buffer[out.count] != '\0')
+ ++out.count;
+
+ unsigned new_max = out.count + 100;
+ out.verify_buffer_len(new_max);
+ BD_TO_FS(bd->read_bytes(node, 100, out.buffer + out.count))
+
+ while (out.count < 255 && out.buffer[out.count] != '\0')
+ ++out.count;
+
+ return fs_result::success;
+
+ }
+
+ //len <= 12.
+ fs_result tarfs_instance::read_num(
+ uint64_t offset, unsigned len, uint64_t &out
+ ) {
+
+ char buf[12];
+ BD_TO_FS(bd->read_bytes(offset, len, buf))
+
+ out = 0;
+ for (unsigned i = 0; i < len && buf[i] != '\0'; ++i) {
+ if (buf[i] < '0' || buf[i] > '7')
+ return fs_result::fs_corrupt;
+ out = out * 8 + buf[i] - '0';
+ }
+
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::first_child_starting_at(
+ node_id_t parent, node_id_t start, std::optional<node_id_t> &out
+ ) {
+
+ utility::string parent_full_name;
+ FS_TO_FS(read_full_name(parent, parent_full_name))
+
+ utility::string child_full_name;
+ out = start;
+
+ do {
+
+ FS_TO_FS(read_full_name(*out, child_full_name))
+
+ if (child_full_name.count > parent_full_name.count &&
+ child_full_name.starts_with(parent_full_name)
+ ) {
+ if (child_full_name.buffer[child_full_name.count - 1] == '/')
+ --child_full_name.count;
+ for (unsigned i = parent_full_name.count;
+ i < child_full_name.count; ++i)
+ if (child_full_name.buffer[i] == '/')
+ goto next;
+ return fs_result::success;
+ }
+
+ next:
+ next_node(*out, out);
+
+ } while (out);
+
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::get_dir_entry(node_id_t node, dir_entry &entry) {
+
+ utility::string full_name;
+ read_full_name(node, full_name);
+
+ if (full_name.count == 2)
+ entry.name.count = 0;
+
+ else {
+
+ if (full_name.buffer[full_name.count - 1] == '/')
+ --full_name.count;
+
+ unsigned last_slash =
+ utility::find_last(full_name.buffer, full_name.count, '/');
+ entry.name.count = full_name.count - last_slash - 1;
+ entry.name.verify_buffer_len(entry.name.count);
+
+ for (unsigned i = 0; i < entry.name.count; ++i)
+ entry.name.buffer[i] = full_name.buffer[last_slash + 1 + i];
+
+ }
+
+ entry.node = node;
+
+ char ch;
+ BD_TO_FS(bd->read_bytes(node + 156, 1, &ch));
+ switch (ch) {
+ case '0':
+ entry.type = file_type::regular_file;
+ break;
+ case '2':
+ entry.type = file_type::symlink;
+ break;
+ case '5':
+ entry.type = file_type::directory;
+ break;
+ default:
+ return fs_result::fs_corrupt;
+ }
+
+ if (entry.type == file_type::regular_file) {
+ uint64_t length;
+ FS_TO_FS(read_num(node + 124, 12, length))
+ entry.length = length;
+ }
+
+ else if (entry.type == file_type::symlink) {
+ utility::string target;
+ target.verify_buffer_len(100);
+ BD_TO_FS(bd->read_bytes(node + 157, 100, target.buffer))
+ while (target.count < 100 && target.buffer[target.count] != '\0')
+ ++target.count;
+ entry.target = std::move(target);
+ }
+
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::get_root_node(node_id_t &out) {
+
+ utility::string full_name;
+ std::optional<node_id_t> on = 0;
+
+ do {
+
+ FS_TO_FS(read_full_name(*on, full_name))
+ if (full_name.count == 2) {
+ out = *on;
+ return fs_result::success;
+ }
+ next_node(*on, on);
+
+ } while (on);
+
+ return fs_result::fs_corrupt;
+
+ }
+
+ fs_result tarfs_instance::get_first_child(
+ node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter_out
+ ) {
+
+ std::optional<node_id_t> child;
+ FS_TO_FS(first_child_starting_at(node, 0, child))
+ if (!child) {
+ out = {};
+ return fs_result::success;
+ }
+
+ dir_entry entry;
+ FS_TO_FS(get_dir_entry(*child, entry))
+ out = std::move(entry);
+ iter_out = (directory_iter_t)*child;
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::get_next_child(
+ node_id_t node, std::optional<dir_entry> &out, directory_iter_t &iter
+ ) {
+
+ std::optional<node_id_t> start;
+ FS_TO_FS(next_node((node_id_t)iter, start))
+ if (!start) {
+ out = {};
+ return fs_result::success;
+ }
+
+ std::optional<node_id_t> child;
+ FS_TO_FS(first_child_starting_at(node, *start, child))
+ if (!child) {
+ out = {};
+ return fs_result::success;
+ }
+
+ dir_entry entry;
+ FS_TO_FS(get_dir_entry(*child, entry))
+ out = std::move(entry);
+ iter = (directory_iter_t)*child;
+ return fs_result::success;
+
+ }
+
+ fs_result tarfs_instance::read_bytes_from_file(
+ node_id_t node, uint64_t start, uint64_t count, void *into
+ ) {
+ BD_TO_FS(bd->read_bytes(node + 512 + start, count, into))
+ return fs_result::success;
+ }
+
+
+}