From c4b67adacf42fa36806b6d369c12330d48e97e27 Mon Sep 17 00:00:00 2001 From: Rhodez-x Date: Sun, 11 Nov 2018 21:33:28 +0100 Subject: [PATCH] Starting --- Makefile | 23 ++ load.sh | 1 + lsfs_disk_controller.h | 565 +++++++++++++++++++++++++++++++++++++++++ lsfs_fuse | Bin 0 -> 31392 bytes lsfs_fuse.c | 308 ++++++++++++++++++++++ lsfs_fuse.o | Bin 0 -> 40104 bytes unload.sh | 1 + 7 files changed, 898 insertions(+) create mode 100644 Makefile create mode 100644 load.sh create mode 100644 lsfs_disk_controller.h create mode 100755 lsfs_fuse create mode 100644 lsfs_fuse.c create mode 100644 lsfs_fuse.o create mode 100644 unload.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3babbff --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +GCC = gcc +SOURCES = lsfs_fuse.c +OBJS := $(patsubst %.c,%.o,$(SOURCES)) +CFLAGS = -O0 -g -Wall -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=25 + +.always_rebuilt: + +## +# Libs +## +LIBS := fuse +LIBS := $(addprefix -l,$(LIBS)) + +all: tfs + +%.o: %.c .always_rebuilt + $(GCC) $(CFLAGS) -c -o $@ $< + +tfs: $(OBJS) .always_rebuilt + $(GCC) $(OBJS) $(LIBS) $(CFLAGS) -o lsfs_fuse + +clean: + rm -f $(OBJS) lsfs_fuse diff --git a/load.sh b/load.sh new file mode 100644 index 0000000..cca2784 --- /dev/null +++ b/load.sh @@ -0,0 +1 @@ +./lsfs_fuse lsfs_folder/ \ No newline at end of file diff --git a/lsfs_disk_controller.h b/lsfs_disk_controller.h new file mode 100644 index 0000000..7fba263 --- /dev/null +++ b/lsfs_disk_controller.h @@ -0,0 +1,565 @@ +#ifndef LSFS_DISK_CONTROLLER_H +#define LSFS_DISK_CONTROLLER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct struct_partition_control partition_control; +typedef struct struct_global_tag global_tag; +typedef struct struct_partition_control partition_control; +typedef struct File_System_Control_Information FSCI; +typedef struct meta_information_format mif; +typedef struct tag_record tag_record; + +typedef uint64_t lsfs_sector_offset; +typedef lsfs_sector_offset lsfs_file_id; +typedef lsfs_sector_offset lsfs_tag_id; + +//typedef uint64_t sector_index; +static FILE* disk; +static partition_control p_control; // used for debugging + +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_tag_id* tags, void* file_data); +int lsfs_disk_untag_file(lsfs_tag_id tag_id, lsfs_file_id file_id); +int lsfs_disk_tag_file(lsfs_tag_id tag_id, lsfs_file_id file_id); +int lsfs_disk_delete_tag(lsfs_tag_id tag_id); +int lsfs_disk_delete_file(lsfs_file_id file_id); +int get_free_sectors(int num_sectors_needed, lsfs_sector_offset* output_array); +int lsfs_disk_read_data_from_file(lsfs_file_id file_id, int buffer_size, void* buffer_for_data); +int lsfs_disk_write_data_to_file(lsfs_file_id file_id, int data_length, char *data); +int lsfs_disk_rename_tag(lsfs_file_id file_id, char* new_tagname); +int lsfs_disk_rename_file(lsfs_file_id file_id, const char* new_filename); +int lsfs_disk_load_disk(); +int write_data_to_disk(lsfs_sector_offset at_sector, void* data_to_write); +int read_data_from_disk(lsfs_sector_offset index, void* data_buffer); +int write_data_to_disk_off(lsfs_sector_offset index, void* data_to_write, int offset); + + +#define SECTOR_SIZE 4096 +#define DEFAULT_FILE_SIZE 1 // This is in sectors +#define DEFAULT_MASTER_TAG_TABLE_SIZE 3855 // correspond to 1 MiB in sectors 3855 * 57825 +#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 MAX_NUM_ONE_LEVEL_DATA 256 // First Mib of a file. +#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 struct struct_partition_control{ + FSCI* fsci; + global_tag* master_table; +} __attribute__((packed)) partition_control; + +typedef struct File_System_Control_Information { + uint64_t offset_on_disk; + uint64_t next_partition; + uint64_t maximum_sectors_on_partition; + uint64_t maximum_sectors_on_disk; + uint64_t sectors_size_on_disk; + uint64_t next_free_sector; + uint64_t number_of_mtt; + uint64_t master_tag_records[16]; +} __attribute__((packed)) FSCI; + +typedef struct struct_global_tag { + /* SizeOf this = + * tagname + * address + * control_bits + */ + char tagname[256]; + lsfs_tag_id tag_table_index; + struct { + uint64_t is_filename : 1; + } control_bits; +} __attribute__((packed)) global_tag; + + +typedef struct meta_information_format { + char filename[246]; // remeber that the 246 bytes has to be a /0 terminator.. + uint32_t owner_id; + lsfs_tag_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; + /* + * 256 first pointers in direct mapping to data + * 94 next pointers is a pointer + * 94 next pointers to pointers to data + */ + lsfs_sector_offset one_level_pointer_data[MAX_NUM_ONE_LEVEL_DATA]; + lsfs_sector_offset two_level_pointer_data[94]; + lsfs_sector_offset three_level_pointer_data[94]; + +} __attribute__((packed)) mif; + + +typedef struct tag_record { + /* SIZE 16 bytes */ + lsfs_file_id mif_record; + struct { + uint64_t is_filename : 1; + } control_bits; + +} __attribute__((packed)) tag_record; + + +int lsfs_disk_read_data_from_file(lsfs_file_id file_id, int buffer_size, void* buffer_for_data) { + // TODO some offset, to tell where in the file we want to write + mif* mif_record = calloc(1, SECTOR_SIZE); + read_data_from_disk(file_id, mif_record); + int return_val = 0; + for (int i = 0; i < MAX_NUM_ONE_LEVEL_DATA; ++i) { + if(mif_record->one_level_pointer_data[i] == 0) { + break; + } + return_val += read_data_from_disk(mif_record->one_level_pointer_data[i], buffer_for_data + (SECTOR_SIZE * i)); + i++; + } + + time_t current_time; + time ( ¤t_time ); + + mif_record->last_access_date = (uint64_t) current_time; + write_data_to_disk(file_id, mif_record); + free(mif_record); + return return_val; +} + +static inline time_t lsfs_disk_update_timestamps(mif *file) { + return file->last_modification_data = file->last_access_date = time(NULL); +} + +#define lsfs_num_sectors_for_size(x) (((x)+SECTOR_SIZE-1)&~(SECTOR_SIZE-1)) + +int lsfs_disk_write_data_to_file(lsfs_file_id file_id, int data_length, char *data) { + int amount_written = data_length; + + mif mif_record; + read_data_from_disk(file_id, &mif_record); + + lsfs_sector_offset current_sector = mif_record.file_size / SECTOR_SIZE; + unsigned int offset_in_sector = mif_record.file_size % SECTOR_SIZE; + + char *tmp_buffer = malloc(SECTOR_SIZE); + assert(tmp_buffer); + + read_data_from_disk(mif_record.one_level_pointer_data[current_sector], tmp_buffer); + + memcpy(tmp_buffer + offset_in_sector, data, SECTOR_SIZE-offset_in_sector); + data_length -= SECTOR_SIZE-offset_in_sector; + + if (data_length < 0) { + data_length = 0; + } + + for (;;) { + assert(current_sector <= MAX_NUM_ONE_LEVEL_DATA); + write_data_to_disk(mif_record.one_level_pointer_data[current_sector++], tmp_buffer); + if (data_length <= 0) break; + + data += SECTOR_SIZE; + if (data_length >= SECTOR_SIZE) { + memcpy(tmp_buffer, data, SECTOR_SIZE); + data_length -= SECTOR_SIZE; + } + else { + memset(tmp_buffer, 0, SECTOR_SIZE); + memcpy(tmp_buffer, data, data_length); + data_length = 0; + } + } + + amount_written -= data_length; + + free(tmp_buffer); + + lsfs_disk_update_timestamps(&mif_record); + mif_record.file_size += amount_written; // update file size + + write_data_to_disk(file_id, &mif_record); + return amount_written; +} + +time_t lsfs_disk_truncate_file(lsfs_file_id file_id) { + mif file_mif; + read_data_from_disk(file_id, &file_mif); + time_t result = lsfs_disk_update_timestamps(&file_mif); + file_mif.file_size = 0; + write_data_to_disk(file_id, &file_mif); + return result; +} + +int lsfs_disk_rename_tag(lsfs_tag_id tag_id, char* new_tagname) { + for (int i = 0; i < MAX_MASTER_TAGS; ++i) + { + if(p_control.mtt_tags[i].tag_table_index == tag_id) { + memset(p_control.mtt_tags[i].tagname, 0, sizeof(p_control.mtt_tags[i].tagname)); + sprintf(p_control.mtt_tags[i].tagname, "%s", new_tagname); + 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.mtt_tags, 1, sizeof(global_tag) * DEFAULT_MASTER_TAG_TABLE_SIZE, disk); + return 1; +} + +int lsfs_disk_rename_file(lsfs_file_id file_id, const char* new_filename) { + mif* mif_record = calloc(1, SECTOR_SIZE); + read_data_from_disk(file_id, mif_record); + + memset(mif_record->filename, 0, sizeof(mif_record->filename)); + sprintf(mif_record->filename, "%s", new_filename); + + 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); + return 1; +} + +int lsfs_disk_delete_file(lsfs_file_id file_id) { + mif* mif_record = calloc(1, SECTOR_SIZE); + read_data_from_disk(file_id, 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. + + free(mif_record); + return 1; +} + +int lsfs_disk_delete_tag(lsfs_tag_id tag_id) { + tag_record* tag_table = calloc(64, SECTOR_SIZE); + read_data_from_disk(tag_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(tag_id, tag_table[i].mif_record); + } + + + int truncate_table = 0; + + for (int i = 0; i < MAX_MASTER_TAGS; ++i) + { + if(p_control.mtt_tags[i].tag_table_index == tag_id) { + p_control.mtt_tags[i] = p_control.mtt_tags[i+1]; + truncate_table = 1; + printf("Tag deleted from master table - TagID: %lu\n", tag_id); + } + else if (truncate_table) { + p_control.mtt_tags[i] = p_control.mtt_tags[i+1]; + if (p_control.mtt_tags[i+1].tag_table_index == 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.mtt_tags, 1, sizeof(global_tag) * DEFAULT_MASTER_TAG_TABLE_SIZE, disk); + free(tag_table); + return 1; +} + +int lsfs_disk_tag_file(lsfs_tag_id tag_id, lsfs_file_id file_id) { + mif* mif_record = calloc(1, SECTOR_SIZE); + read_data_from_disk(file_id, mif_record); + + for (int i = 0; i < MAX_TAGS_FOR_A_FILE; ++i) + { + if(mif_record->tags[i] == 0) { + mif_record->tags[i] = tag_id; + break; + } + } + write_data_to_disk(file_id, mif_record); + free(mif_record); + + tag_record* tag_table = calloc(64, SECTOR_SIZE); + read_data_from_disk(tag_id, tag_table); + + 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(tag_id, tag_table); + free(tag_table); + return 1; +} + +int lsfs_disk_untag_file(lsfs_tag_id tag_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; + + for (int i = 0; i < MAX_TAGS_FOR_A_FILE; ++i) + { + if(mif_record->tags[i] == tag_id) { + mif_record->tags[i] = mif_record->tags[i+1]; + truncate_table = 1; + printf("file untagged - TagID: %lu - FileID: %lu \n", tag_id, file_id); + } + else if (truncate_table) { + mif_record->tags[i] = mif_record->tags[i+1]; + if (mif_record->tags[i+1] == 0) { + break; + } + } + } + + 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(tag_id, tag_table); + truncate_table = 0; + + for (int i = 0; i < MAX_TAGS_IN_TAG_TABLE; ++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(tag_id, tag_table); + free(tag_table); + return 1; +} + +lsfs_tag_id lsfs_disk_create_tag(char* tag_name, bool is_filename) { + /* Return ID of new tag, oterwise return 0 */ + lsfs_tag_id* free_sectors; + // Returns an array, with the sector numbers that is assignt to you + free_sectors = calloc(DEFAULT_TAG_TABLE_SIZE, sizeof(lsfs_tag_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.mtt_tags[index_in_mtt].tag_table_index != 0) { + //TODO also have to count if we enter next data section. + index_in_mtt++; + } + + + p_control.mtt_tags[index_in_mtt].tag_table_index = free_sectors[0]; + printf("%lu\n", free_sectors[0]); + sprintf(p_control.mtt_tags[index_in_mtt].tagname, "%s", tag_name); + p_control.mtt_tags[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.mtt_tags, 1, sizeof(global_tag) * DEFAULT_MASTER_TAG_TABLE_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, p_control.fsci); + return p_control.fsci->next_free_sector; +} + +int create_file_system() { + //char* sector_to_write; + // make default File System Control information (FSCI) + // first integer says how many pointers we got + // to master tag tables + // Second and forward is the pointers to the master Tag Tables + // we need the first number to allocate memory at one go. + int* zero_buffer; + FSCI fsci; + fsci.offset_on_disk = 1; + fsci.maximum_sectors_on_partition = 1048576; // Max 4GiB + fsci.next_free_sector = 257; + fsci.number_of_mtt = 1; + memset(fsci.master_tag_records, 0, 128); + fsci.master_tag_records[0] = 1; + + fseek ( disk , 0, SEEK_SET ); + fwrite(&fsci, 1, sizeof(fsci), disk); + zero_buffer = calloc(1, (4096 - sizeof(fsci))); + fwrite(zero_buffer, 1, (4096 - sizeof(fsci)), disk); + free(zero_buffer); + /* MASTER TAG TABLE */ + + global_tag mtt_tags[DEFAULT_MASTER_TAG_TABLE_SIZE]; + memset(mtt_tags, 0, (DEFAULT_MASTER_TAG_TABLE_SIZE * sizeof(global_tag))); + + fwrite(&mtt_tags, 1, sizeof(mtt_tags), disk); + zero_buffer = calloc(1, 16); + fwrite(zero_buffer, 1, 16, disk); + free(zero_buffer); + return 0; +} + +int lsfs_disk_load_disk() { + + // First we find the Mater Table. + fseek ( disk , 1 * SECTOR_SIZE, SEEK_SET ); + fread(p_control.mtt_tags, 1, sizeof(global_tag) * DEFAULT_MASTER_TAG_TABLE_SIZE , disk); + + // Now we can finde the FSCI data + fseek ( disk , 0, SEEK_SET ); + fread(p_control.fsci, 1, sizeof(FSCI), disk); + + return 1; +} + + +lsfs_tag_id lsfs_disk_create_file(char* filename, lsfs_tag_id* tags, void* file_data) { + + // create space for mif + mif new_file_data; + lsfs_tag_id return_id; + + + 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_tag_id* index_to_mif_data; + index_to_mif_data = calloc(1, sizeof(lsfs_tag_id)); + get_free_sectors(1, index_to_mif_data); // has to be freed + + memset(new_file_data.tags, 0, (32 * sizeof(lsfs_tag_id))); + + new_file_data.file_size = 0; + + 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); + + 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; +} + +int write_data_to_disk(lsfs_sector_offset index, void* data_to_write) { + fseek ( disk, (index * SECTOR_SIZE), SEEK_SET ); // SEEK_SET start offset at index 0 and move 1 * SECTOR_SIZE, and write here. + int written = fwrite(data_to_write, 1, SECTOR_SIZE, disk); + return written; +} + +int write_data_to_disk_off(lsfs_sector_offset index, void* data_to_write, int offset) { + fseek ( disk, (index * SECTOR_SIZE) + offset, SEEK_SET ); // SEEK_SET start offset at index 0 and move 1 * SECTOR_SIZE, and write here. + int written = fwrite(data_to_write, 1, SECTOR_SIZE, disk); + return written; +} + +int read_data_from_disk(lsfs_sector_offset index, void* data_buffer) { + fseek ( disk, (index * SECTOR_SIZE ), SEEK_SET ); // SEEK_SET start offset at index 0 and move 1 * SECTOR_SIZE, and write here. + int read = fread(data_buffer, 1, SECTOR_SIZE, disk); + return read; +} + +#endif diff --git a/lsfs_fuse b/lsfs_fuse new file mode 100755 index 0000000000000000000000000000000000000000..8e528fe7b99558e1905a2c47332a1161c30b1ed7 GIT binary patch literal 31392 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 literal 0 HcmV?d00001 diff --git a/lsfs_fuse.c b/lsfs_fuse.c new file mode 100644 index 0000000..d77d7b0 --- /dev/null +++ b/lsfs_fuse.c @@ -0,0 +1,308 @@ +/* +TODO(jakob): we have a lot of memory leaks +TODO(jakob): handle multiple files with the same name!!! +*/ + +#include +#include +#include +#include +#include +#include + +#include "lsfs_disk_controller.h" + +int lsfs_getattr( const char *, struct stat * ); +int lsfs_mkdir(const char *path, mode_t mode); +int lsfs_mknod(const char *path, mode_t mode, dev_t device); +int lsfs_open( const char *, struct fuse_file_info * ); +int lsfs_read( const char *, char *, size_t, off_t, struct fuse_file_info * ); +int lsfs_readdir( const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info * ); +int lsfs_release(const char *path, struct fuse_file_info *fi); +int lsfs_rename(const char *from, const char *to); +int lsfs_rmdir(const char *path); +int lsfs_truncate(const char *path, off_t offset); +int lsfs_unlink(const char *); +int lsfs_write(const char *, const char *, size_t, off_t, struct fuse_file_info *); + +static inline int lsfs_utime_STUB(const char *path, struct utimbuf *buf) { + (void)path; + (void)buf; + return 0; +} + +static struct fuse_operations lsfs_oper = { + .getattr = lsfs_getattr, + .readdir = lsfs_readdir, + .mknod = lsfs_mknod, + .mkdir = lsfs_mkdir, + .link = NULL, + .unlink = lsfs_unlink, + .rmdir = lsfs_rmdir, + .truncate = lsfs_truncate, + .open = lsfs_open, + .read = lsfs_read, + .release = lsfs_release, + .write = lsfs_write, + .rename = NULL, + .utime = lsfs_utime_STUB, +}; + +int lsfs_mkdir(const char *path, mode_t mode) { + (void)mode; + + // Call to the disk controller make the dir + return 0; +} + + +int lsfs_rmdir(const char *path) { + + // call to the disk controller to remove a dir + + return 0; +} + +int lsfs_unlink(const char *path) { + + // remove / delete a file + + return 0; +} + +int lsfs_truncate(const char *path, off_t offset) { + (void)offset; + + + //found_file->modification_time = modification_time + + // Truncate a file + + return 0; +} + + +int lsfs_rename(const char *path, const char *to) { + (void)path; + (void)to; + return 0; +} + +int lsfs_getattr( const char *path, struct stat *stbuf ) { + int res = 0; + 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 = 2; + } else if( strcmp( path, "/hello" ) == 0 ) { + stbuf->st_mode = S_IFREG | 0777; + stbuf->st_nlink = 1; + stbuf->st_size = 12; + } 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) { + (void) offset_to_next_entry; + //(void) file_info; + int res; + // printf("read: (path=%s)\n", path); + + time_t current_time; + time ( ¤t_time ); + + //res = lsfs_disk_write_data_to_file(((lsfs_file*) file_info->fh)->file_id, content_length, (void*) content); + + //((lsfs_file*) file_info->fh)->size += res; + //((lsfs_file*) file_info->fh)->access_time = current_time; + //((lsfs_file*) file_info->fh)->modification_time = current_time; + return res; +} + +int lsfs_readdir( const char *path, void *buf, fuse_fill_dir_t filler, off_t offset_to_next_entry, struct fuse_file_info *fi ) { + //(void) offset; + (void) fi; + printf("readdir: (path=%s)\n", path); + + if(strcmp(path, "/") != 0) + return -ENOENT; + + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + filler(buf, "hello", NULL, 0); + + return 0; +} + +//Permission +int lsfs_open( const char *path, struct fuse_file_info *fi ) { + // printf("open: (path=%s)\n", path); + // We can store a pinter in the *fi, this pointer can be used in both read and write. + // https://libfuse.github.io/doxygen/structfuse__operations.html + // printf("read: (path=%s)\n", path); + + /* + 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) { + //(void*) file_data_buffer = calloc(1, SECTOR_SIZE); + fi->fh = (uint64_t) found_file; + //lsfs_disk_read_data_from_file(found_file->file_id, (256*4096), buf); + } + } + } + } + + lsfs_destroy_string_array(split_path); + */ + return 0; +} + +int lsfs_read( const char *path, char *buf, size_t size, off_t offset_to_next_entry, struct fuse_file_info *fi ) { + // printf("read: (path=%s)\n", path); + + time_t current_time; + time ( ¤t_time ); + + //int res = lsfs_disk_read_data_from_file( ((lsfs_file*) fi->fh)->file_id, size, buf); + //((lsfs_file*) fi->fh)->access_time = current_time; + return 0; +} + +int lsfs_release(const char *path, struct fuse_file_info *fi) { + // printf("release: (path=%s)\n", path); + return 0; +} + +int lsfs_mknod(const char *path, mode_t mode, dev_t device) { + /*(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;*/ +} + +int main( int argc, char *argv[] ) { + + disk = fopen ( "~/Documents/github/SingOS/bin/SingOS.img" , "r+b" ); + p_control.fsci = malloc(sizeof(FSCI)); + p_control.master_table = malloc(sizeof(global_tag) * DEFAULT_MASTER_TAG_TABLE_SIZE); + lsfs_disk_load_disk(disk, &p_control); + + //tag_record *tag_table = calloc(1, SECTOR_SIZE); + //mif* mif_data = calloc(1, SECTOR_SIZE); + + //free(mif_data); + //free(tag_table); + //free(seen_file_ids); + + return fuse_main( argc, argv, &lsfs_oper ); +} diff --git a/lsfs_fuse.o b/lsfs_fuse.o new file mode 100644 index 0000000000000000000000000000000000000000..779d9cf2dcbbe472e189d70283b942eaae95336a GIT binary patch literal 40104 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

elZL#=&HbG;#Yj5WX^dL4QBnkq!q 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