From e5d9d51870a677cda4b9034c612a0e6759b18e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rn=20Guldberg?= Date: Thu, 6 Jun 2019 22:40:35 +0200 Subject: [PATCH] Major update, both design and implementation --- lsfs_disk_controller.h | 404 ++++++++++++++--------------------------- lsfs_fuse | Bin 36640 -> 38992 bytes lsfs_fuse.c | 154 +++------------- lsfs_fuse.o | Bin 31504 -> 36376 bytes lsfs_string.h | 152 ++++++++++++++++ 5 files changed, 310 insertions(+), 400 deletions(-) create mode 100644 lsfs_string.h diff --git a/lsfs_disk_controller.h b/lsfs_disk_controller.h index 903b51c..aea2f8d 100644 --- a/lsfs_disk_controller.h +++ b/lsfs_disk_controller.h @@ -10,6 +10,8 @@ #include #include +#include "lsfs_string.h" + typedef struct Directory_Table Directory_Table; typedef struct struct_table_entry struct_table_entry; typedef struct struct_partition_control partition_control; @@ -18,6 +20,7 @@ typedef struct File_System_Control_Information FSCI; typedef struct meta_information_format mif; typedef struct tag_record tag_record; typedef struct lsfs_file lsfs_file; +typedef enum Table_Entry_Kind Table_Entry_Kind; typedef uint64_t lsfs_sector_offset; typedef lsfs_sector_offset lsfs_file_id; @@ -27,17 +30,13 @@ static FILE* disk; static partition_control p_control; int create_file_system(); -//lsfs_sector_offset lsfs_disk_create_tag(char* tag_name, bool is_filename); -lsfs_sector_offset lsfs_disk_create_file(char* filename, lsfs_file_id* tags, void* file_data); +int lsfs_disk_create_entry(const char* path, Table_Entry_Kind entry_kind); int lsfs_disk_getattr(lsfs_file* find_file, const char *path); -//int lsfs_disk_untag_file(lsfs_file_id file_id, lsfs_file_id file_id); -//int lsfs_disk_tag_file(lsfs_file_id file_id, lsfs_file_id file_id); -//int lsfs_disk_delete_tag(lsfs_file_id file_id); int lsfs_disk_delete_file(lsfs_file_id file_id); +int get_free_sectors_table(); int get_free_sectors(int num_sectors_needed, lsfs_sector_offset* output_array); int lsfs_disk_read_data_from_file(lsfs_file* file, int buffer_size, void* buffer_for_data); int lsfs_disk_write_data_to_file(lsfs_file* file, int data_length, char *data); -//int lsfs_disk_rename_tag(lsfs_file_id file_id, char* new_filename); int lsfs_disk_rename_file(lsfs_file* file, const char* new_filename); int lsfs_disk_load_disk(); int write_data_to_disk(lsfs_sector_offset at_sector, uint32_t file_block_size, void* data_to_write); @@ -45,20 +44,19 @@ int read_data_from_disk(lsfs_sector_offset index, uint32_t file_block_size, void int write_data_to_disk_off(lsfs_sector_offset index, void* data_to_write, int offset); int save_modified_file_information(lsfs_file* file); - #define SECTOR_SIZE 512 #define NUMBER_OF_MBR_PARTITIONS 4 #define DEFAULT_FILE_SIZE 4 // This is in sectors -#define DEFAULT_MASTER_TABLE_SIZE 64 -#define DEFAULT_TAG_TABLE_SIZE 64 // correspond to 262144 bytes in sectors - 16384 files pr. default. (minus one, the last is a pointer to a table more ) -#define MAX_MASTER_TAGS 57825 -#define MAX_TAGS_IN_TAG_TABLE 16384 -#define MAX_TAGS_FOR_A_FILE 32 +#define DEFAULT_TABLE_SIZE 10 #define NUM_DATA_POINTERS 28 -//#define MAX_NUM_TWO_LEVEL_DATA 94 // First Mib of a file. -//#define MAX_NUM_THREE_LEVEL_DATA 94 // First Mib of a file. - +typedef enum Table_Entry_Kind +{ + // These are specific values since, is has to corrospond to the implementation in assembly + ENTRY_EMPTY = 0, + ENTRY_FILE = 1, + ENTRY_DIRECTORY = 2, +} Table_Entry_Kind; typedef struct Partition_Entry { @@ -101,13 +99,15 @@ typedef struct Directory_Table typedef struct File_System_Control_Information { char filesyste_information[256]; - uint64_t offset_on_disk; + uint64_t master_table_index; + uint64_t this_partition_offset_on_disk; uint64_t next_free_sector; + uint64_t next_uniqe_id; // both files and directories gets this. uint64_t next_sector_reuse_pointer; uint64_t last_sector_index_on_partition; uint64_t maximum_sectors_on_disk; uint64_t sectors_size_on_disk; - uint64_t not_used[26]; + uint64_t not_used[24]; } __attribute__((packed)) FSCI; @@ -120,15 +120,15 @@ typedef struct struct_partition_control typedef struct meta_information_format { - char filename[246]; // remeber that the 246 bytes has to be a /0 terminator.. - uint32_t owner_id; - lsfs_file_id tags[32]; - uint64_t file_size; - uint32_t control_bits; + char filename[246]; // remeber that the 246 bytes has to be a /0 terminator.. + uint32_t owner_id; + lsfs_file_id tags[32]; + uint64_t file_size; + uint32_t control_bits; /* not pressent - Permission key table 64 bytes sha-265 pr. key*/ - uint64_t creation_date; - uint64_t last_modification_data; - uint64_t last_access_date; + uint64_t creation_date; + uint64_t last_modification_data; + uint64_t last_access_date; /* * 256 first pointers in direct mapping to data * 94 next pointers is a pointer @@ -153,6 +153,7 @@ typedef struct tag_record { typedef struct lsfs_file { lsfs_file_id file_id; lsfs_sector_offset table_entry_pointer; + Table_Entry_Kind entry_kind; char* filename; uint32_t owner_id; uint64_t size; @@ -173,6 +174,7 @@ int lsfs_disk_getattr(lsfs_file* find_file, const char* path) { time ( ¤t_time ); find_file->file_id = p_control.master_table.entries[i].file_id; + find_file->entry_kind = p_control.master_table.entries[i].entry_kind; find_file->table_entry_pointer = i; find_file->filename = p_control.master_table.entries[i].filename; find_file->owner_id = getuid(); @@ -277,23 +279,6 @@ time_t lsfs_disk_truncate_file(lsfs_file *file, off_t offset) { return result; } -// int lsfs_disk_rename_tag(lsfs_file_id file_id, char* new_filename) { -// for (int i = 0; i < MAX_MASTER_TAGS; ++i) -// { -// if(p_control.master_table.entries[i].file_id == file_id) { -// memset(p_control.master_table.entries[i].filename, 0, sizeof(p_control.master_table.entries[i].filename)); -// sprintf(p_control.master_table.entries[i].filename, "%s", new_filename); -// break; -// } -// } -// // free the sectors including the tag table - -// // Save the changes to disk -// fseek ( disk , (p_control.fsci.master_tag_records[0] * SECTOR_SIZE) , SEEK_SET ); -// fwrite(p_control.master_table, 1, sizeof(table_entry) * DEFAULT_MASTER_TABLE_SIZE, disk); -// return 1; -// } - int lsfs_disk_rename_file(lsfs_file* file, const char* new_filename) { memset(file->filename, 0, 256); @@ -314,13 +299,7 @@ int lsfs_disk_delete_file(lsfs_file_id file_id) { mif* mif_record = calloc(1, SECTOR_SIZE); read_data_from_disk(file_id, 1, mif_record); - for (int i = 0; i < MAX_TAGS_FOR_A_FILE; ++i) - { - if(mif_record->tags[i] == 0) { - break; - } - //lsfs_disk_untag_file(mif_record->tags[i], file_id); - } + // TODO Delete/free all data sectors. // Delete/free the mif record sector. @@ -328,189 +307,44 @@ int lsfs_disk_delete_file(lsfs_file_id file_id) { return 1; } -/*int lsfs_disk_delete_tag(lsfs_file_id file_id) { - tag_record* tag_table = calloc(64, SECTOR_SIZE); - read_data_from_disk(file_id, tag_table); - - for (int i = 0; i < MAX_TAGS_IN_TAG_TABLE; ++i) - { - if(tag_table[i].mif_record == 0) { - break; - } - lsfs_disk_untag_file(file_id, tag_table[i].mif_record); - } - - - int truncate_table = 0; - - for (int i = 0; i < MAX_MASTER_TAGS; ++i) - { - if(p_control.master_table.entries[i].file_id == file_id) { - p_control.master_table.entries[i] = p_control.master_table[i+1]; - truncate_table = 1; - printf("Tag deleted from master table - TagID: %lu\n", file_id); - } - else if (truncate_table) { - p_control.master_table.entries[i] = p_control.master_table[i+1]; - if (p_control.master_table[i+1].file_id == 0) { - break; - } - } - } - // free the sectors including the tag table - - // Save the changes to disk - fseek ( disk , (p_control.fsci.master_tag_records[0] * SECTOR_SIZE) , SEEK_SET ); - fwrite(p_control.master_table, 1, sizeof(table_entry) * DEFAULT_MASTER_TABLE_SIZE, disk); - free(tag_table); - return 1; -}*/ - -/*int lsfs_disk_tag_file(lsfs_file_id file_id, lsfs_file_id file_id) { - mif* mif_record = calloc(1, SECTOR_SIZE); - read_data_from_disk(file_id, mif_record); +int get_free_sectors_table() { + // We need DEFAULT_TABLE_SIZE sectors straight contigious for a table + // Otherwise the file system cannot make a new table. + // We return the offset where the table is starting. + // If we cannot assing DEFAULT_TABLE_SIZE sectors, we report errror. + + int return_index = p_control.fsci.next_free_sector; - for (int i = 0; i < MAX_TAGS_FOR_A_FILE; ++i) + if ((p_control.fsci.next_free_sector + DEFAULT_TABLE_SIZE) > p_control.fsci.last_sector_index_on_partition) { - if(mif_record->tags[i] == 0) { - mif_record->tags[i] = file_id; - break; - } + // We don't have space, report error + return -EINVAL; } - write_data_to_disk(file_id, mif_record); - free(mif_record); + p_control.fsci.next_free_sector += DEFAULT_TABLE_SIZE; - tag_record* tag_table = calloc(64, SECTOR_SIZE); - read_data_from_disk(file_id, tag_table); + fseek ( disk , (p_control.fsci.this_partition_offset_on_disk) * SECTOR_SIZE, SEEK_SET ); + fwrite(&p_control.fsci, 1, SECTOR_SIZE, disk); - for (int i = 0; i < MAX_TAGS_IN_TAG_TABLE; ++i) - { - if(tag_table[i].mif_record == 0) { - tag_table[i].mif_record = file_id; - printf("file tagged - TagID: %lu - FileID: %lu \n", tag_table[i].mif_record, file_id); - break; - } - } - write_data_to_disk(file_id, tag_table); - free(tag_table); - return 1; + return return_index; } -*/ -/*int lsfs_disk_untag_file(lsfs_file_id file_id, lsfs_file_id file_id) { - mif* mif_record = calloc(1, SECTOR_SIZE); - read_data_from_disk(file_id, mif_record); - int truncate_table = 0; +int get_free_sectors(int num_sectors_needed, lsfs_sector_offset* output_array) { - for (int i = 0; i < MAX_TAGS_FOR_A_FILE; ++i) + if ((p_control.fsci.next_free_sector + num_sectors_needed) > p_control.fsci.last_sector_index_on_partition ) { - if(mif_record->tags[i] == file_id) { - mif_record->tags[i] = mif_record->tags[i+1]; - truncate_table = 1; - printf("file untagged - TagID: %lu - FileID: %lu \n", file_id, file_id); - } - else if (truncate_table) { - mif_record->tags[i] = mif_record->tags[i+1]; - if (mif_record->tags[i+1] == 0) { - break; - } - } + // We cannot assign what we want. + return -EINVAL; } - - time_t current_time; - time ( ¤t_time ); - - mif_record->last_modification_data = (uint64_t) current_time; - mif_record->last_access_date = (uint64_t) current_time; - - write_data_to_disk(file_id, mif_record); - free(mif_record); - - tag_record* tag_table = calloc(64, SECTOR_SIZE); - read_data_from_disk(file_id, tag_table); - truncate_table = 0; - - for (int i = 0; i < MAX_TAGS_IN_TAG_TABLE; ++i) + for (int i = 0; i < num_sectors_needed; ++i) { - if(tag_table[i].mif_record == file_id) { - tag_table[i].mif_record = tag_table[i+1].mif_record; - truncate_table = 1; - printf("file untagged - TagID: %lu - FileID: %lu \n", tag_table[i].mif_record, file_id); - } - else if (truncate_table) { - tag_table[i].mif_record = tag_table[i+1].mif_record; - if (tag_table[i+1].mif_record == 0) { - break; - } - } - } - write_data_to_disk(file_id, tag_table); - free(tag_table); - return 1; -}*/ - -/*lsfs_file_id lsfs_disk_create_tag(char* tag_name, bool is_filename) { - // Return ID of new tag, oterwise return 0 - lsfs_file_id* free_sectors; - // Returns an array, with the sector numbers that is assignt to you - free_sectors = calloc(DEFAULT_TAG_TABLE_SIZE, sizeof(lsfs_file_id)); - get_free_sectors(DEFAULT_TAG_TABLE_SIZE, free_sectors); // has to be freed - - // Insert tag in the master tag, table an set the pointer to the first of the tag table - int index_in_mtt = 0; - while(p_control.master_table[index_in_mtt].file_id != 0) { - //TODO also have to count if we enter next data section. - index_in_mtt++; + output_array[i] = p_control.fsci.next_free_sector; + p_control.fsci.next_free_sector += DEFAULT_FILE_SIZE; } - - p_control.master_table[index_in_mtt].file_id = free_sectors[0]; - printf("%lu\n", free_sectors[0]); - sprintf(p_control.master_table[index_in_mtt].filename, "%s", tag_name); - p_control.master_table[index_in_mtt].control_bits.is_filename = is_filename; - - tag_record new_tag[DEFAULT_TAG_TABLE_SIZE]; - memset(new_tag, 0, (DEFAULT_TAG_TABLE_SIZE * sizeof(tag_record))); - - //printf("Sector number: %lu, is assignt to you \n", (*free_sectors)); - //char* data = "red_file_1\nred_file_2\n"; - //char* sector_to_write = write_mechanism_new_buffer(data); - fseek ( disk , (p_control.fsci.master_tag_records[0] * SECTOR_SIZE) , SEEK_SET ); - fwrite(p_control.master_table, 1, sizeof(table_entry) * DEFAULT_MASTER_TABLE_SIZE, disk); + fseek ( disk , (p_control.fsci.this_partition_offset_on_disk) * SECTOR_SIZE, SEEK_SET ); + fwrite(&p_control.fsci, 1, SECTOR_SIZE, disk); - fseek ( disk , ((*free_sectors) * SECTOR_SIZE) , SEEK_SET ); - fwrite(new_tag, 1, DEFAULT_TAG_TABLE_SIZE * sizeof(tag_record), disk); - - //free(sector_to_write); - int return_value = free_sectors[0]; - free(free_sectors); - return return_value; -} -*/ - -int get_free_sectors(int num_sectors_needed, lsfs_sector_offset* output_array) { - /* - * TODO - WARNING - * This has to be a much better algoritm, to pick free sctors. - * We have to keep some bookeeping of what is free. - * Also if more sctors are claimed, we want them to be sequtive. - * This is just a naiv counter, just added more sectors to the file. - */ - - if (num_sectors_needed > 1) { - for (int i = 0; i < num_sectors_needed; ++i) - { - output_array[i] = p_control.fsci.next_free_sector; - p_control.fsci.next_free_sector++; - } - } - else { - output_array[0] = p_control.fsci.next_free_sector; - p_control.fsci.next_free_sector++; - } - printf("Sector %lu is assignt\n", output_array[0]); - write_data_to_disk(0, 4, &p_control.fsci); - return p_control.fsci.next_free_sector; + return 0; } int create_file_system() { @@ -522,7 +356,7 @@ int create_file_system() { // we need the first number to allocate memory at one go. int* zero_buffer; FSCI fsci; - fsci.offset_on_disk = 1; + fsci.this_partition_offset_on_disk = 1; //fsci.maximum_sectors_on_partition = 1048576; // Max 4GiB fsci.next_free_sector = 257; @@ -533,8 +367,8 @@ int create_file_system() { free(zero_buffer); /* MASTER TAG TABLE */ - table_entry master_table[DEFAULT_MASTER_TABLE_SIZE]; - memset(master_table, 0, (DEFAULT_MASTER_TABLE_SIZE * sizeof(table_entry))); + table_entry master_table[DEFAULT_TABLE_SIZE]; + memset(master_table, 0, (DEFAULT_TABLE_SIZE * sizeof(table_entry))); fwrite(&master_table, 1, sizeof(master_table), disk); zero_buffer = calloc(1, 16); @@ -561,10 +395,12 @@ int lsfs_disk_load_disk() { // First we find the File system control information. fseek ( disk , mbr.partitions[i].LBA_abs_first_sector * SECTOR_SIZE, SEEK_SET ); fread(&p_control.fsci, 1, SECTOR_SIZE , disk); + //printf("next free sector: %d\n", p_control.fsci.next_free_sector); + //printf("next free ID: %d\n", p_control.fsci.next_uniqe_id); // next we find the Mater Table. fseek ( disk , (mbr.partitions[i].LBA_abs_first_sector + 1) * SECTOR_SIZE, SEEK_SET ); - fread(&p_control.master_table, 1, 10 * SECTOR_SIZE , disk); + fread(&p_control.master_table, 1, DEFAULT_TABLE_SIZE * SECTOR_SIZE , disk); return 1; } @@ -573,62 +409,92 @@ int lsfs_disk_load_disk() { } -lsfs_file_id lsfs_disk_create_file(char* filename, lsfs_file_id* tags, void* file_data) { +int lsfs_disk_create_entry(const char* path, Table_Entry_Kind entry_kind) +{ - // create space for mif - mif new_file_data; - lsfs_file_id return_id; + lsfs_string_array split_path = lsfs_string_split_c(path, '/', false); + lsfs_string filename = split_path.strings[split_path.length-1]; + // Start from the master table + int free_index = -1; // -1 is no index found. + Directory_Table *dir_table = &p_control.master_table; + lsfs_sector_offset table_disk_position = p_control.fsci.master_table_index; + for (int i = 0; i < split_path.length; ++i) + { + for (int j = 0; j < DEFAULT_TABLE_SIZE; ++j) + { + if (i == (split_path.length - 1)) + { + // Find free index and be sure that there dosent exist a file with the same name. + if (dir_table->entries[j].entry_kind == ENTRY_EMPTY) + { + // Set the free index, continue to see if the filename exist. + // if not -1, we have found a better index. + if (free_index == -1) + { + printf("Index found for file: %d\n", j); + free_index = j; + } + } + else if (strcmp(dir_table->entries[j].filename, split_path.strings[0].chars) == 0) + { + // Abort mission, we have a file with the same name. + return -EINVAL; + } + } + else + { + if (strcmp(dir_table->entries[j].filename, split_path.strings[0].chars) == 0) + { + // We have found the next directory to traverse. + ; + } + } + } + } - memset(new_file_data.filename, 0, sizeof(new_file_data.filename)); - sprintf(new_file_data.filename, "%s", filename); // remeber that the 260 bytes has to be a /0 terminator. // 260 because it pads to a full sector. - - printf("%s\n", new_file_data.filename); - new_file_data.owner_id = getuid(); - - lsfs_file_id* index_to_mif_data; - index_to_mif_data = calloc(1, sizeof(lsfs_file_id)); - get_free_sectors(1, index_to_mif_data); // has to be freed + if (free_index == -1) + { + // The table is full, and we cannot create an entry + return -EINVAL; + } - memset(new_file_data.tags, 0, (32 * sizeof(lsfs_file_id))); - - new_file_data.file_size = 0; + // Find the entry for the file in the table structure: + dir_table->entries[free_index].file_id = p_control.fsci.next_uniqe_id; + p_control.fsci.next_uniqe_id++; + + sprintf(dir_table->entries[free_index].filename, "%s", filename.chars); + dir_table->entries[free_index].entry_kind = entry_kind; + if (entry_kind == ENTRY_DIRECTORY) + { + dir_table->entries[free_index].data_pointer[0] = get_free_sectors_table(); + dir_table->entries[free_index].file_size = 5120; + } + else if (entry_kind == ENTRY_FILE) + { + // We assign one data pointer consiting of DEFAULT_FILE_SIZE sectors + get_free_sectors_table(1, dir_table->entries[free_index].data_pointer); + } + else + { + return -EINVAL; + } + /* + find_file->creation_date = (uint64_t) current_time; + find_file->access_time = (uint64_t) current_time; + find_file->modification_time = (uint64_t) current_time; + find_file->data = p_control.master_table.entries[i].data_pointer; + find_file->owner_id = getuid(); + new_file_data.owner_id = getuid(); time_t current_time; time ( ¤t_time ); - - new_file_data.creation_date = (uint64_t) current_time; - new_file_data.last_modification_data = (uint64_t) current_time; - new_file_data.last_access_date = (uint64_t) current_time; - - new_file_data.control_bits = 0; - memset(new_file_data.one_level_pointer_data, 0, (256 * sizeof(lsfs_sector_offset))); - memset(new_file_data.two_level_pointer_data, 0, (94 * sizeof(lsfs_sector_offset))); - memset(new_file_data.three_level_pointer_data, 0, (94 * sizeof(lsfs_sector_offset))); - get_free_sectors(1, new_file_data.one_level_pointer_data); - int actual_file_size = 0; + */ - new_file_data.file_size = actual_file_size; - fseek ( disk , index_to_mif_data[0] * SECTOR_SIZE, SEEK_SET ); - fwrite(&new_file_data, 1, SECTOR_SIZE, disk); + fseek ( disk , (table_disk_position + free_index) * SECTOR_SIZE, SEEK_SET ); + fwrite(&dir_table->entries[free_index], 1, SECTOR_SIZE, disk); - printf("MIF written at sector: %lu\n", index_to_mif_data[0]); - printf("DATA written at sector: %lu\n", new_file_data.one_level_pointer_data[0]); - - int i = 0; - if (tags != NULL) { - while(tags[i] != 0) { - printf("A tag is found \n"); - //lsfs_disk_tag_file(tags[i], index_to_mif_data[0]); - new_file_data.tags[i] = tags[i]; - i++; - } - } - - - return_id = index_to_mif_data[0]; - free(index_to_mif_data); - return return_id; + return 0; } int save_modified_file_information(lsfs_file* file) { diff --git a/lsfs_fuse b/lsfs_fuse index a19ab82dfb4fcd3cfc9c4db7d522286a8d60a4bc..80499746431eed6ffd9c5608b95fd72b26f420d4 100755 GIT binary patch literal 38992 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�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._J*X#`=~}t&d!21S#&| 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 QX<)`hKROkAB@~VS2aJg1(f|Me literal 36640 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 #include #include @@ -51,8 +46,7 @@ static struct fuse_operations lsfs_oper = { int lsfs_mkdir(const char *path, mode_t mode) { (void)mode; - // Call to the disk controller make the dir - return 0; + return lsfs_disk_create_entry(path, ENTRY_DIRECTORY); } @@ -100,7 +94,19 @@ int lsfs_getattr( const char *path, struct stat *stbuf ) { stbuf->st_nlink = 2; } else { if(lsfs_disk_getattr(found_file, path)) { - stbuf->st_mode = S_IFREG | 0777; // @Hardcode + if (found_file->entry_kind == ENTRY_FILE) + { + stbuf->st_mode = S_IFREG | 0777; + } + else if (found_file->entry_kind == ENTRY_DIRECTORY) + { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + } + else + { + res = -ENOENT; + } stbuf->st_nlink = 1; // @Hardcode stbuf->st_size = found_file->size; stbuf->st_uid = found_file->owner_id; @@ -114,78 +120,7 @@ int lsfs_getattr( const char *path, struct stat *stbuf ) { } } - - - /*else { - if (lsfs_disk_getattr(found_file, path)) { - stbuf->st_mode = S_IFREG | 0777; // @Hardcode - stbuf->st_nlink = 1; // @Hardcode - stbuf->st_size = found_file->size; - stbuf->st_uid = found_file->owner_id; - stbuf->st_gid = found_file->owner_id; - stbuf->st_atime = found_file->access_time; - stbuf->st_mtime = found_file->modification_time; - free(found_file); - } - else { - res = -ENOENT; - } - } */ - return res; - // printf("getattr: (path=%s)\n", path); - - /*memset(stbuf, 0, sizeof(struct stat)); - - if( strcmp( path, "/" ) == 0 ) { - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 3; - return res; - } - - lsfs_string_array split_path = lsfs_string_split_c(path, '/', false); - - lsfs_string filename = split_path.strings[split_path.length-1]; - - lsfs_tag *filename_tag = lsfs_hash_find(globals.tag_table, filename); - - if (filename_tag) { - if (filename_tag->is_filename) { - lsfs_file *found_file = lsfs_find_unique_file(filename_tag, split_path); - - if (found_file) { - if (found_file == &dummy_ambiguous_file) { - // stbuf->st_mode = S_IFDIR | 0755; // @Hardcode - // stbuf->st_nlink = 3; // @Hardcode - res = -ENOENT; - } - else { - stbuf->st_mode = S_IFREG | 0777; // @Hardcode - stbuf->st_nlink = 1; // @Hardcode - - stbuf->st_size = found_file->size; // @Hardcode - stbuf->st_uid = found_file->owner_id; - stbuf->st_gid = found_file->owner_id; - stbuf->st_atime = found_file->access_time; - stbuf->st_mtime = found_file->modification_time; - } - } - else { - res = -ENOENT; - } - } - else { - stbuf->st_mode = S_IFDIR | 0755; // @Hardcode - stbuf->st_nlink = 3; // @Hardcode - } - } - else { - res = -ENOENT; - } - - lsfs_destroy_string_array(split_path); - */ - //return res; } int lsfs_write(const char *path, const char *content, size_t content_length, off_t offset_to_next_entry, struct fuse_file_info *file_info) { @@ -216,12 +151,13 @@ int lsfs_readdir( const char *path, void *buf, fuse_fill_dir_t filler, off_t off filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); - int i = 0; - while(strcmp( "", p_control.master_table.entries[i].filename ) != 0) { - filler(buf, p_control.master_table.entries[i].filename, NULL, 0); - i++; + for (int i = 0; i < DEFAULT_TABLE_SIZE; ++i) + { + if (strcmp( "", p_control.master_table.entries[i].filename ) != 0) + { + filler(buf, p_control.master_table.entries[i].filename, NULL, 0); + } } - return 0; } @@ -258,59 +194,15 @@ int lsfs_release(const char *path, struct fuse_file_info *fi) { } int lsfs_mknod(const char *path, mode_t mode, dev_t device) { - /*(void)mode; + (void)mode; (void)device; - int res = 0; - - lsfs_string_array split_path = lsfs_string_split_c(path, '/', false); - lsfs_string filename = split_path.strings[split_path.length-1]; - if(filename.chars[0] == '@') { - res = -EINVAL; - goto end; - } - - lsfs_file *file = NULL; - - lsfs_tag *filename_tag = lsfs_hash_find(globals.tag_table, filename); - if (filename_tag) { - file = lsfs_find_unique_file(filename_tag, split_path); - if (file == &dummy_ambiguous_file) file = NULL; - } - - if (file) { - res = -EINVAL; - goto end; - } - - lsfs_file_id file_id = lsfs_disk_create_file(filename.chars, NULL, NULL ); - lsfs_set *tagset = lsfs_create_set(split_path.length - 1); - filename_tag = lsfs_get_or_create_tag(filename, true, true); - - file = lsfs_create_file(filename, file_id, tagset, filename_tag); - - lsfs_hash_table_index *index = malloc(sizeof(*index)); - *index = lsfs_hash_insert(globals.file_table, lsfs_create_id_string(file_id), file); - lsfs_set_insert(&filename_tag->fileset, file->file_id, index); - lsfs_disk_tag_file(filename_tag->tag_id, file->file_id); - - for (unsigned int i = 0; i < split_path.length - 1; ++i) { - lsfs_string tag_name = split_path.strings[i]; - lsfs_tag *tag = lsfs_get_or_create_tag(tag_name, true, false); - lsfs_tag_file(tag, file); - } - - lsfs_set_insert(&globals.all_files, file_id, index); - -end: - lsfs_destroy_string_array(split_path); - return res;*/ - return 0; + return lsfs_disk_create_entry(path, ENTRY_FILE); } int main( int argc, char *argv[] ) { // "/home/rhodez-x/Documents/github/SingOS/SingOS.img" - disk = fopen ("/home/rhodez-x/Documents/github/SingOS/SingOS.img", "r+b"); + disk = fopen ("/home/rhodezx/Documents/github/SingOS/SingOS.img", "r+b"); if (lsfs_disk_load_disk()) { return fuse_main( argc, argv, &lsfs_oper ); diff --git a/lsfs_fuse.o b/lsfs_fuse.o index 3d1c55bc8e4f45982da9ce7eb7c2530b66167a24..19abc3005dcf83868886c3e758e4e4248f7d934d 100644 GIT binary patch literal 36376 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 literal 31504 zcmeIbd0b6h^!R@pP^JncB&9NxA`K#xbaPQ@q(_nFqDhksrBuk3u}CuZNTv`ai6$yj zg(5wn2qA<}M8CbyT|1}!)bsg#U%%J)zwds%Zs(l$UVH7e*Is)#_nyOguBnNzkPwF? zAxJ`pLWMj&w}Q!teaMR~VG73NOT^$4 zUL3cXFoA-uh518-QE6m>B+hlI5L{S@{byxJ+(MG623dhh$6Cn#r)~=cB!$digi+aX zT`wnfz0~`oD{hB^2yO$bp}1}yuC&C^5c?Z>2p^)jjY2)xERiKD9GxH3H!7@&BN3hT zyQ728ZQ}9GT3vZs{TY`B^9TJHD0?09=l$^sH0zBrDvE-jay-*xsI#FT znBKBb!10tQ82ETR;NgXi0vVLo+5ZZt{~OWlQilZ4v83_DFJuz#Og^_x5T*z-<(X{8 zMt1CVDxjt(Mjzi$G6&CjU7AL6kRR(ve8iSyW6gp9VNFO#7G?!(1rBG{C64A!qq6ZB z|IabYSjq%#9+rinf?H~fTibYz(EvUw;kICd-pQeyR=#Zu9?4OK@;ttAMPEFS`$utG z&|qvC75)?rMl3|U%NX*;c9h#Q^EzM-0XhuUsF!0#UWr{NK%bGC+6@t}~O$r-0 z9J)VCVw%G{a9DGAB{8k|6rDreIoKSklLaQm#WaWEfV#&Ffi5n-#u>=ru)89r5sQ+x z39fKKB-W)Mj}Lhe@~DpW9UVEGh@Ndg`HT%%gx$^8B$46&3mdRlmx7cHjD!Cp`jAV} zc&A+|@cMr`j~R{KIIkq831uOxIB$la2Jx9oX7L|^CNWW-P$N(MH_DL6;!Ky7hhQ0=JddOCE(u4hI?8wZ+7i-#EJ&{pmjY z1DeDBBO5D6bJd?Vmc+Cm!?ADq%o&R7Sf*LTA)o|qCD5iwae-=cm zPVhdq)iGl15b5k>0sW7?E3;cfO9WcLMkC<|VuaASc02tgBYEhwNJ5k6NUz`@57@!PXE}X(;w*)5DD3&NX!s-m}2w z8`an-xZC-kVu9HJA&YSkvzLHaZaZ38yDG;kcC2mfALWI7ZY}Ci)Pw(H%rT4d|FWL{ z(?;;Q4S0SmMuwr?+n?zt)GmH;)QnQ`3`!F>xtvB51A`H#z}Ce(w1?cp^GYJy_+ zP55R3)zW!S$!zIp#M&C+Aq}4H#ynPau>I<`R=nryW@Cf=p0fk#?>YtH@PK;|*x8|B zgg13{C5E!^XQAlD{Z(R!*Fnykt>_PG1KljN-}8kc9g%;EFj2&eBOK{y|Ax0?f_p8# zg)F{|wGiwd$&~_vIJ8j&6LB6Oy+6qA9lCjGL5mi;4Q=TRObdLchI$l*plvBH)-2yN zmYe;rwF&Q!$yC5CWg;aTRVd5EE&2`s)eB=)a4RRs&BE}l^7L+Rl?_P7B4T zRJi&wI!7xKCW7wms6bZqltuR4T7rG|zvrGfZM5m8i+5DOwi}K7e-{y|Kzl2Rto-lQ zD58#couU2@hYh};KyGbC4Z{&6+f~{Bm5%Syhm)8->~YEHW~d5g64axO?CB$_NisI9 z9nU&s%&|6SJi(m|BhT!7P^;1C2yRAwa#f*+uJQb!;=%_eiT34SJw0a5QV4SL!0O!nf_+^TI38|6PC-Ed(-g+}I|X^pPzfC0mot$Q;O6A& z>eZEMa5OYH0cy^u4HG^6eB34mc>1}ztx*r1Xzb?_?1S19IMKr^$TQe^qP3T=#{z3O zXn6T}K)da^R#pqFmMEwz%=SYba0yay_wsf_rUdw)M)>-nYk^fD|Hq%Wt!|F4PC-tN zL4J<76o~x4!~enqcv-+{gq+|=u0xZ9?biC+R1M2}{b9`I{a4q1d~)T=P4`t_>YR9D zt2xdQV$sLzNA}WOp3jrspz_Hw(P_hQsW~UNRoQymt`pT4d1!fStCo4H%MB|ZpN@*H z2eu6x#nZ2`+;^gSmg{My8P8rMKjG`FZwk-*GNXJ|`1dOw*88-!o)cQXCt|@r*>w|l zjw?;gGz{OE^m2|}eBnG#-O`1_N>%E2`9$geZ7GqoA+@q^zkWeZgUt$^evI~rpZzr< z%+tckE@`pmW+(ohqzm&eCZAO;-EK2rdE33wtJm+m>%zU3cV~!qxb4N?p;bca20PVD zmG*5|_jis$Ub0$gN^p$cS(%%DrWLyNLC-_fJ$6ZWP2IoF{HDr-!DBDhN*Nq|S=M^! zVs2%J(d`v4@*aG)6`ym?Veu=Ecn963;$Q2Ag)Y1vU|O`9bMC0FtmZlE5u$dV<_5gJ zcS`ZT_b=Pk+{N=34Zo!}xu~dGRAW}$qHW94`F+MM-yQv0E8QtKaiz|S+p@1^&$=hO z)tc$8P)@P`5m)-7amR!yX0d0DEap_rba?7KQfy`9OP}*fIW?dEI=?D7^W~S?3l{2; zZ3A243lmlqA3iZvxSvZ!+caUm(z&9WqI;Yhdkd>Mr%pH$m!A}RRrG>*&{{3!MrD7W zw~m8?q#C&uvzIAt2p67WyRCF&(`2KHkP!o4rCrx-%=D^m_P?5bZjzjT?96u&;y0Ca z2t{(t0#w#Kt!*2ik)QMk}58;IrXwk=++b`AuQ_dCH%Y9H*QS zUFcp~u6MfPv+ycSjfoTMm)RFwNjL6QH1_#Yv5hrP77Oiai4xI0bLQ4s<9GEtu6{qU zC2U@p=_$?G`Zx6ct{i*uM=#GGN)5kKtq%1|JN9buvDkzR6OHh*GRM=n$L7XoxbJ(I zE4=#P5zo8xo*b{Lyu()}xp6UL&vV}#s{Ao_?xCV@wWnIvkN-7m?W9eU4gT)m zF*xnlri~xR&Awv#I6_&+$L`+LV~5hes_}2l+k5^;QikC{ql|>gj1ad_kBW1xK|kZ~ zI{8GoiazhX<=ULLzwS+bcspiq{Oc=`s>fdG9BZ4mV(GXo+Ub=CZP(e%`xR$(X}Qjz zy;afy(q~1z>TC8`mq}b3ap42*rIEZQ;?Dp=L04l2DlZm`PFvFGR=um7gdjyj#7lbd5Jg;K6^58o3u-zw#4bXPCZEAh2*E3zcpYJ{(uUXw+<-o}TKC9) zKW;yYyKm6%I~j#{3-XkOcWxdPt}pz2lfej~Peq5KB3kRqxrXh{`jV0c?enG3pVeYW zVl0x#lSC4Z(MV$A3B3`v?ag~t^7yLZtxN255Z|_}Po%VE=-XPYvm;@a;n$sXbax24rjFb?~2pn_u$a~Y3moJS^ZF+d@U~m0~OLm89 zUJl>#^ZBhI4zl}xI?sNqB9XnI@noffgO%*pzjf!wy9DVRU2gJ7;iOZL`q9NESKsV) zyECo8HYaY%4Vwh%=emA*=>cXxx6*GG9_^*?b-<5F_7iuvCjj@tAj z?Wzxd=*RbCjFxN`_k4q% zekjl0mH13+4d-57+n%wz_DPI9X!PW{xZjmi%h%nXGe%NxNAF%{il1yQPKvqypzQFq zQ(HWe6~h)umybR?UfD#lV#uIAQs_^)6DP^&`6JuRjBJhcGliq9^2&_e2C9}GPA+(@ zJZN(3_JtRQHC$YJY;BHR-Q$Bb#YP?%t{vxd2aXesypbk8*lt78AmRDvXFmNgSv1Q@ z>7NznJR?uYe~Pd9(2TCieW@rv9TgTf|`8{^r0>D$Mb!phYt zyP``KKE4^H9UtVARq$fvmtFS{>{aS5FFaz+w>goHg{#|n}a-iYJ>zPBYEUb^4oHA1((E0s@Q?26yvTHQvEgT+`r{1o9aQGg}laEC| zTDyl|DSWIZ`F)s9w&cL#v>?b-SCF*IC#jKL$widO|?FK*H{`tx2 z_tEw_-kc(xl9l}q*cDX!*3>mNE62{P`{n(*vPAgAoZ#i5iQ{TCtCnn3zkB1!3|)ij zCob=7R}K4U`TkhA{Gwlfhv%Nix%;xE_pO`awq?ON$xExmH)_>QiEI*$c~)BQcer_f z(3M#Ji128B`P=3ZC#8q3s-0k|rfB>0a|CL3htM?#%|lNeSKB0uM_%S4-C!~ zA202<^W>%()55R29Qf8)+G~b;@9FE_pLbk)$40(?m57h2N%H*mj?`aE`jlp#OjXnm zoWH46y!fWFypD)UyYZYv>jG(An`~))%YrEkl;h*IZ<|ZIi~G^Ai(YQpyxnZMTKFTPu{zyr6V$d)O=K z$gtW~PTP(w+h))xcOht-;sx9Do7YV<^Vn+nc}J9 zX9Jq=uh+Rd@pZ3t{f}>$SiY#`UX|?$7d@59(|-?HD*7>W?yRdPZf`A2y(V+bxzFpw z!FSGEzs{cXI#FqOh*Yl=zb_w^^VQg;F>TI#%WbO*>vx_A(C!_WdpL0GJGr={2gg;X zGq zdnVRwliIq*y}DoMfH%9ej4bMHTGpODnyo%EM}6>xo|pwmfh3Vs2{7I2=!OvnxC8Tu`oOQ*R))I_ zvp8N44%ck_YpsF8ZK3K)rCw!WCujA(qqg+YH|P2KKQArl+aR*u{h6cm!(*Rwlvh>x zb)0z5{}sPB_0dU@ooW(R(ffa|6A82rYPzjHa)Q0|$j`yMpY|Kkp?ct?{+OvE-!oFR zV-l{v-^P^MJlL^POPc5l4 zh$wcQzh}tT#dRSoJMvp3IIo@`@V&k3Nz?b1AqPe%fBceL{`zB^p5Ld)n~7R5bMt{TxHwK?{TUwok1>hu0p2XDK-f9H~- zmGZHF!iCv4jBZ|?sl6!R{rLv}`;R}Zn_>F&%ryaU!_j>g$>{Y;; zBd1TKH-ywp*^xYT?T3ropG%KTP}jD8l2+EBnlM_V&A0cMxMlgCzyC5wd=)5q$>Cy7 z==#2jnOk)ejDHR?Ka=A4a%C;IU9Umq?oW{>LS1P(hjM}3&d!c=v@ywTX*M}F&*C%c&k{>O9XShc0UGIS#U!8we zv~0jg`RDVi&kCu`y(9DF=s|^4sqYcfifxDHZyoxw_Ehh~;d8#rZFm%BzpnPEJpb#{ ze9t~cgT=~~Y-aydH%{Px8=%8Y-FW!eSM8kB{sxn_m-gO$S?o-|(z$(}tUvmq*XZbT zm6GoM#cxe#&T-v8w<_X?^u6Gg){$Xe+A;A-Cgn+WCUysQzxp~g&Qj%=;-PEarDk>G zj8;4kD9Bkc=E&)~G1Kg#P2zQBt|%JEDUGp7SyXDAaz6K1bo=JRqkNm!8tbfaP!@Z6 zFegsr^u3^!`6rZABiDY6O}3V}t}pJprQyWlAK#^qs0%$fmHIBCbpLtUzIbr_6y&9QuPA4jUSpC#lZl3!>%C!FYVqMLu&j~f62Tv@n zvlu$i_sGZ5M|;gpHxC$li1S_Q*`BJGs)5$qvokV()#m;4_I!KbY+uEYK7PJV{=Imo z>_yYUQ(Fhxt!&k;@#&wglrEp~by(1zx$@52r5lpQ<;@(bl)S)MWYp?b!xw|ox4o~| z-aGHu&d|s&@?~qrozS~~ZNl}M>IAj%A(eZ4-YIWZ=vTjR<${@k!#@}N=&(Jcudg9> z>)FdW@y8#2$-E-Wv!9Y@KKi0!y^pVqkwo;2c%w#pr|0IohlNP~j9Mn=rgbpSt>{GR zYwLpNW}@L#@DJoViE+gS!tjj9E!bQYeS7*(x0Xhq|x7oG#I4xh7RYM(Zf%9nvJ zE`Qf}ow=asqDjf818@A#g>K9WHhp#P)sgL2k`?~i`Cuv6aq;jeyQJKsxo&?q_?hYs zeLHi=5wWej-q}TuOE>P?y1TfG!nG}wiS$0D3tJ> zCRw-rg@IXJaKFxV!UR2+fYYCO%xBSU-&#G%hU2Y$9=dp+~}9--|UrtMJPG6e3Du33>K}+n$ z4Z3B0cZ|f_pL@$|ng*3^^->Z3Cuys}O0SIGb&uUPMemPrDBQf`^Mxs#(_^mxoH9GK zC4R5*(ftK_+dKAbo>Jl_yrJapt8qJbxDSlTeJ59X?EOBGGeggHNR9VYPn?iEvn69| zOq{gV00jw+-e*mVCQfW$)&E*v{+N?GkCQb=Cw@}d*=LBUnzq%L-LY%cqNnz=wP-O? zuAJ@g%O{^#>1b4%{V2M+Z@(moTSvwTpStKZeE*;s5vO^Tq3i9$Q(fEUHcpdVlo{{v z(PyntZN$@))+UCM^+oOv`ZZr#^(;?Hn%6cp^tNooma?r^Cw*KTyVY1&vMN`x`H{gh zdHLT}m-5!vPOtsdendxlQQ^+G@=;IgPY+7^a!Ofdy^Oq{^HO9{E##ES$3b$%@7a!l$#2MIYH#yfww#_^^h<-o>wG7wvl8>$Zz_$>5X4 zmr`a(-MG9v*Q<}{Sm%~+maat+71I5(H!KR~2Jf-6uNbrFzUPnV{zsIaCY4oaWoZwd zH~vCu#@GGI_N`?}uHt+NmxQ`HdE31f?Msg=Jv=t~;mZ6Q=dw*LRQmgDTC*!)(CRS* zmr9TMTK4kqZx1)vKj<$vL;m(?UDe>V>jRJfE!MD6W_i`cv1S(*^M4hlGz~s+eowCI zv{yNWMs_=&+b%y7JINzMH$>=!`#*bxzb@H4_|%eLZ7;sxxVzs#W9GFrKi_`6@iKm> zpTSp|$5(nAn0aqpuwaydLwM+_m5&`Aey>}yF5=j@+K3N};;J9@t{Jp??)aMVa;GP0 zSPj-nt5I1r?v}~rfOk9h-0MI1>#hlsE8A=P^>tPI*c8}tcJ(wzvvm6?sqmJHf(nI%^NmWbKfo(+kJJ5NW=GaZ9!jkv~Nc%Jh1)v`CL=F zs@$>w??;vWiXt48w_TNRcu?wg#_QXV_Iwq$@ROT%hdf%UCb?pV?786&GZHNi8LKqq zUS0aQe%0Q6rg51Yat^*yu3MyUqGYJzDm7R0Ws*>lKO_7Lh@stR0DCs{n?OI}V?#Vi{aI4}CroqGOG)`7{4|(J{ zVuA1C)P%C-A3TOP=J%o%4@keVVV9)ZNsuGY>;d^PZ};KAt~* z!)MQyhk8$Q{XhPQS?f1-iFhjCX;<{E)?TZo1$jlSn^_jU-B|gwZP0RoJdw;I| zF{?X6$M4wA+dF)(wuPv+rsbvhn?^})`@Uqoot~1G7GJNQXesk>sZpWM-SB<x@N_jI&&j$Q{W%(<4*`;TC?|Wa7-m5V(Y__7) zH|>&pUq!Ok^q**JeLH1a$gDFW5weM)N@k}sy)2&I_+Wf|g|Swq#k1XmR_#f$doLun zdC-?L=KLhdCefO6;dNRe}ivnI@{MEj4O~sXdUDibg4hLv;JhrU(zMtt}{Ou@;$rcyL9H)LcU9v z{H2}w?U2vhToL=snQX>BIk;yX#4d04Cx1HROR)3j{mFOuAM)2hz5~0w@S;xpk|Dn* z`*I;)4!48YFEX^#|Fw|M{LV}Ag)emKZ-;#5q1?{=kDd8)=p_K0%2L;3tH;CL9ZcP|HYS=4Nyeia!6A<}h<}cRZm7 z`J^8Bksf#k;@BQy2eTF+RssAGxX1-vnuX+*fj7apX6AClHG%Jip#jqgXF6~*kS8N& z4sc)KWCU6RcceIuKWtAJaAplc^?C!p2t(BjF`Pnx3qgfZ0?dg3E(VQe)*6(@2ObUb zWWGuSo)7ZO+Joee0e=kk5c%`K&ww6gEkN>Fh4+$5mP z<4(c;=+PWE#~?2sx4b8Q@LW*M1s9XWwP0WLoN=ox1pH~jcg zu$L>U4OQ!c9=q~f;ppz<<;~KCA7v6eIOHDS=OcJ@h+?=2tPhN*%aq(Zx=_=0XLOFqMK^*r0H#bh8Q;3_RkDsfTyO*0QQ@x{?ui%L%FF#*a zwLt;FzAo5{%+pPOoPnMcb3)EyXrO<9mv4|etCXvow;L&goZ*P;gy+fvIWDN7km(q> zIxxu1hvOdT=C*?4&KS*7`L&y8Yg?ffnj(L(vF_>#L17vWm$X! z@J>CjCCZ>VTo=L-Pxr&nhvzjzb%U^vv@)eypqM)^}Ys9{KLaWpwz;WAB(O@y=2Rcv)OT?3i}Prm09w$S)3h* z%UGNpSF2f^-R~Dzob87%z_I;I-4KVrdf;NP&!Oc<_Q2J8;JQ8VIV_Ija6bCrWCIg{r!jESU&Q`pEP1wmKNe@(8P_&EP1v)IX%di zQJlo*1C}1Poxdr0Qtv?6=VM#g^-2S$`*%FW<*)!s8Z15RdYxIE-7lwEd^9Rg>?vUB z$0qZza+Jzg@?+68k$=MCnk@dD#o6olcNS;I;UM^m0Q+G)OOGatv+bPE;_UhMAd9o* zulB%ed*I(#oNdoo_{sviogF_@fYbdrvj_RbEP3|$@@2_m|GT3PPN6J$Rdh}KA4|z^ zV5B*m-95;kqvQ{Pd?rht-H(&{3j}bx*yDXZi?ib@1UT`JoQR-&GR>jhvn)Ldh>>>P zrR2$e?;XX-xa%`Opb*>1UKg~06MKmM*(~`9ta|75An(GGS7*uZ??L_)OP;MiqX+rh zEO~X7{?;Dk#o&IHjyDP5bbB>e^6dE`k;U2L@hpo=v+OBkaTOLXV{vwz43t1ZC}BI< z`W1jKuZLi^2z#YdoeiQmFmob9(f;G|to!Tw^3HvqrQ(qqWdf1f3f zZ6fv7vg9>b{0mDSPk}`K2TOhyi;KVw8nKgnuOpN2g4}+fgkz#|Io^IAN~M zoC*FYz&YmlKAfa+aDurmf#^7i#iy|NG8WfkaZeVFM#LuK& zvTlY6vODWlfJX2sJ{%mjgW|>Dhl3P%0X-=c9|!F^OL0|@ze4fnP;UvvHL=4{dLTG- z+J6S@c}8(P+;EiM2@aj|Y2g3w6qkiy5rKM$-^jjAg5vqGzZpSs6X0VhJ`Va(i{k6R zeglf{1Nr$B_W}7u6dwlda-q03=<%cYWQd=&6juiSZ=(1Fkl#&l@_XGOijM|=o}#z{ z^vflRPlk9XqWEx-FQ@o2xc-OYme8;G_fMQiKSqL{7K*EbokCDQk)I5HkfL}w=#i!P zd+3*u6h8{%k;FUEe;)d43MKCb`i&_*9s1Fd;-z4x6UA*{ep^X#HHgnhieCi2lj1U< z=Lp67Lp)riIQiY^HpNrH@6{CdhW5UsxG?m~XNpUKd}2m=aBDd ziJl6uXC@`z8}!VlxI5TsNAYCP??&+hU}pfuJ7B!5r??};)mDm61^@4(xIWmEO7SCb zeSzX*!Jd4Ip9FjEQk?9W-ch_95O3=!z6AUkOYwPFAxe8FUJCJ$Lh%&>an3o4 zAAtGrD#dTXcp-5|?0*IIKBDBw^E3?<_X7JrQ`{J?nGFlFjp&~Q_LF@O;ieD|WIiC= z5&W+Ob`f3ydJHJ83GE`^e-in#Ah(E;H-Y&b{lh3sBJv-={*@HJ3jG*P@web-KE*S^ zKZz9ghB!G!@$=B$42t_h{O3{p4)8k^{{nt^O!2!=FX?BTjqe9zK>iCQ-!5IB7qgPtskJ3zb@QCt}0D=01k{&`AqE0BLn zac79z&lKMa<8B!E4;RPra11vVC362nxI47VfRgV6ek0GV5&3xV&k{<0FkE|4{1@~~ z48@f}|4xcO0ei?iN9rZ~6eWKY+IyMe#&CV32mXNKG2qWSic5n1A1Lkt_P0}f6ZB(W z@Ds8B7|a{86#pdX77kgLi2NFeA8ks08*n3v9|dkf@eZ($%)3NC*~hG)mf~dGeW$oP z)GG?VYmj=0pQR{H{47uLTeu)fY7{4a)1&xV@H3a<1Ho@r6er`|k>cZ^Jw6nl2lK;f ziW`9b7>f6Xe%S>a4^MD0)2{uL{8|`CX_UMIBhBHQr{u}+eAg&WzAv~<@xD-bHN~sJ z&X*L|gLUgO#qsmZICW5b2#kx7@Z~k>cVg#wisypgrcj*BC&m=t3vp{j@qWb56es$x zQ`}BqK4%O}M8wVt;99_OGrvl>AocmkShEhw)fM@hli$l@uq>B|HO;)#7%M=bOJ#@}%A&@cR_;C;5J46mX)4 zJXftt$rC-~cRHen%mc2J{0110Q563P{ya+Y^Waa?Kg1sL{P%TAp4d}QaRaFLJH^jJ zJP5<@ZbUzM{(Bg3(k`NZ3dP^UJYYg`(%t}yheEw!z)8L2IrVr-p46K{@$V2P859?T zaaTxjV$Xewll!Yr6es;6G(^x5#1G^-lK#Mn{p39XB}$&yKbhi}V1BcpIO(rt6#oeF zD}a-F$#eM|D0x!vVT$*IN#YcxhrDl4NXZjD_bC1h^wd$D*xyWXHL(9DaAH5%e++^j zAc>zzy`w4q3dV&xaH5C2M`29K6FoK*7lZh5r1X&YH$o_Rq9=ypWWGwExH-h(VM;&w zz4#I(PxL>ecowV|&nZ3R`<6CJz7X~m;_zY$@h90o45K)HuLP&@6er)OO`~`b%qJ$m ziT}y(AWJBDVvi5S$@eT_6en>NNAdg6Uk52(4)&h}PV6D?Srt(7#GZQ;C;q9YII;5+ z#fd$__~8^Jp#WipPQA(%yFqjAOC{E_j3W{gKdiR~;QP7VfvVwYu-^hDkY7`#{ec1oVq+aEtHg%a@_nP=oEt_N|bPH}md zhxLGW)(i3rD0x!vQi}hC{_>$XxsMB@IGJa*Qv4R^*-vqC7}qB$o(A*DWr`mn{-<~h z$lnK!+l#y00-vDtiITqu{nE~o$F3poR}UV+6zlXq@sAS47l1t)z=@y9d%Rpqp6Icm zcnHL;3&rig&H#%0K)sQ`Nxf&Gy+3LvW()S-yB`i zoa~G0C{Ffo#JR*yvOiP+rxQ;0rG^wI``%>~C;RFMij#f*VTzOchs0un+Ez9Z`7HWi=mL4 zCwj*gy^t&L=>NaJ3aB^^IWiv*ZH90{|I3vSob4Lgb8L{eXH*0IYpF#5_xY}aP_A=xeh=l z&_sfm&|{jB|-TTtelldd4eL*Nq6;U zL;b{0xPH9OlJbP_LJZeGiDaO2oT|qPjxM; + +typedef struct lsfs_string { + bool dynamic; + unsigned int length; + char *chars; +} lsfs_string; + +typedef struct lsfs_string_array { + unsigned int length; + lsfs_string *strings; +} lsfs_string_array; + + +static inline lsfs_string lsfs_make_id_string(uint64_t *id) { + return (lsfs_string){ + .dynamic = false, + .length = sizeof(*id), + .chars = (char *)id + }; +} + +static inline lsfs_string lsfs_create_id_string(uint64_t id) { + uint64_t *id_ = malloc(sizeof(id)); + *id_ = id; + return (lsfs_string){ + .dynamic = true, + .length = sizeof(id), + .chars = (char *)id_ + }; +} + +static inline lsfs_string lsfs_make_string(unsigned int length, const char *chars) { + return (lsfs_string){ + .dynamic = false, + .length = length, + .chars = (char *)chars + }; +} + +static inline lsfs_string lsfs_make_string_c(const char *cstring) { + return lsfs_make_string(strlen(cstring), cstring); +} + +static inline lsfs_string lsfs_create_string(unsigned int length, const char *chars) { + + char *copy = malloc(length + 1); // Space for null terminator + memcpy(copy, chars, length); + copy[length] = '\0'; + + return (lsfs_string){ + .dynamic = true, + .length = length, + .chars = copy + }; +} + +static inline lsfs_string lsfs_clone_string(lsfs_string string) { + return lsfs_create_string(string.length, string.chars); +} + +static inline void lsfs_destroy_string(lsfs_string string) { + if(string.dynamic) free(string.chars); +} + +static inline bool lsfs_string_equal(lsfs_string a, lsfs_string b) { + if (a.length != b.length) return false; + return strncmp(a.chars, b.chars, b.length) == 0; +} + +lsfs_string_array lsfs_create_string_array(size_t array_size) { + lsfs_string_array result; + result.length = array_size; + result.strings = malloc(array_size * sizeof(lsfs_string)); + return result; +} + +static inline void lsfs_destroy_string_array(lsfs_string_array array) { + for (unsigned int i = 0; i < array.length; ++i) { + lsfs_destroy_string(array.strings[i]); + } + free(array.strings); +} + + +lsfs_string_array lsfs_string_split(lsfs_string string, char delim, bool keep_delim) { + unsigned int i; + unsigned int last; + + unsigned int count = 0; + + i = 0; + last = 0; + while(i < string.length) { + if (string.chars[i] == delim) { + if (i > last+1) { + ++count; + } + last = i; + } + ++i; + } + if (i > last+1) { + ++count; + } + + lsfs_string_array result = lsfs_create_string_array(count); + + unsigned int insert_index = 0; + int k = keep_delim ? 0 : 1; + + i = 0; + last = 0; + while(i < string.length) { + if (string.chars[i] == delim) { + if (i > last+1) { + result.strings[insert_index++] = lsfs_create_string(i-(last+k), string.chars+(last+k)); + } + last = i; + } + ++i; + } + if (i > last+1) { + result.strings[insert_index++] = lsfs_create_string(i-(last+k), string.chars+(last+k)); + } + + return result; + +} + +static inline lsfs_string_array lsfs_string_split_c(const char *string, char delim, bool keep_delim) { + return lsfs_string_split(lsfs_make_string_c(string), delim, keep_delim); +} + +char *dbg_strarr(lsfs_string_array strings) { + static char temp[8192]; + memset(temp, 0, sizeof(temp)); + + int where = 0; + where += sprintf(temp+where, "{"); + for (unsigned int i = 0; i < strings.length; ++i) { + where += sprintf(temp+where, "<'%.*s'>", strings.strings[i].length, strings.strings[i].chars); + } + where += sprintf(temp+where, "}"); + + return temp; // @Leak +} + +#endif \ No newline at end of file