#include #include #include #include #include #include #include #include #include #include #define FS_MAGIC 0x53465331u #define CLUSTER_SIZE 1024u #define MAX_NAME 32 #define MAX_PATH 256 #define MAX_ENTRIES 128 #define MAX_FILE_CLUSTERS 128 #define MAX_OPEN_DATA (MAX_FILE_CLUSTERS * CLUSTER_SIZE) #define MAX_HISTORY 50 #define MAX_LINE 1024 #define TYPE_FREE 0 #define TYPE_FILE 1 #define TYPE_DIR 2 #define MODE_READ 1 #define MODE_WRITE 2 typedef struct { uint32_t magic; uint32_t disk_size; uint32_t cluster_size; uint32_t total_clusters; uint32_t bitmap_offset; uint32_t bitmap_size; uint32_t index_offset; uint32_t index_clusters; uint32_t data_start_cluster; uint32_t root_index; } SuperBlock; typedef struct { char name[MAX_NAME]; uint8_t type; uint8_t compressed; uint16_t reserved; uint32_t parent; uint32_t size; /* logical size visible to user */ uint32_t stored_size; /* bytes really stored on disk */ uint32_t cluster_count; uint32_t clusters[MAX_FILE_CLUSTERS]; } Entry; typedef struct { FILE *disk; char image_path[MAX_PATH]; SuperBlock sb; unsigned char *bitmap; Entry entries[MAX_ENTRIES]; uint32_t current_dir; bool mounted; } FileSystem; typedef struct { FileSystem *fs; int entry_index; int mode; unsigned char *data; uint32_t size; uint32_t capacity; uint32_t position; bool dirty; } OpenFile; static void safe_copy(char *dst, const char *src, size_t size) { if (size == 0) return; strncpy(dst, src, size - 1); dst[size - 1] = '\0'; } static uint32_t bytes_to_clusters(uint32_t bytes) { if (bytes == 0) return 0; return (bytes + CLUSTER_SIZE - 1) / CLUSTER_SIZE; } static bool valid_name(const char *name) { return name && name[0] && strlen(name) < MAX_NAME && strchr(name, '/') == NULL; } static bool join_path(char *out, size_t out_size, const char *left, const char *right) { size_t left_len = strlen(left); size_t right_len = strlen(right); if (left_len + 1 + right_len + 1 > out_size) return false; memcpy(out, left, left_len); out[left_len] = '/'; memcpy(out + left_len + 1, right, right_len + 1); return true; } static bool bit_is_set(FileSystem *fs, uint32_t cluster) { return (fs->bitmap[cluster / 8] & (1u << (cluster % 8))) != 0; } static void bit_set(FileSystem *fs, uint32_t cluster) { fs->bitmap[cluster / 8] |= (unsigned char)(1u << (cluster % 8)); } static void bit_clear(FileSystem *fs, uint32_t cluster) { fs->bitmap[cluster / 8] &= (unsigned char)~(1u << (cluster % 8)); } static bool fs_sync(FileSystem *fs) { if (!fs || !fs->mounted || !fs->disk) return false; fseek(fs->disk, 0, SEEK_SET); fwrite(&fs->sb, sizeof(SuperBlock), 1, fs->disk); fseek(fs->disk, fs->sb.bitmap_offset, SEEK_SET); fwrite(fs->bitmap, 1, fs->sb.bitmap_size, fs->disk); fseek(fs->disk, fs->sb.index_offset, SEEK_SET); fwrite(fs->entries, sizeof(Entry), MAX_ENTRIES, fs->disk); fflush(fs->disk); return true; } static int alloc_cluster(FileSystem *fs) { uint32_t i; for (i = fs->sb.data_start_cluster; i < fs->sb.total_clusters; i++) { if (!bit_is_set(fs, i)) { bit_set(fs, i); return (int)i; } } return -1; } static void free_entry_clusters(FileSystem *fs, Entry *entry) { uint32_t i; for (i = 0; i < entry->cluster_count; i++) { if (entry->clusters[i] < fs->sb.total_clusters) bit_clear(fs, entry->clusters[i]); } entry->cluster_count = 0; entry->stored_size = 0; } static int find_child(FileSystem *fs, uint32_t parent, const char *name) { int i; for (i = 0; i < MAX_ENTRIES; i++) { if (fs->entries[i].type != TYPE_FREE && fs->entries[i].parent == parent && strcmp(fs->entries[i].name, name) == 0) return i; } return -1; } static int free_entry_index(FileSystem *fs) { int i; for (i = 0; i < MAX_ENTRIES; i++) if (fs->entries[i].type == TYPE_FREE) return i; return -1; } static int resolve_path(FileSystem *fs, const char *path) { char copy[MAX_PATH]; char *part; uint32_t current; if (!path || !path[0]) return -1; safe_copy(copy, path, sizeof(copy)); current = (copy[0] == '/') ? fs->sb.root_index : fs->current_dir; part = strtok(copy, "/"); if (!part && path[0] == '/') return (int)fs->sb.root_index; while (part) { if (strcmp(part, ".") == 0) { /* stay */ } else if (strcmp(part, "..") == 0) { current = fs->entries[current].parent; } else { int next = find_child(fs, current, part); if (next < 0) return -1; current = (uint32_t)next; } part = strtok(NULL, "/"); } return (int)current; } static bool split_parent_name(FileSystem *fs, const char *path, int *parent, char *name) { char copy[MAX_PATH]; char *slash; if (!path || !path[0]) return false; safe_copy(copy, path, sizeof(copy)); slash = strrchr(copy, '/'); if (!slash) { *parent = (int)fs->current_dir; safe_copy(name, copy, MAX_NAME); } else if (slash == copy) { *parent = (int)fs->sb.root_index; safe_copy(name, slash + 1, MAX_NAME); } else { *slash = '\0'; *parent = resolve_path(fs, copy); safe_copy(name, slash + 1, MAX_NAME); } return *parent >= 0 && fs->entries[*parent].type == TYPE_DIR && valid_name(name); } static bool write_stored_bytes(FileSystem *fs, Entry *entry, const unsigned char *data, uint32_t size) { uint32_t needed = bytes_to_clusters(size); uint32_t i, written = 0; if (needed > MAX_FILE_CLUSTERS) return false; free_entry_clusters(fs, entry); entry->cluster_count = needed; entry->stored_size = size; for (i = 0; i < needed; i++) { int c = alloc_cluster(fs); if (c < 0) { free_entry_clusters(fs, entry); return false; } entry->clusters[i] = (uint32_t)c; } for (i = 0; i < needed; i++) { uint32_t chunk = size - written; if (chunk > CLUSTER_SIZE) chunk = CLUSTER_SIZE; fseek(fs->disk, (long)(entry->clusters[i] * CLUSTER_SIZE), SEEK_SET); fwrite(data + written, 1, chunk, fs->disk); if (chunk < CLUSTER_SIZE) { unsigned char zero[CLUSTER_SIZE] = {0}; fwrite(zero, 1, CLUSTER_SIZE - chunk, fs->disk); } written += chunk; } return true; } static unsigned char *read_stored_bytes(FileSystem *fs, Entry *entry) { unsigned char *data; uint32_t i, read_count = 0; if (entry->stored_size == 0) return calloc(1, 1); data = malloc(entry->stored_size); if (!data) return NULL; for (i = 0; i < entry->cluster_count; i++) { uint32_t chunk = entry->stored_size - read_count; if (chunk > CLUSTER_SIZE) chunk = CLUSTER_SIZE; fseek(fs->disk, (long)(entry->clusters[i] * CLUSTER_SIZE), SEEK_SET); fread(data + read_count, 1, chunk, fs->disk); read_count += chunk; } return data; } static unsigned char *rle_compress(const unsigned char *src, uint32_t size, uint32_t *out_size) { unsigned char *out; uint32_t i = 0, j = 0; out = malloc(size * 2 + 2); if (!out) return NULL; while (i < size) { unsigned char value = src[i]; uint32_t count = 1; while (i + count < size && src[i + count] == value && count < 255) count++; out[j++] = (unsigned char)count; out[j++] = value; i += count; } *out_size = j; return out; } static unsigned char *rle_decompress(const unsigned char *src, uint32_t stored_size, uint32_t logical_size) { unsigned char *out; uint32_t i = 0, j = 0, k; out = malloc(logical_size + 1); if (!out) return NULL; while (i + 1 < stored_size && j < logical_size) { unsigned char count = src[i++]; unsigned char value = src[i++]; for (k = 0; k < count && j < logical_size; k++) out[j++] = value; } if (j != logical_size) { free(out); return NULL; } return out; } static unsigned char *read_logical_bytes(FileSystem *fs, Entry *entry) { unsigned char *stored = read_stored_bytes(fs, entry); unsigned char *logical; if (!stored) return NULL; if (!entry->compressed) return stored; logical = rle_decompress(stored, entry->stored_size, entry->size); free(stored); return logical; } static bool store_logical_bytes(FileSystem *fs, Entry *entry, const unsigned char *data, uint32_t size) { entry->compressed = 0; entry->size = size; return write_stored_bytes(fs, entry, data, size); } bool fs_format(FileSystem *fs, const char *image_path, uint32_t size_kb) { uint32_t total_bytes = size_kb * 1024u; uint32_t bitmap_clusters, i; if (total_bytes < 128u * 1024u) return false; memset(fs, 0, sizeof(*fs)); safe_copy(fs->image_path, image_path, sizeof(fs->image_path)); fs->disk = fopen(image_path, "wb+"); if (!fs->disk) return false; fseek(fs->disk, (long)total_bytes - 1, SEEK_SET); fputc(0, fs->disk); fs->sb.magic = FS_MAGIC; fs->sb.disk_size = total_bytes; fs->sb.cluster_size = CLUSTER_SIZE; fs->sb.total_clusters = total_bytes / CLUSTER_SIZE; fs->sb.bitmap_offset = CLUSTER_SIZE; fs->sb.bitmap_size = (fs->sb.total_clusters + 7) / 8; bitmap_clusters = bytes_to_clusters(fs->sb.bitmap_size); fs->sb.index_offset = (1 + bitmap_clusters) * CLUSTER_SIZE; fs->sb.index_clusters = bytes_to_clusters((uint32_t)(sizeof(Entry) * MAX_ENTRIES)); fs->sb.data_start_cluster = 1 + bitmap_clusters + fs->sb.index_clusters; fs->sb.root_index = 0; if (fs->sb.data_start_cluster >= fs->sb.total_clusters) { fclose(fs->disk); return false; } fs->bitmap = calloc(fs->sb.bitmap_size, 1); if (!fs->bitmap) { fclose(fs->disk); return false; } for (i = 0; i < fs->sb.data_start_cluster; i++) bit_set(fs, i); memset(fs->entries, 0, sizeof(fs->entries)); safe_copy(fs->entries[0].name, "/", MAX_NAME); fs->entries[0].type = TYPE_DIR; fs->entries[0].parent = 0; fs->current_dir = 0; fs->mounted = true; fs_sync(fs); return true; } bool fs_mount(FileSystem *fs, const char *image_path) { memset(fs, 0, sizeof(*fs)); safe_copy(fs->image_path, image_path, sizeof(fs->image_path)); fs->disk = fopen(image_path, "rb+"); if (!fs->disk) return false; fread(&fs->sb, sizeof(SuperBlock), 1, fs->disk); if (fs->sb.magic != FS_MAGIC || fs->sb.cluster_size != CLUSTER_SIZE) { fclose(fs->disk); return false; } fs->bitmap = malloc(fs->sb.bitmap_size); if (!fs->bitmap) { fclose(fs->disk); return false; } fseek(fs->disk, fs->sb.bitmap_offset, SEEK_SET); fread(fs->bitmap, 1, fs->sb.bitmap_size, fs->disk); fseek(fs->disk, fs->sb.index_offset, SEEK_SET); fread(fs->entries, sizeof(Entry), MAX_ENTRIES, fs->disk); fs->current_dir = fs->sb.root_index; fs->mounted = true; return true; } void fs_unmount(FileSystem *fs) { if (!fs || !fs->mounted) return; fs_sync(fs); fclose(fs->disk); free(fs->bitmap); fs->disk = NULL; fs->bitmap = NULL; fs->mounted = false; } bool fs_create_dir(FileSystem *fs, const char *path) { int parent, index; char name[MAX_NAME]; if (!split_parent_name(fs, path, &parent, name) || find_child(fs, parent, name) >= 0) return false; index = free_entry_index(fs); if (index < 0) return false; memset(&fs->entries[index], 0, sizeof(Entry)); safe_copy(fs->entries[index].name, name, MAX_NAME); fs->entries[index].type = TYPE_DIR; fs->entries[index].parent = (uint32_t)parent; fs_sync(fs); return true; } bool fs_create_file(FileSystem *fs, const char *path) { int parent, index; char name[MAX_NAME]; if (!split_parent_name(fs, path, &parent, name) || find_child(fs, parent, name) >= 0) return false; index = free_entry_index(fs); if (index < 0) return false; memset(&fs->entries[index], 0, sizeof(Entry)); safe_copy(fs->entries[index].name, name, MAX_NAME); fs->entries[index].type = TYPE_FILE; fs->entries[index].parent = (uint32_t)parent; fs_sync(fs); return true; } bool fs_remove(FileSystem *fs, const char *path) { int index = resolve_path(fs, path); if (index <= 0 || fs->entries[index].type != TYPE_FILE) return false; free_entry_clusters(fs, &fs->entries[index]); memset(&fs->entries[index], 0, sizeof(Entry)); fs_sync(fs); return true; } bool fs_remove_dir(FileSystem *fs, const char *path) { int index = resolve_path(fs, path); int i; if (index <= 0 || fs->entries[index].type != TYPE_DIR) return false; for (i = 0; i < MAX_ENTRIES; i++) if (fs->entries[i].type != TYPE_FREE && fs->entries[i].parent == (uint32_t)index) return false; memset(&fs->entries[index], 0, sizeof(Entry)); fs_sync(fs); return true; } bool fs_rename(FileSystem *fs, const char *path, const char *new_name) { int index = resolve_path(fs, path); Entry *e; if (index <= 0 || !valid_name(new_name)) return false; e = &fs->entries[index]; if (find_child(fs, e->parent, new_name) >= 0) return false; safe_copy(e->name, new_name, MAX_NAME); fs_sync(fs); return true; } bool fs_move(FileSystem *fs, const char *from, const char *to) { int source = resolve_path(fs, from); int target = resolve_path(fs, to); int new_parent; char new_name[MAX_NAME]; if (source <= 0) return false; if (target >= 0 && fs->entries[target].type == TYPE_DIR) { new_parent = target; safe_copy(new_name, fs->entries[source].name, sizeof(new_name)); } else { if (!split_parent_name(fs, to, &new_parent, new_name)) return false; } if (find_child(fs, (uint32_t)new_parent, new_name) >= 0) return false; fs->entries[source].parent = (uint32_t)new_parent; safe_copy(fs->entries[source].name, new_name, MAX_NAME); fs_sync(fs); return true; } bool fs_cd(FileSystem *fs, const char *path) { int index = resolve_path(fs, path); if (index < 0 || fs->entries[index].type != TYPE_DIR) return false; fs->current_dir = (uint32_t)index; return true; } void fs_pwd(FileSystem *fs, char *out, size_t out_size) { char temp[MAX_PATH] = ""; uint32_t cur = fs->current_dir; if (cur == fs->sb.root_index) { safe_copy(out, "/", out_size); return; } while (cur != fs->sb.root_index) { char next[MAX_PATH]; size_t name_len = strlen(fs->entries[cur].name); size_t temp_len = strlen(temp); if (1 + name_len + temp_len + 1 > sizeof(next)) { safe_copy(out, "/", out_size); return; } next[0] = '/'; memcpy(next + 1, fs->entries[cur].name, name_len); memcpy(next + 1 + name_len, temp, temp_len + 1); safe_copy(temp, next, sizeof(temp)); cur = fs->entries[cur].parent; } safe_copy(out, temp, out_size); } void fs_list(FileSystem *fs, const char *path) { int dir = path ? resolve_path(fs, path) : (int)fs->current_dir; int i; if (dir < 0 || fs->entries[dir].type != TYPE_DIR) { printf("Directory not found.\n"); return; } printf("%-32s %-8s %-10s %-10s\n", "name", "type", "size", "stored"); for (i = 0; i < MAX_ENTRIES; i++) { Entry *e = &fs->entries[i]; if (e->type != TYPE_FREE && e->parent == (uint32_t)dir && i != dir) { printf("%-32s %-8s %-10u %-10u%s\n", e->name, e->type == TYPE_DIR ? "dir" : "file", e->size, e->stored_size, e->compressed ? " compressed" : ""); } } } OpenFile *fs_open(FileSystem *fs, const char *path, const char *mode) { int index = resolve_path(fs, path); OpenFile *f; if (index < 0 && strchr(mode, 'w')) { if (!fs_create_file(fs, path)) return NULL; index = resolve_path(fs, path); } if (index < 0 || fs->entries[index].type != TYPE_FILE) return NULL; f = calloc(1, sizeof(OpenFile)); if (!f) return NULL; f->fs = fs; f->entry_index = index; f->mode = strchr(mode, 'w') ? MODE_WRITE : MODE_READ; f->capacity = MAX_OPEN_DATA; f->data = calloc(f->capacity, 1); if (!f->data) { free(f); return NULL; } if (!strchr(mode, 'w')) { unsigned char *existing = read_logical_bytes(fs, &fs->entries[index]); if (!existing && fs->entries[index].size > 0) { free(f->data); free(f); return NULL; } memcpy(f->data, existing, fs->entries[index].size); f->size = fs->entries[index].size; free(existing); } else { f->dirty = true; } return f; } int fs_seek(OpenFile *f, uint32_t position) { if (!f || position > f->size) return -1; f->position = position; return 0; } uint32_t fs_read(OpenFile *f, unsigned char *buffer, uint32_t count) { uint32_t available; if (!f || !buffer) return 0; available = f->size - f->position; if (count > available) count = available; memcpy(buffer, f->data + f->position, count); f->position += count; return count; } uint32_t fs_write(OpenFile *f, const unsigned char *buffer, uint32_t count) { if (!f || !buffer || f->mode != MODE_WRITE || f->position + count > f->capacity) return 0; memcpy(f->data + f->position, buffer, count); f->position += count; if (f->position > f->size) f->size = f->position; f->dirty = true; return count; } bool fs_close(OpenFile *f) { bool ok = true; if (!f) return false; if (f->dirty) { ok = store_logical_bytes(f->fs, &f->fs->entries[f->entry_index], f->data, f->size); fs_sync(f->fs); } free(f->data); free(f); return ok; } bool fs_write_text(FileSystem *fs, const char *path, const char *text) { OpenFile *f = fs_open(fs, path, "w"); bool ok; if (!f) return false; ok = fs_write(f, (const unsigned char *)text, (uint32_t)strlen(text)) == strlen(text); return fs_close(f) && ok; } bool fs_print_file(FileSystem *fs, const char *path) { OpenFile *f = fs_open(fs, path, "r"); unsigned char buffer[CLUSTER_SIZE + 1]; uint32_t n; if (!f) return false; while ((n = fs_read(f, buffer, CLUSTER_SIZE)) > 0) { buffer[n] = '\0'; fwrite(buffer, 1, n, stdout); } printf("\n"); return fs_close(f); } bool fs_copy_file(FileSystem *fs, const char *from, const char *to) { OpenFile *in; OpenFile *out; unsigned char buffer[CLUSTER_SIZE]; uint32_t n; int source = resolve_path(fs, from); int target = resolve_path(fs, to); char destination[MAX_PATH]; if (source < 0 || fs->entries[source].type != TYPE_FILE) return false; if (target >= 0 && fs->entries[target].type == TYPE_DIR) { snprintf(destination, sizeof(destination), "%s/%s", to, fs->entries[source].name); } else { safe_copy(destination, to, sizeof(destination)); } in = fs_open(fs, from, "r"); out = fs_open(fs, destination, "w"); if (!in || !out) { if (in) fs_close(in); if (out) fs_close(out); return false; } while ((n = fs_read(in, buffer, sizeof(buffer))) > 0) { if (fs_write(out, buffer, n) != n) { fs_close(in); fs_close(out); return false; } } fs_close(in); return fs_close(out); } bool fs_import_file(FileSystem *fs, const char *host_path, const char *fs_path) { FILE *in = fopen(host_path, "rb"); OpenFile *out; unsigned char buffer[CLUSTER_SIZE]; size_t n; if (!in) return false; out = fs_open(fs, fs_path, "w"); if (!out) { fclose(in); return false; } while ((n = fread(buffer, 1, sizeof(buffer), in)) > 0) { if (fs_write(out, buffer, (uint32_t)n) != n) { fclose(in); fs_close(out); return false; } } fclose(in); return fs_close(out); } bool fs_export_file(FileSystem *fs, const char *fs_path, const char *host_path) { FILE *out = fopen(host_path, "wb"); OpenFile *in; unsigned char buffer[CLUSTER_SIZE]; uint32_t n; if (!out) return false; in = fs_open(fs, fs_path, "r"); if (!in) { fclose(out); return false; } while ((n = fs_read(in, buffer, sizeof(buffer))) > 0) fwrite(buffer, 1, n, out); fclose(out); return fs_close(in); } static bool fs_import_dir(FileSystem *fs, const char *host_path, const char *fs_path) { DIR *dir = opendir(host_path); struct dirent *item; if (!dir) return false; if (resolve_path(fs, fs_path) < 0 && !fs_create_dir(fs, fs_path)) { closedir(dir); return false; } while ((item = readdir(dir)) != NULL) { char host_child[MAX_PATH], fs_child[MAX_PATH]; struct stat st; if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0) continue; if (!join_path(host_child, sizeof(host_child), host_path, item->d_name)) continue; if (!join_path(fs_child, sizeof(fs_child), fs_path, item->d_name)) continue; if (stat(host_child, &st) != 0) continue; if (S_ISDIR(st.st_mode)) fs_import_dir(fs, host_child, fs_child); else fs_import_file(fs, host_child, fs_child); } closedir(dir); return true; } static bool fs_export_dir(FileSystem *fs, const char *fs_path, const char *host_path) { int dir = resolve_path(fs, fs_path); int i; if (dir < 0 || fs->entries[dir].type != TYPE_DIR) return false; mkdir(host_path, 0777); for (i = 0; i < MAX_ENTRIES; i++) { Entry *e = &fs->entries[i]; if (e->type != TYPE_FREE && e->parent == (uint32_t)dir && i != dir) { char child_fs[MAX_PATH], child_host[MAX_PATH]; snprintf(child_fs, sizeof(child_fs), "%s/%s", fs_path, e->name); snprintf(child_host, sizeof(child_host), "%s/%s", host_path, e->name); if (e->type == TYPE_DIR) fs_export_dir(fs, child_fs, child_host); else fs_export_file(fs, child_fs, child_host); } } return true; } bool fs_compress_file(FileSystem *fs, const char *path) { int index = resolve_path(fs, path); Entry *e; unsigned char *logical, *compressed; uint32_t compressed_size; bool ok; if (index < 0 || fs->entries[index].type != TYPE_FILE) return false; e = &fs->entries[index]; logical = read_logical_bytes(fs, e); if (!logical) return false; compressed = rle_compress(logical, e->size, &compressed_size); if (!compressed || compressed_size >= e->size) { free(logical); free(compressed); return false; } e->compressed = 1; ok = write_stored_bytes(fs, e, compressed, compressed_size); e->compressed = ok ? 1 : 0; free(logical); free(compressed); fs_sync(fs); return ok; } bool fs_rawcat(FileSystem *fs, const char *path) { int index = resolve_path(fs, path); Entry *e; unsigned char *stored; uint32_t i; if (index < 0 || fs->entries[index].type != TYPE_FILE) return false; e = &fs->entries[index]; stored = read_stored_bytes(fs, e); if (!stored) return false; printf("logical size: %u bytes\n", e->size); printf("stored size: %u bytes\n", e->stored_size); printf("compression: %s\n", e->compressed ? "RLE" : "none"); printf("raw bytes:"); for (i = 0; i < e->stored_size; i++) printf(" %02X", stored[i]); printf("\n"); if (e->compressed) { printf("RLE meaning:\n"); for (i = 0; i + 1 < e->stored_size; i += 2) { unsigned char count = stored[i]; unsigned char value = stored[i + 1]; if (value >= 32 && value <= 126) printf(" %02X %02X -> repeat '%c' %u time(s)\n", count, value, value, count); else printf(" %02X %02X -> repeat byte 0x%02X %u time(s)\n", count, value, value, count); } } else { printf("meaning: bytes are stored directly, without compression\n"); } free(stored); return true; } bool fs_decompress_file(FileSystem *fs, const char *path) { int index = resolve_path(fs, path); Entry *e; unsigned char *logical; bool ok; if (index < 0 || fs->entries[index].type != TYPE_FILE) return false; e = &fs->entries[index]; if (!e->compressed) return false; logical = read_logical_bytes(fs, e); if (!logical) return false; ok = store_logical_bytes(fs, e, logical, e->size); free(logical); fs_sync(fs); return ok; } bool fs_defrag(FileSystem *fs) { unsigned char *copies[MAX_ENTRIES] = {0}; uint32_t stored_sizes[MAX_ENTRIES] = {0}; uint32_t i, c; for (i = 0; i < MAX_ENTRIES; i++) { if (fs->entries[i].type == TYPE_FILE && fs->entries[i].stored_size > 0) { copies[i] = read_stored_bytes(fs, &fs->entries[i]); if (!copies[i]) return false; stored_sizes[i] = fs->entries[i].stored_size; } } memset(fs->bitmap, 0, fs->sb.bitmap_size); for (c = 0; c < fs->sb.data_start_cluster; c++) bit_set(fs, c); for (i = 0; i < MAX_ENTRIES; i++) { if (fs->entries[i].type == TYPE_FILE) { fs->entries[i].cluster_count = 0; if (!write_stored_bytes(fs, &fs->entries[i], copies[i], stored_sizes[i])) return false; } free(copies[i]); } fs_sync(fs); return true; } void fs_info(FileSystem *fs) { uint32_t i, free_clusters = 0; for (i = 0; i < fs->sb.total_clusters; i++) if (!bit_is_set(fs, i)) free_clusters++; printf("image: %s\n", fs->image_path); printf("disk size: %u KiB\n", fs->sb.disk_size / 1024); printf("cluster size: %u bytes\n", fs->sb.cluster_size); printf("clusters: %u total, %u free, %u occupied\n", fs->sb.total_clusters, free_clusters, fs->sb.total_clusters - free_clusters); } static bool copy_file(const char *from, const char *to) { FILE *in = fopen(from, "rb"); FILE *out; unsigned char buffer[4096]; size_t n; if (!in) return false; out = fopen(to, "wb"); if (!out) { fclose(in); return false; } while ((n = fread(buffer, 1, sizeof(buffer), in)) > 0) fwrite(buffer, 1, n, out); fclose(in); fclose(out); return true; } static void print_help(void) { puts("Commands:"); puts(" format create a new carrier"); puts(" info | ls [path] | pwd | cd "); puts(" mkdir | rmdir "); puts(" touch | rm "); puts(" cp | mv "); puts(" write | cat | rawcat "); puts(" import | export "); puts(" importdir | exportdir "); puts(" savecarrier | loadcarrier "); puts(" compress | decomp | defrag | help | exit"); puts("Use Up/Down arrows to browse command history in an interactive terminal."); } static void redraw_line(const char *prompt, const char *line) { printf("\r%s%s\033[K", prompt, line); fflush(stdout); } static bool read_command(const char *prompt, char *line, size_t size) { static char history[MAX_HISTORY][MAX_LINE]; static int history_count = 0; int history_pos; size_t len = 0; struct termios old_mode, raw_mode; if (!isatty(STDIN_FILENO)) { printf("%s", prompt); fflush(stdout); if (!fgets(line, size, stdin)) return false; line[strcspn(line, "\n")] = '\0'; return true; } printf("%s", prompt); fflush(stdout); if (tcgetattr(STDIN_FILENO, &old_mode) != 0) return false; raw_mode = old_mode; raw_mode.c_lflag &= (tcflag_t)~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &raw_mode); line[0] = '\0'; history_pos = history_count; while (1) { int ch = getchar(); if (ch == EOF) { tcsetattr(STDIN_FILENO, TCSANOW, &old_mode); return false; } if (ch == '\n' || ch == '\r') { putchar('\n'); break; } else if ((ch == 127 || ch == 8) && len > 0) { line[--len] = '\0'; redraw_line(prompt, line); } else if (ch == 27) { int a = getchar(); int b = getchar(); if (a == '[' && b == 'A' && history_count > 0 && history_pos > 0) { history_pos--; safe_copy(line, history[history_pos], size); len = strlen(line); redraw_line(prompt, line); } else if (a == '[' && b == 'B') { if (history_pos < history_count - 1) { history_pos++; safe_copy(line, history[history_pos], size); } else { history_pos = history_count; line[0] = '\0'; } len = strlen(line); redraw_line(prompt, line); } } else if (ch >= 32 && ch <= 126 && len + 1 < size) { line[len++] = (char)ch; line[len] = '\0'; putchar(ch); fflush(stdout); } } tcsetattr(STDIN_FILENO, TCSANOW, &old_mode); if (line[0]) { if (history_count < MAX_HISTORY) { safe_copy(history[history_count++], line, MAX_LINE); } else { int i; for (i = 1; i < MAX_HISTORY; i++) safe_copy(history[i - 1], history[i], MAX_LINE); safe_copy(history[MAX_HISTORY - 1], line, MAX_LINE); } } return true; } int main(int argc, char **argv) { FileSystem fs; char line[1024]; if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; } memset(&fs, 0, sizeof(fs)); if (!fs_mount(&fs, argv[1])) printf("Carrier is not formatted yet. Use: format \n"); print_help(); while (1) { char *cmd, *a, *b; char cwd[MAX_PATH] = "/"; if (fs.mounted) fs_pwd(&fs, cwd, sizeof(cwd)); { char prompt[MAX_PATH + 3]; snprintf(prompt, sizeof(prompt), "%s> ", cwd); if (!read_command(prompt, line, sizeof(line))) break; } cmd = strtok(line, " "); if (!cmd) continue; a = strtok(NULL, " "); b = strtok(NULL, ""); if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "quit") == 0) break; if (strcmp(cmd, "help") == 0) print_help(); else if (strcmp(cmd, "format") == 0 && a) { if (fs.mounted) fs_unmount(&fs); printf(fs_format(&fs, argv[1], (uint32_t)atoi(a)) ? "formatted\n" : "format failed\n"); } else if (!fs.mounted) printf("No mounted filesystem. Format or load a carrier first.\n"); else if (strcmp(cmd, "info") == 0) fs_info(&fs); else if (strcmp(cmd, "ls") == 0) fs_list(&fs, a); else if (strcmp(cmd, "pwd") == 0) printf("%s\n", cwd); else if (strcmp(cmd, "cd") == 0 && a) printf(fs_cd(&fs, a) ? "ok\n" : "directory not found\n"); else if (strcmp(cmd, "mkdir") == 0 && a) printf(fs_create_dir(&fs, a) ? "ok\n" : "mkdir failed\n"); else if (strcmp(cmd, "rmdir") == 0 && a) printf(fs_remove_dir(&fs, a) ? "ok\n" : "rmdir failed\n"); else if (strcmp(cmd, "touch") == 0 && a) printf(fs_create_file(&fs, a) ? "ok\n" : "touch failed\n"); else if (strcmp(cmd, "rm") == 0 && a) printf(fs_remove(&fs, a) ? "ok\n" : "rm failed\n"); else if (strcmp(cmd, "cp") == 0 && a && b) printf(fs_copy_file(&fs, a, b) ? "ok\n" : "cp failed\n"); else if (strcmp(cmd, "mv") == 0 && a && b) printf(fs_move(&fs, a, b) ? "ok\n" : "mv failed\n"); else if (strcmp(cmd, "rename") == 0 && a && b) printf(fs_rename(&fs, a, b) ? "ok\n" : "rename failed\n"); else if (strcmp(cmd, "write") == 0 && a && b) printf(fs_write_text(&fs, a, b) ? "ok\n" : "write failed\n"); else if (strcmp(cmd, "cat") == 0 && a) printf(fs_print_file(&fs, a) ? "" : "cat failed\n"); else if (strcmp(cmd, "rawcat") == 0 && a) printf(fs_rawcat(&fs, a) ? "" : "rawcat failed\n"); else if (strcmp(cmd, "import") == 0 && a && b) printf(fs_import_file(&fs, a, b) ? "ok\n" : "import failed\n"); else if (strcmp(cmd, "export") == 0 && a && b) printf(fs_export_file(&fs, a, b) ? "ok\n" : "export failed\n"); else if (strcmp(cmd, "importdir") == 0 && a && b) printf(fs_import_dir(&fs, a, b) ? "ok\n" : "importdir failed\n"); else if (strcmp(cmd, "exportdir") == 0 && a && b) printf(fs_export_dir(&fs, a, b) ? "ok\n" : "exportdir failed\n"); else if (strcmp(cmd, "savecarrier") == 0 && a) { fs_sync(&fs); printf(copy_file(fs.image_path, a) ? "ok\n" : "copy failed\n"); } else if (strcmp(cmd, "loadcarrier") == 0 && a) { fs_unmount(&fs); printf(copy_file(a, argv[1]) && fs_mount(&fs, argv[1]) ? "ok\n" : "load failed\n"); } else if (strcmp(cmd, "compress") == 0 && a) printf(fs_compress_file(&fs, a) ? "ok\n" : "compression did not reduce file\n"); else if (strcmp(cmd, "decomp") == 0 && a) printf(fs_decompress_file(&fs, a) ? "ok\n" : "decomp failed\n"); else if (strcmp(cmd, "defrag") == 0) printf(fs_defrag(&fs) ? "ok\n" : "defrag failed\n"); else printf("Unknown or incomplete command. Type help.\n"); } if (fs.mounted) fs_unmount(&fs); return 0; }