From 3f605489ac150bbd8f7f610becd60e18a298d909 Mon Sep 17 00:00:00 2001 From: hasslesstech Date: Wed, 23 Apr 2025 22:51:00 +0300 Subject: [PATCH] [wip] initial commit --- Makefile | 17 ++++ config.h | 26 ++++++ inc/cli.h | 22 +++++ inc/color.h | 18 ++++ inc/ctrl.h | 8 ++ inc/fs.h | 40 +++++++++ inc/macro.h | 10 +++ inc/print.h | 66 ++++++++++++++ src/cli.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ctrl.c | 4 + src/fs.c | 195 ++++++++++++++++++++++++++++++++++++++++ src/main.c | 12 +++ 12 files changed, 668 insertions(+) create mode 100644 Makefile create mode 100644 config.h create mode 100644 inc/cli.h create mode 100644 inc/color.h create mode 100644 inc/ctrl.h create mode 100644 inc/fs.h create mode 100644 inc/macro.h create mode 100644 inc/print.h create mode 100644 src/cli.c create mode 100644 src/ctrl.c create mode 100644 src/fs.c create mode 100644 src/main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3cab772 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +CFLAGS = -g3 -O0 -I. -Iinc + +build: main + +main: out/main.o out/cli.o out/fs.o out/ctrl.o config.h + gcc out/*.o -o main + +out/%.o : src/%.c out/ config.h + gcc -c $(CFLAGS) -o "$@" "$<" + +out/: + mkdir -p out/ + +clean: + rm -r out/ main + +.PHONY : build clean diff --git a/config.h b/config.h new file mode 100644 index 0000000..3a1d245 --- /dev/null +++ b/config.h @@ -0,0 +1,26 @@ +#ifndef CONFIG_FILE +#define CONFIG_FILE + +/* Global config options */ +#define DEBUG 1 +#define LOG_LEVEL 4 +#define ENABLE_FILE_LINE_IN_OTHER_PR 1 + + +/* Output color config section */ +#define COLOR_ENABLE 1 + + +/* CLI config section */ +#define CLI_MAX_LINE_LENGTH 256 +#define CLI_MAX_ACCEPTED_ARGS 4 +#define CLI_MAX_TOKEN_LENGTH 64 + + +/* FS config section */ +#define FS_MAX_DEVICE_FILE_NAME_LEN 512 +#define FS_BLOCK_SIZE 4096 +#define FS_MAX_BITMAP_SIZE 256 + + +#endif diff --git a/inc/cli.h b/inc/cli.h new file mode 100644 index 0000000..e9a56d8 --- /dev/null +++ b/inc/cli.h @@ -0,0 +1,22 @@ +#ifndef INC_CLI_H +#define INC_CLI_H + + +enum CliArgType { + INT, + STR, + WRONG_TYPE +}; + +struct CliCommandEntry { + char *cmd; + unsigned int arg_count; + enum CliArgType *args; + int (*process)(void *); +}; + + +unsigned int cli_poll_process_next(void); + + +#endif diff --git a/inc/color.h b/inc/color.h new file mode 100644 index 0000000..8de8611 --- /dev/null +++ b/inc/color.h @@ -0,0 +1,18 @@ +#ifndef INC_COLOR_H +#define INC_COLOR_H + + +#include "config.h" + +#if COLOR_ENABLE == 1 + #define COLOR_RESET "\e[0m" + #define COLOR_RED "\e[0;31m" + #define COLOR_YELLOW "\e[0;33m" +#else + #define COLOR_RESET "" + #define COLOR_RED "" + #define COLOR_YELLOW "" +#endif + + +#endif diff --git a/inc/ctrl.h b/inc/ctrl.h new file mode 100644 index 0000000..118eaf9 --- /dev/null +++ b/inc/ctrl.h @@ -0,0 +1,8 @@ +#ifndef INC_CTRL_H +#define INC_CTRL_H + + +int ctrl_exit(void *d); + + +#endif diff --git a/inc/fs.h b/inc/fs.h new file mode 100644 index 0000000..86cf1a7 --- /dev/null +++ b/inc/fs.h @@ -0,0 +1,40 @@ +#include "config.h" + +enum fs_filetype { + REGULAR, + DIRECTORY +}; + +__attribute__((packed)) +struct fs_header { + unsigned char version; + unsigned int max_inode_count:24; + unsigned int block_count; + unsigned int next_extension; + unsigned int inode_ptrs[(FS_BLOCK_SIZE-sizeof(int)*3) / sizeof(int)]; +}; + +struct fs_header_extension { + unsigned int next_extension; + unsigned int inode_ptrs[(FS_BLOCK_SIZE-sizeof(int)) / sizeof(int)]; +}; + +__attribute__((packed)) +struct fs_inode { + unsigned int ftype:8; + unsigned int ref_count:24; + unsigned int size; + unsigned int next_extension; + unsigned int blocks[(FS_BLOCK_SIZE-sizeof(int)*3) / sizeof(int)]; +}; + +struct fs_inode_extension { + unsigned int next_extension; + unsigned int blocks[(FS_BLOCK_SIZE-sizeof(int)) / sizeof(int)]; +}; + +int fs_create(void *d); +int fs_use(void *d); +int fs_mkfs(void *d); +int fs_allow_write(void *d); +int fs_prohibit_write(void *d); diff --git a/inc/macro.h b/inc/macro.h new file mode 100644 index 0000000..d6fe389 --- /dev/null +++ b/inc/macro.h @@ -0,0 +1,10 @@ +#ifndef INC_MACRO_H +#define INC_MACRO_H + + +#define LEN(x) ( sizeof(x) / sizeof((x)[0]) ) + +#define FOR(i, max) for (unsigned int (i) = 0; (i) < (max); (i)++) + + +#endif diff --git a/inc/print.h b/inc/print.h new file mode 100644 index 0000000..7df0b13 --- /dev/null +++ b/inc/print.h @@ -0,0 +1,66 @@ +#ifndef INC_PRINT_H +#define INC_PRINT_H + + +#include +#include "config.h" +#include "color.h" + +#if DEBUG == 1 + #define pr(...) { printf("[%s:%d] ", __FILE__, __LINE__); printf(__VA_ARGS__); } +#else + #define pr(...) {} +#endif + + +#if LOG_LEVEL >= 2 + #if ENABLE_FILE_LINE_IN_OTHER_PR == 1 + #define pr_err(...) { \ + printf(COLOR_RED "[%s:%d] Error: ", __FILE__, __LINE__); \ + printf(__VA_ARGS__); \ + printf(COLOR_RESET); \ + } + #else + #define pr_err(...) { \ + printf(COLOR_RED "Error: "); \ + printf(__VA_ARGS__); \ + printf(COLOR_RESET);\ + } + #endif +#else + #define pr_err(...) {} +#endif + +#if LOG_LEVEL >= 3 + #if ENABLE_FILE_LINE_IN_OTHER_PR == 1 + #define pr_warn(...) { \ + printf(COLOR_YELLOW "[%s:%d] Warning: ", __FILE__, __LINE__); \ + printf(__VA_ARGS__); \ + printf(COLOR_RESET); \ + } + #else + #define pr_warn(...) { \ + printf(COLOR_YELLOW "Warning: "); \ + printf(__VA_ARGS__); \ + printf(COLOR_RESET); \ + } + #endif +#else + #define pr_warn(...) {} +#endif + +#if LOG_LEVEL >= 4 + #if ENABLE_FILE_LINE_IN_OTHER_PR == 1 + #define pr_info(...) { \ + printf("[%s:%d] Info: ", __FILE__, __LINE__); \ + printf(__VA_ARGS__); \ + } + #else + #define pr_info(...) { printf("Info: "); printf(__VA_ARGS__); } + #endif +#else + #define pr_info(...) {} +#endif + + +#endif diff --git a/src/cli.c b/src/cli.c new file mode 100644 index 0000000..1820948 --- /dev/null +++ b/src/cli.c @@ -0,0 +1,250 @@ +#include "macro.h" +#include "config.h" + +#include "cli.h" +#include "print.h" + +#include "fs.h" +#include "ctrl.h" + +#include +#include + + +static const struct CliCommandEntry cmd[] = { + // mandatory commands + {"mkfs", 1, (enum CliArgType[]) {INT}, fs_mkfs}, + {"create", 1, (enum CliArgType[]) {STR}, fs_create}, + + // custom commands + {"use", 1, (enum CliArgType[]) {STR}, fs_use}, + {"prohibit-write", 0, NULL, fs_prohibit_write}, + {"allow-write", 0, NULL, fs_allow_write}, + {"exit", 0, NULL, ctrl_exit} +}; + + +static void destroy_tokenized_line(char **tokenized_line) +{ + for (int j = 0; j < CLI_MAX_ACCEPTED_ARGS; j++) + if (tokenized_line[j]) + free(tokenized_line[j]); + + free(tokenized_line); +} + +static int tokenized_line_len(char **t) +{ + int i; + + for (i = 0; i < CLI_MAX_ACCEPTED_ARGS; i++) + if (!t[i]) + break; + + return i; +} + +static char **tokenize_line(char *line, ssize_t result) +{ + if (result > CLI_MAX_LINE_LENGTH) { + pr_err("command too long (%d > %d)\n", result+1, CLI_MAX_LINE_LENGTH); + return NULL; + } + + char **tokenized_line = malloc(sizeof(char *) * CLI_MAX_ACCEPTED_ARGS); + memset(tokenized_line, 0, sizeof(char *) * CLI_MAX_ACCEPTED_ARGS); + + unsigned int i, t, l; + for ( + i = 0, t = 0, l = 0; + i < CLI_MAX_LINE_LENGTH, + t <= CLI_MAX_ACCEPTED_ARGS, + l < CLI_MAX_TOKEN_LENGTH; + i++ + ) { + if (line[i] == '\n') { + if (l) { + if (t == CLI_MAX_ACCEPTED_ARGS) { + t++; + break; + } + + tokenized_line[t] = malloc(sizeof(char) * (l+1)); + memcpy(tokenized_line[t], &(line[i-l]), l); + tokenized_line[t][l] = '\0'; + + t++; + } + + break; + } else if (line[i] == ' ') { + if (l) { + if (t == CLI_MAX_ACCEPTED_ARGS) { + t++; + break; + } + + tokenized_line[t] = malloc(sizeof(char) * (l+1)); + memcpy(tokenized_line[t], &(line[i-l]), l); + tokenized_line[t][l] = '\0'; + + l = 0; + t++; + } + } else { + l++; + } + } + + if ( + i >= CLI_MAX_LINE_LENGTH || + t > CLI_MAX_ACCEPTED_ARGS || + l >= CLI_MAX_TOKEN_LENGTH || + t == 0 + ) goto cli_process_error; + + return tokenized_line; + + +cli_process_error: + // print error + if (i >= CLI_MAX_LINE_LENGTH) + pr_err("command too long (> %d)\n", CLI_MAX_LINE_LENGTH); + + if (t > CLI_MAX_ACCEPTED_ARGS) + pr_err("too many arguments (> %d)\n", CLI_MAX_ACCEPTED_ARGS); + + if (l >= CLI_MAX_TOKEN_LENGTH) + pr_err("argument %d too long (> %d)\n", t, CLI_MAX_TOKEN_LENGTH); + + // clean up + destroy_tokenized_line(tokenized_line); + + // return + return NULL; +} + + +void *format_args(char **tokenized_line, unsigned int arg_count, enum CliArgType *arg_types) +{ + char *new_struct; + + { + // prepare struct space + unsigned int struct_size = 0; + + for (unsigned int i = 0; i < arg_count; i++) + switch (arg_types[i]) { + case INT: + struct_size += sizeof(int); + break; + case STR: + struct_size += sizeof(char *); + break; + } + + new_struct = malloc(struct_size); + memset(new_struct, 0, struct_size); + } + + unsigned int struct_offset = 0; + + for (unsigned int i = 0; i < arg_count; i++) + { + if (arg_types[i] == INT) { + if (!tokenized_line[i+1]) { + pr_err("missing argument %d of type 'integer'\n", i+1); + goto abandon_struct; + } + + int value; + int result = sscanf(tokenized_line[i+1], "%d", &value); + + if (result != 1) { + pr_err("argument %d must be of type 'integer'\n", i+1); + goto abandon_struct; + } + + *((int*) &(new_struct[struct_offset])) = value; + struct_offset += sizeof(int); + } + else if (arg_types[i] == STR) { + if (!tokenized_line[i+1]) { + pr_err("missing argument %d of type 'string'\n", i+1); + goto abandon_struct; + } + + *((char**) &(new_struct[struct_offset])) = tokenized_line[i+1]; + struct_offset += sizeof(char *); + } + } + + return new_struct; + +abandon_struct: + free(new_struct); + return NULL; +} + + +unsigned int cli_poll_process_next(void) +{ + for ( ;; ) { + fputs("$ ", stdout); + + size_t buf_size = 0; + char *line = NULL; + + ssize_t result = getline(&line, &buf_size, stdin); + if (result == -1) + return 0x1; + + char **tokenized_line = tokenize_line(line, result); + free(line); + + if (tokenized_line == NULL) + continue; + + unsigned int i; + for (i = 0; (i < LEN(cmd)) && strcmp(tokenized_line[0], cmd[i].cmd); i++); + if (i == LEN(cmd)) { + pr_err("command '%s' not found\n", tokenized_line[0]); + destroy_tokenized_line(tokenized_line); + continue; + } + + { + int tll = tokenized_line_len(tokenized_line); + + if ((cmd[i].arg_count + 1) != tll) { + pr_err("wrong amount of arguments for '%s' (%d required, %d got)\n", tokenized_line[0], cmd[i].arg_count, tll-1); + destroy_tokenized_line(tokenized_line); + continue; + } + } + + int (*executor)(void *) = cmd[i].process; + void *data; + + if (cmd[i].arg_count) { + data = format_args(tokenized_line, cmd[i].arg_count, cmd[i].args); + + if (data == NULL) { + destroy_tokenized_line(tokenized_line); + continue; + } + } else { + data = NULL; + } + + int execution_result_code = executor(data); + + if (data) + free(data); + + destroy_tokenized_line(tokenized_line); + + if (0x1 == execution_result_code) + return 0x1; + } +} diff --git a/src/ctrl.c b/src/ctrl.c new file mode 100644 index 0000000..a902a13 --- /dev/null +++ b/src/ctrl.c @@ -0,0 +1,4 @@ +int ctrl_exit(void *d) +{ + return 1; +} diff --git a/src/fs.c b/src/fs.c new file mode 100644 index 0000000..b7b428a --- /dev/null +++ b/src/fs.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include + +#include "print.h" +#include "fs.h" + +#include "config.h" + +const static int BLOCK_ADDRESSES_PER_INODE = (FS_BLOCK_SIZE-sizeof(int)*4) / sizeof(int); +const static int BLOCK_ADDRESSES_PER_INODE_EXTENSION = (FS_BLOCK_SIZE-sizeof(int)) / sizeof(int); + + +static char used_file_path[FS_MAX_DEVICE_FILE_NAME_LEN+1]; +static int used_file_fd; +static int write_permitted; + + +static int read_block(unsigned int block_no, void *data) +{ + if (lseek(used_file_fd, block_no * FS_BLOCK_SIZE, SEEK_SET) < 0) { + pr_err("failed to seek to block %d (bs=%d) on device '%s'\n", block_no, FS_BLOCK_SIZE, used_file_path); + return -1; + } + + return read(used_file_fd, data, FS_BLOCK_SIZE); +} + +static int write_block(unsigned int block_no, void *data) +{ + if (lseek(used_file_fd, block_no * FS_BLOCK_SIZE, SEEK_SET) < 0) { + pr_err("failed to seek to block %d (bs=%d) on device '%s'\n", block_no, FS_BLOCK_SIZE, used_file_path); + return -1; + } + + return write(used_file_fd, data, FS_BLOCK_SIZE); +} + + +static int identify_fs(void) +{ + if (lseek(used_file_fd, 0, SEEK_SET) < 0) { + pr_err("failed to seek '%s' to 0\n", used_file_path); + return 0; + } + + unsigned char read_buf[FS_BLOCK_SIZE]; + { + int read_amount = read(used_file_fd, read_buf, FS_BLOCK_SIZE); + + if (read_amount < 0) { + pr_err("failed to read from storage device\n"); + return 0; + } + + if (read_amount < FS_BLOCK_SIZE) + pr_warn("failed to read full first block of %d bytes\n", FS_BLOCK_SIZE); + + if (read_amount == 0) { + pr_err("storage device size is 0\n"); + return 0; + } + } + + return read_buf[0]; +} + +int fs_allow_write(void *d) +{ + if (used_file_fd <= 0) { + pr_err("no device present\n"); + return 0; + } + + pr_info("Allowing write operations on device '%s'\n", used_file_path); + write_permitted = 1; +} + +int fs_prohibit_write(void *d) +{ + if (used_file_fd <= 0) { + pr_err("no device present\n"); + return 0; + } + + pr_info("Prohibiting write operations on device '%s'\n", used_file_path); + write_permitted = 0; +} + +int fs_create(void *d) +{ + pr("[mock] Regular file '%s' created\n", *((char **) d)); + + return 0; +} + +int fs_use(void *d) +{ + char *fname = *((char **) d); + int name_len = strlen(fname); + + if (name_len > FS_MAX_DEVICE_FILE_NAME_LEN) { + pr_err("device filename too long (> %d)\n", FS_MAX_DEVICE_FILE_NAME_LEN); + return 0; + } + + pr("Using file '%s' as storage device\n", fname); + + strcpy(used_file_path, fname); + + if (used_file_fd > 0) + close(used_file_fd); + + used_file_fd = open(fname, O_RDWR); + + if (used_file_fd < 0) { + pr_err("failed to open filename '%s'\n", fname); + return 0; + } + + int fs_version = identify_fs(); + if (!fs_version) { + pr_info("filesystem could not be identified on device '%s'\n", fname); + write_permitted = 0; + } else if (fs_version != 1) { + pr_warn("filesystem is corrupted or has unsupported version (0x%hhx)\n", fs_version); + write_permitted = 0; + } else if (fs_version == 1) { + pr_info("filesystem v1 has been identified on device '%s'\n", fname); + write_permitted = 1; + } + + return 0; +} + +int fs_mkfs(void *d) +{ + if (!write_permitted) { + pr_err("device '%s' is write-protected\n", used_file_path); + return 0; + } + + if (used_file_fd <= 0) { + pr_err("storage device not present\n"); + return 0; + } + + int max_inode_count = *((int *)d); + + struct stat st; + if (fstat(used_file_fd, &st) < 0) { + pr_err("could not stat device '%s'\n", used_file_path); + return 0; + } + + int block_count = st.st_size / FS_BLOCK_SIZE; + int blocks_used_for_bitmap = block_count / (FS_BLOCK_SIZE * 8); + if (block_count % (FS_BLOCK_SIZE * 8)) + blocks_used_for_bitmap++; + + if (blocks_used_for_bitmap > FS_MAX_BITMAP_SIZE) { + pr_err("memory bitmap is too large (%d blocks > %d)\n", blocks_used_for_bitmap, FS_MAX_BITMAP_SIZE); + return 0; + } + + pr("Formatting storage device '%s' of size %d (total_block_count = %d, bitmap_blocks = %d) with %d allowed inode pointers\n", used_file_path, st.st_size, block_count, blocks_used_for_bitmap, max_inode_count); + + struct fs_header fsh = {}; + fsh.version = 0x1; + fsh.max_inode_count = max_inode_count; + fsh.block_count = block_count; + + pr("header size is %d bytes, writing it to the first block\n", sizeof(fsh)); + + int result = write_block(0, (void *) &fsh); + if (FS_BLOCK_SIZE != result) { + if (result < 0) { + pr_err("failed to write fs header to device '%s'\n", used_file_path); + } else { + pr_err("failed to write full block to device, written %d/%d bytes\n", result, FS_BLOCK_SIZE); + } + + return 0; + } + + unsigned char bitmap[blocks_used_for_bitmap * FS_BLOCK_SIZE]; + memset(bitmap, 0, blocks_used_for_bitmap * FS_BLOCK_SIZE); + + int blocks_used = 1 + blocks_used_for_bitmap; + + //for (int i = 0; i < FS_MAX_BITMAP_SIZE; i++) + + return 0; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7ee4a99 --- /dev/null +++ b/src/main.c @@ -0,0 +1,12 @@ +#include "print.h" +#include "cli.h" + +int +main(void) +{ + for (;;) + if (0x1 == cli_poll_process_next()) + break; + + return 0; +}