From e5a26d66b927f32f6fc0da409245152138c37ea7 Mon Sep 17 00:00:00 2001 From: pajjilykk Date: Mon, 18 May 2026 17:22:21 +0700 Subject: [PATCH] cleanup --- rgz_vlasd/Makefile | 18 -- rgz_vlasd/TESTING.md | 84 ----- rgz_vlasd/report.md | 204 ------------ rgz_vlasd/simplefs.c | 715 ------------------------------------------- 4 files changed, 1021 deletions(-) delete mode 100644 rgz_vlasd/Makefile delete mode 100644 rgz_vlasd/TESTING.md delete mode 100644 rgz_vlasd/report.md delete mode 100644 rgz_vlasd/simplefs.c diff --git a/rgz_vlasd/Makefile b/rgz_vlasd/Makefile deleted file mode 100644 index 205e502..0000000 --- a/rgz_vlasd/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -CC = gcc -CFLAGS = -std=c11 -Wall -Wextra -pedantic -TARGET = simplefs -SOURCE = simplefs.c -DISK = disk.img - -.PHONY: all run clean - -all: $(TARGET) - -$(TARGET): $(SOURCE) - $(CC) $(CFLAGS) $(SOURCE) -o $(TARGET) - -run: $(TARGET) - ./$(TARGET) $(DISK) - -clean: - rm -f $(TARGET) $(DISK) diff --git a/rgz_vlasd/TESTING.md b/rgz_vlasd/TESTING.md deleted file mode 100644 index 877e327..0000000 --- a/rgz_vlasd/TESTING.md +++ /dev/null @@ -1,84 +0,0 @@ -# Brief testing guide - -## 1. Build and start - -```bash -make -make run -``` - -`make run` starts the program with `disk.img` as the virtual carrier file. - -## 2. Basic filesystem scenario - -Enter these commands inside the program: - -```text -format 512 -mkdir /docs -touch /docs/test.txt -write /docs/test.txt hello filesystem -ls /docs -cat /docs/test.txt -rename /docs/test.txt renamed.txt -cat /docs/renamed.txt -info -``` - -Expected result: - -- formatting succeeds; -- `/docs` contains one file; -- `cat` prints `hello filesystem`; -- after renaming, the file is available as `renamed.txt`; -- `info` shows a 512 KiB carrier and occupied clusters. - -## 3. Compression and defragmentation - -```text -touch /docs/repeat.txt -write /docs/repeat.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -compress /docs/repeat.txt -ls /docs -defrag -cat /docs/repeat.txt -``` - -Expected result: - -- `compress` succeeds for repeated text; -- in `ls`, `stored` is smaller than `size` for `repeat.txt`; -- after `defrag`, the file still reads correctly. - -## 4. Import and export - -Before starting the program, create a host file: - -```bash -echo "outside file" > host.txt -``` - -Then inside the program: - -```text -import host.txt /docs/imported.txt -cat /docs/imported.txt -export /docs/imported.txt exported.txt -``` - -After leaving the program, verify: - -```bash -cat exported.txt -``` - -Expected result: `exported.txt` contains `outside file`. - -## 5. Cleanup - -```bash -make clean -rm -f host.txt exported.txt -``` - -`make clean` removes the executable and the default virtual disk image. diff --git a/rgz_vlasd/report.md b/rgz_vlasd/report.md deleted file mode 100644 index 5c5c83a..0000000 --- a/rgz_vlasd/report.md +++ /dev/null @@ -1,204 +0,0 @@ -# Отчет по дисциплине «Операционные системы» - -## РГР «Разработка простой файловой системы» - -**Выполнил:** ____________________ -**Группа:** ____________________ -**Проверил:** ____________________ -**Новосибирск, 2026** - -## Оглавление - -1. [Введение](#введение) -2. [Постановка задачи](#постановка-задачи) -3. [Выбранная архитектура файловой системы](#выбранная-архитектура-файловой-системы) -4. [Структура носителя и основные данные](#структура-носителя-и-основные-данные) -5. [Реализованные операции](#реализованные-операции) -6. [Алгоритмы работы](#алгоритмы-работы) -7. [Тестирование](#тестирование) -8. [Ограничения реализации](#ограничения-реализации) -9. [Вывод](#вывод) - -## Введение - -Цель работы — разработать учебную файловую систему на языке C, использующую обычный бинарный файл как виртуальный носитель произвольного доступа. Программа должна не только хранить файлы и каталоги, но и показывать основные принципы настоящих файловых систем: разметку носителя, учет свободного места, хранение метаданных, работу с путями и прикладные операции обслуживания. - -В качестве ориентира использовался предоставленный пример реализации, однако приоритет был отдан требованиям задания. Поэтому итоговая программа сохраняет простую учебную архитектуру примера, но дополнительно реализует операции `open`, `close`, `seek`, `read`, `write`, `rename`, импорт и экспорт, дефрагментацию и сжатие. - -## Постановка задачи - -Согласно заданию необходимо разработать систему управления файлами, где разделом является бинарный файл произвольного доступа. Реализация должна включать: - -- физический уровень хранения данных; -- структуру учета свободных блоков; -- базовый уровень с индексными метаданными; -- размещение содержимого файлов; -- систему каталогов и путей; -- символьные операции над файлами и каталогами; -- прикладную утилиту для форматирования, просмотра, импорта, экспорта, дефрагментации и сжатия. - -## Выбранная архитектура файловой системы - -| Уровень | Реализация | -| --- | --- | -| Физический уровень | Кластеры фиксированного размера по 1024 байта | -| Учет свободного места | Битовая карта | -| Базовый уровень | Индексный файл с дескрипторами объектов | -| Размещение файлов | Список номеров кластеров в дескрипторе | -| Каталоги | Неупорядоченный список имен с линейным поиском | -| Прикладной уровень | Интерактивная консольная утилита | - -Выбранная схема хорошо подходит для учебной реализации: она достаточно близка к реальным файловым системам, но не требует сложных деревьев, журналирования и динамических таблиц. - -```mermaid -flowchart LR - A[Файл-носитель] --> B[Суперблок] - A --> C[Битовая карта] - A --> D[Индексный файл] - A --> E[Область данных] - D --> F[Файлы] - D --> G[Каталоги] - F --> E -``` - -## Структура носителя и основные данные - -В начале виртуального диска находится суперблок. Он хранит сигнатуру файловой системы, размер диска, размер кластера, смещения служебных областей и индекс корневого каталога. - -```c -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; -``` - -После суперблока располагается битовая карта. Каждый ее бит соответствует одному кластеру: `0` означает свободный кластер, `1` — занятый. Далее находится индексный файл, представленный массивом дескрипторов `Entry`. - -```c -typedef struct { - char name[32]; - uint8_t type; - uint8_t compressed; - uint32_t parent; - uint32_t size; - uint32_t stored_size; - uint32_t cluster_count; - uint32_t clusters[128]; -} Entry; -``` - -Поле `size` хранит логический размер файла, видимый пользователю, а `stored_size` — фактическое количество байт на носителе. Это различие необходимо для поддержки сжатых файлов. - -```mermaid -flowchart TB - S[Суперблок] --> B[Битовая карта] - B --> I[Индексный файл] - I --> D[Область данных] - I --> R[Корневой каталог /] - R --> F[Дескрипторы файлов и каталогов] - F --> D -``` - -## Реализованные операции - -Программа собирается из файла `simplefs.c` и запускается с именем файла-носителя: - -```bash -gcc -std=c11 -Wall -Wextra -pedantic simplefs.c -o simplefs -./simplefs disk.img -``` - -Поддерживаются следующие команды: - -| Команда | Назначение | -| --- | --- | -| `format ` | создание новой файловой структуры | -| `info` | сведения о носителе и свободном месте | -| `ls [path]`, `pwd`, `cd ` | просмотр и навигация по каталогам | -| `mkdir`, `rmdir` | создание и удаление каталогов | -| `touch`, `rm`, `rename` | операции над файлами | -| `write`, `cat` | запись и чтение текстовых данных | -| `import`, `export` | импорт и экспорт отдельных файлов | -| `importdir`, `exportdir` | импорт и экспорт папок | -| `savecarrier`, `loadcarrier` | экспорт и импорт всего носителя | -| `compress` | сжатие файла методом RLE | -| `defrag` | упорядочивание занятых кластеров | - -На уровне программного интерфейса реализованы функции `fs_open`, `fs_close`, `fs_seek`, `fs_read` и `fs_write`. Открытый файл использует буфер в памяти, поэтому чтение и запись записей произвольной длины выполняются без прямой работы пользователя с кластерами. - -## Алгоритмы работы - -### Форматирование - -При выполнении `format` создается бинарный файл нужного размера, вычисляются размеры служебных областей, резервируются занятые кластеры и создается корневой каталог `/`. - -### Выделение свободного кластера - -Алгоритм линейно просматривает битовую карту, начиная с области данных. Первый свободный бит помечается как занятый, а номер соответствующего кластера возвращается вызывающей функции. - -### Работа с каталогами - -Каталог не хранит отдельный массив дочерних элементов. Вместо этого у каждого дескриптора есть поле `parent`, содержащее индекс родительского каталога. Для поиска элемента программа просматривает индексный файл и сравнивает имя и родителя. - -### Сжатие - -Команда `compress` использует простой алгоритм RLE: одинаковые подряд идущие байты заменяются парой «количество + значение». Если сжатый вариант не меньше исходного, файл не изменяется. При чтении сжатый файл автоматически восстанавливается в обычный вид. - -### Дефрагментация - -Команда `defrag` временно считывает содержимое файлов, очищает карту размещения области данных и записывает файлы заново подряд. После операции используемые кластеры становятся компактно расположенными. - -## Тестирование - -Пример базового сценария: - -```text -format 512 -mkdir /docs -touch /docs/a.txt -write /docs/a.txt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -cat /docs/a.txt -compress /docs/a.txt -ls /docs -rename /docs/a.txt b.txt -export /docs/b.txt exported.txt -defrag -info -``` - -Ожидаемые результаты: - -| Проверка | Результат | -| --- | --- | -| Форматирование носителя | создается корректный виртуальный диск | -| Создание каталога и файла | объекты появляются в `ls` | -| Запись и чтение | `cat` выводит ранее записанный текст | -| Сжатие повторяющихся данных | `stored_size` становится меньше `size` | -| Переименование | файл доступен под новым именем | -| Экспорт | создается внешний файл с тем же содержимым | -| Дефрагментация | содержимое файлов сохраняется, кластеры уплотняются | - -## Ограничения реализации - -| Ограничение | Причина | -| --- | --- | -| Не более 128 объектов | фиксированный размер индексного файла | -| Не более 128 кластеров на файл | фиксированный массив номеров кластеров | -| Длина имени до 31 символа | размер поля `name[32]` | -| Линейный поиск по каталогу | выбрана простая неупорядоченная структура | -| RLE эффективно не для всех данных | алгоритм выбран ради понятности реализации | -| Нет одновременного доступа нескольких процессов | учебная однопользовательская модель | - -## Вывод - -В ходе работы была реализована простая файловая система, работающая поверх бинарного файла как виртуального носителя. Реализация охватывает все основные уровни, указанные в задании: физическое размещение, учет свободных блоков, индексные метаданные, каталоги, операции над файлами и сервисные команды управления носителем. - -Полученная программа остается достаточно простой для изучения начинающим разработчиком на C, но при этом показывает полный путь данных: от пользовательской команды до изменения битовой карты, индексного дескриптора и байтов в кластерах виртуального диска. diff --git a/rgz_vlasd/simplefs.c b/rgz_vlasd/simplefs.c deleted file mode 100644 index dca7d12..0000000 --- a/rgz_vlasd/simplefs.c +++ /dev/null @@ -1,715 +0,0 @@ -#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 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 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_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]; - snprintf(next, sizeof(next), "/%s%s", fs->entries[cur].name, temp); - 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_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; - snprintf(host_child, sizeof(host_child), "%s/%s", host_path, item->d_name); - snprintf(fs_child, sizeof(fs_child), "%s/%s", fs_path, item->d_name); - 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_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 | rename "); - puts(" write | cat "); - puts(" import | export "); - puts(" importdir | exportdir "); - puts(" savecarrier | loadcarrier "); - puts(" compress | defrag | help | exit"); -} - -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)); - printf("%s> ", cwd); - if (!fgets(line, sizeof(line), stdin)) break; - line[strcspn(line, "\n")] = '\0'; - cmd = strtok(line, " "); - if (!cmd) continue; - a = strtok(NULL, " "); - b = strtok(NULL, ""); - if (strcmp(cmd, "exit") == 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, "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, "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, "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; -}