[wip] initial commit

This commit is contained in:
ІО-23 Шмуляр Олег 2025-04-23 22:51:00 +03:00
commit 3f605489ac
12 changed files with 668 additions and 0 deletions

17
Makefile Normal file
View File

@ -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

26
config.h Normal file
View File

@ -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

22
inc/cli.h Normal file
View File

@ -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

18
inc/color.h Normal file
View File

@ -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

8
inc/ctrl.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef INC_CTRL_H
#define INC_CTRL_H
int ctrl_exit(void *d);
#endif

40
inc/fs.h Normal file
View File

@ -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);

10
inc/macro.h Normal file
View File

@ -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

66
inc/print.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef INC_PRINT_H
#define INC_PRINT_H
#include <stdio.h>
#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

250
src/cli.c Normal file
View File

@ -0,0 +1,250 @@
#include "macro.h"
#include "config.h"
#include "cli.h"
#include "print.h"
#include "fs.h"
#include "ctrl.h"
#include <stdlib.h>
#include <string.h>
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;
}
}

4
src/ctrl.c Normal file
View File

@ -0,0 +1,4 @@
int ctrl_exit(void *d)
{
return 1;
}

195
src/fs.c Normal file
View File

@ -0,0 +1,195 @@
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#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;
}

12
src/main.c Normal file
View File

@ -0,0 +1,12 @@
#include "print.h"
#include "cli.h"
int
main(void)
{
for (;;)
if (0x1 == cli_poll_process_next())
break;
return 0;
}