#include //in fs::tarfs_instance, storage::node_id_t refers to the number //of bytes into the block device that the info sector is located namespace mercury::kernel::fs { storage::io_result tarfs_mounter( storage::block_device *bd, storage::file_system_instance *&fs_out ) { fs_out = new tarfs_instance(bd); return storage::io_result::success; } tarfs_instance::tarfs_instance(storage::block_device *bd) : bd(bd) {} storage::io_result tarfs_instance::next_node(storage::node_id_t &node) { uint64_t file_length; storage::io_result result = read_num(node + 124, 12, file_length); if (result != storage::io_result::success) return result; node += ((file_length - 1) / 512 + 2) * 512; uint8_t sector[512]; result = bd->read_bytes(node, 512, sector); if (result != storage::io_result::success) return result; for (unsigned i = 0; i < 512; ++i) if (sector[i] != 0) return storage::io_result::success; return storage::io_result::out_of_bounds; } storage::io_result tarfs_instance::read_name( storage::node_id_t node, char *name_buf, unsigned &name_len_out ) { name_len_out = 0; storage::io_result result = bd->read_bytes(node + 345, 155, name_buf); if (result != storage::io_result::success) return result; while (name_buf[name_len_out] && name_len_out < 155) ++name_len_out; result = bd->read_bytes(node, 100, name_buf + name_len_out); if (result != storage::io_result::success) return result; unsigned new_limit = name_len_out + 100; while (name_buf[name_len_out] && name_len_out < new_limit) ++name_len_out; return storage::io_result::success; } storage::io_result tarfs_instance::read_num( uint64_t offset, unsigned len, uint64_t &out ) { //len <= 12 char buffer[12]; storage::io_result result = bd->read_bytes(offset, len, buffer); if (result != storage::io_result::success) return result; out = 0; for (unsigned i = 0; i < len; ++i) { if (!buffer[i]) return i == 0 ? storage::io_result::fs_corrupt : storage::io_result::success; if (buffer[i] < '0' || buffer[i] > '7') return storage::io_result::fs_corrupt; out = out * 8 + buffer[i] - '0'; } return storage::io_result::success; } #define RETURN_MAYBE_NOT_FOUND(expr) \ { \ storage::io_result _result = expr; \ if (_result == storage::io_result::out_of_bounds) \ return storage::io_result::not_found; \ if (_result != storage::io_result::success) \ return _result; \ } #define RETURN_NOT_SUCCESS(expr) \ { \ storage::io_result _result = expr; \ if (_result != storage::io_result::success) \ return _result; \ } storage::io_result tarfs_instance::get_root_node(storage::node_id_t &out) { out = 0; while (true) { char name_buf[255]; unsigned name_len; RETURN_MAYBE_NOT_FOUND(read_name(out, name_buf, name_len)) if (name_len == 2 && name_buf[0] == '.' && name_buf[1] == '/') return storage::io_result::success; RETURN_MAYBE_NOT_FOUND(next_node(out)) } } storage::io_result tarfs_instance::get_first_child(storage::node_id_t node, storage::node_id_t &out, storage::directory_iter_t &iter_out ) { char name_buf[255]; unsigned name_len; RETURN_NOT_SUCCESS(read_name(node, name_buf, name_len)) out = 0; while (true) { char cand_name_buf[255]; unsigned cand_name_len; RETURN_MAYBE_NOT_FOUND(read_name(out, cand_name_buf, cand_name_len)) if (cand_name_len > name_len && utility::starts_with( cand_name_buf, cand_name_len, name_buf, name_len )) { const char *rem = cand_name_buf + name_len; unsigned rem_len = cand_name_len - name_len; unsigned slash = utility::find(rem, rem_len, '/'); if (slash == rem_len - 1 || slash == rem_len) { iter_out = out; return storage::io_result::success; } } RETURN_MAYBE_NOT_FOUND(next_node(out)) } } storage::io_result tarfs_instance::get_next_child(storage::node_id_t node, storage::node_id_t &out, storage::directory_iter_t &iter ) { char name_buf[255]; unsigned name_len; RETURN_NOT_SUCCESS(read_name(node, name_buf, name_len)) out = iter; RETURN_MAYBE_NOT_FOUND(next_node(out)) while (true) { char cand_name_buf[255]; unsigned cand_name_len; RETURN_MAYBE_NOT_FOUND(read_name(out, cand_name_buf, cand_name_len)) if (cand_name_len > name_len && utility::starts_with( cand_name_buf, cand_name_len, name_buf, name_len )) { const char *rem = cand_name_buf + name_len; unsigned rem_len = cand_name_len - name_len; unsigned slash = utility::find(rem, rem_len, '/'); if (slash == rem_len - 1 || slash == rem_len) { iter = out; return storage::io_result::success; } } RETURN_MAYBE_NOT_FOUND(next_node(out)) } } storage::io_result tarfs_instance::get_child(storage::node_id_t node, storage::node_id_t &out, const char *name, unsigned name_len ) { char full_name[255]; unsigned full_name_len; RETURN_MAYBE_NOT_FOUND(read_name(node, full_name, full_name_len)) if (full_name_len + name_len > 255) return storage::io_result::not_supported; for (unsigned i = 0; i < name_len; ++i) full_name[full_name_len + i] = name[i]; full_name_len += name_len; out = 0; while (true) { char cand_name[255]; unsigned cand_name_len; RETURN_MAYBE_NOT_FOUND(read_name(out, cand_name, cand_name_len)) if (cand_name_len != full_name_len && cand_name_len != full_name_len + 1) goto next_iter; for (unsigned i = 0; i < full_name_len; ++i) if (cand_name[i] != full_name[i]) goto next_iter; if (cand_name_len == full_name_len + 1 && cand_name[full_name_len] != '/') goto next_iter; return storage::io_result::success; next_iter: RETURN_MAYBE_NOT_FOUND(next_node(out)) } } storage::io_result tarfs_instance::get_name_length( storage::node_id_t node, unsigned &length_out ) { char name_buf[255]; return get_name(node, name_buf, length_out); } storage::io_result tarfs_instance::get_name( storage::node_id_t node, char *buffer, unsigned &length_out ) { unsigned full_length; char full_buffer[255]; RETURN_NOT_SUCCESS(read_name(node, full_buffer, full_length)) if (full_length == 2) { //full buffer must be ./ length_out = 0; return storage::io_result::success; } if (full_buffer[full_length - 1] == '/') --full_length; unsigned start = utility::find_last(full_buffer, full_length, '/') + 1; length_out = full_length - start; for (unsigned i = 0; i < length_out; ++i) buffer[i] = full_buffer[start + i]; return storage::io_result::success; } storage::io_result tarfs_instance::get_file_length( storage::node_id_t node, uint64_t &length_out ) { return read_num(node + 124, 12, length_out); } storage::io_result tarfs_instance::get_file_type( storage::node_id_t node, storage::file_type &out ) { uint64_t ft; storage::io_result result = read_num(node + 156, 1, ft); if (result != storage::io_result::success) return result; switch (ft) { case 0: out = storage::file_type::regular_file; return storage::io_result::success; case 2: out = storage::file_type::symlink; return storage::io_result::success; case 5: out = storage::file_type::directory; return storage::io_result::success; default: return storage::io_result::not_supported; } } storage::io_result tarfs_instance::resize_file( storage::node_id_t, uint64_t ) { //TODO: support resize if sector count isn't changed? return storage::io_result::not_supported; } storage::io_result tarfs_instance::read_bytes_from_file( storage::node_id_t node, uint64_t start, uint64_t count, void *into ) { return bd->read_bytes(node + 512 + start, count, into); } storage::io_result tarfs_instance::write_bytes_into_file( storage::node_id_t, uint64_t, uint64_t, const void * ) { //TODO: support this. return storage::io_result::not_supported; } }