#ifndef LSFS_DISK_CONTROLLER_H #define LSFS_DISK_CONTROLLER_H #include #include #include #include #include #include #include #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; 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 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; //typedef uint64_t sector_index; static FILE* disk; static partition_control p_control; static time_t timestamp_loading; int create_file_system(char* disk_name, char* hdd_or_partition, uint64_t filesystem_size_in_MB); int lsfs_disk_create_entry(const char* path, Table_Entry_Kind entry_kind); Directory_Table* lsfs_find_directory(const char* path, bool drop_filename); int lsfs_disk_getattr(lsfs_file* find_file, const char *path); int lsfs_disk_delete_file(lsfs_file *file); 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, size_t offset_to_next_entry); int lsfs_disk_rename_file(const char* old_filename_, 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); int read_data_from_disk(lsfs_sector_offset index, uint32_t file_block_size, void* data_buffer); int write_data_to_disk_off(lsfs_sector_offset index, uint32_t file_block_size, void* data_to_write, int offset); int save_modified_file_information(lsfs_file* file); #define SPACE_MBR_RECORD 2048 // Sectors #define SPACE_VBR_RECORD 2048 // Sectors #define SIZE_FSCI_RECORD 1 // Sectors #define DEFAULT_ENTRY_SIZE 1 // Sectors #define SECTOR_SIZE 512 // BYTES #define NUMBER_OF_MBR_PARTITIONS 4 #define DEFAULT_FILE_SIZE 4 // This is in sectors #define DEFAULT_DATA_POINTER_SIZE 4 // This is in sectors #define DEFAULT_TABLE_SIZE 16 #define NUM_DATA_POINTERS 27 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 { uint8_t active_falg; // This has value 0x80 if it is a bootable partition / it is an active partition. uint8_t CHS_start_addr[3]; // [0] = H, [1] = S, [2] = C uint8_t partition_type; // This has a value such that one can idenfity which file system the partition is. uint8_t CHS_last_addr[3]; // [0] = H, [1] = S, [2] = C uint32_t LBA_abs_first_sector; uint32_t number_of_sectors; } __attribute__((packed)) Partition_Entry; typedef struct Master_Boot_record { uint8_t offset_on_disk[446]; // The code for the bootloader Partition_Entry partitions[4]; uint16_t mbr_signature; // Signature } __attribute__((packed)) Master_Boot_record; typedef struct struct_table_entry { char filename[256]; lsfs_file_id file_id; uint64_t file_size; mif* ext_file_data; uint32_t file_block_size; // This tells how many block there are allocated for the specific file. eg. we read this amount of bloks for the file. uint8_t entry_kind; uint8_t extra_control_bits1; uint8_t extra_control_bits2; uint8_t extra_control_bits3; lsfs_sector_offset table_entry_sector_index; lsfs_sector_offset data_pointer[NUM_DATA_POINTERS]; // if it is a directory, the first pointer will be to the next table. } __attribute__((packed)) Table_Entry; typedef struct Directory_Table { struct_table_entry entries[DEFAULT_TABLE_SIZE]; } __attribute__((packed)) Directory_Table; typedef struct File_System_Control_Information { char filesystem_information[256]; lsfs_sector_offset master_table_index; lsfs_sector_offset this_partition_offset_on_disk; lsfs_sector_offset next_free_sector; uint64_t next_uniqe_id; // both files and directories gets this. lsfs_sector_offset next_sector_reuse_pointer; lsfs_sector_offset last_sector_index_on_partition; lsfs_sector_offset maximum_sectors_on_disk; lsfs_sector_offset sector_size_on_disk; uint64_t not_used[24]; } __attribute__((packed)) FSCI; typedef struct struct_partition_control { FSCI fsci; Directory_Table master_table; } __attribute__((packed)) 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; /* 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[NUM_DATA_POINTERS]; 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; 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; uint64_t creation_date; uint64_t access_time; uint64_t modification_time; uint32_t file_block_size; lsfs_sector_offset table_entry_sector_index; lsfs_sector_offset data_pointer[NUM_DATA_POINTERS]; } lsfs_file; Directory_Table* lsfs_find_directory(const char *path, bool drop_filename) { Directory_Table *dir_table = calloc(1, sizeof(Directory_Table)); read_data_from_disk(p_control.fsci.master_table_index, DEFAULT_TABLE_SIZE, dir_table); lsfs_string_array split_path = lsfs_string_split_c(path, '/', false); int number_of_traversal = split_path.length; if (drop_filename) { number_of_traversal -= 1; } // Start from the master table for (int i = 0; i < number_of_traversal; ++i) { for (int j = 0; j < DEFAULT_TABLE_SIZE; ++j) { if (strcmp(dir_table->entries[j].filename, split_path.strings[i].chars) == 0) { int index_sector = dir_table->entries[j].data_pointer[0]; read_data_from_disk(index_sector, DEFAULT_TABLE_SIZE, dir_table); break; } } } return dir_table; } int lsfs_disk_getattr(lsfs_file* find_file, const char* path) { 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 Directory_Table *dir_table = lsfs_find_directory(path, true); for (int i = 0; i < DEFAULT_TABLE_SIZE; ++i) { if(strcmp( filename.chars, dir_table->entries[i].filename ) == 0) { find_file->file_id = dir_table->entries[i].file_id; find_file->entry_kind = dir_table->entries[i].entry_kind; find_file->table_entry_pointer = i; find_file->filename = dir_table->entries[i].filename; find_file->table_entry_sector_index = dir_table->entries[i].table_entry_sector_index; find_file->owner_id = getuid(); find_file->size = dir_table->entries[i].file_size; // dir_table->entries[i].data_pointer[0]; //; find_file->creation_date = (uint64_t) timestamp_loading; find_file->access_time = (uint64_t) timestamp_loading; find_file->modification_time = (uint64_t) timestamp_loading; memcpy(find_file->data_pointer, dir_table->entries[i].data_pointer, NUM_DATA_POINTERS * 8); find_file->file_block_size = 1; // TODO: should be loaded from disk. return 1; } } return 0; } int lsfs_disk_read_data_from_file(lsfs_file *file, int buffer_size, void* buffer_for_data) { // TODO some offset, to tell where in the file we want to write int return_val = 0; for (int i = 0; i < NUM_DATA_POINTERS; ++i) { if(file->data_pointer[i] == 0) { break; } return_val += read_data_from_disk(file->data_pointer[i], file->file_block_size, buffer_for_data + (SECTOR_SIZE * 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(lsfs_file *file) { return file->modification_time = file->access_time = 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 *file, int data_length, char *data, size_t offset_to_next_entry) { int new_filesize = data_length + offset_to_next_entry; int amount_written = data_length; //printf("Data length: %d\n", data_length); //printf("Offset length: %d\n", offset_to_next_entry); int data_pointer_index = 0; // start at first data pointer. while(data_length > 0) // We have more to write { if (file->data_pointer[data_pointer_index] == 0) { // we have to assign a free sector get_free_sectors(1, (lsfs_sector_offset*) file->data_pointer[data_pointer_index]); } if (offset_to_next_entry == 0) { char *tmp_buffer = calloc(DEFAULT_DATA_POINTER_SIZE, SECTOR_SIZE); assert(tmp_buffer); memcpy(tmp_buffer, data, data_length); data_length -= (DEFAULT_DATA_POINTER_SIZE * SECTOR_SIZE); write_data_to_disk(file->data_pointer[data_pointer_index], DEFAULT_DATA_POINTER_SIZE, tmp_buffer); data_pointer_index++; free(tmp_buffer); } else if (offset_to_next_entry <= (DEFAULT_DATA_POINTER_SIZE * SECTOR_SIZE)) { char *tmp_buffer = calloc(1, ((DEFAULT_DATA_POINTER_SIZE * SECTOR_SIZE) - offset_to_next_entry)); assert(tmp_buffer); memcpy(tmp_buffer, data, data_length); data_length -= ((DEFAULT_DATA_POINTER_SIZE * SECTOR_SIZE) - offset_to_next_entry); write_data_to_disk_off(file->data_pointer[data_pointer_index], DEFAULT_DATA_POINTER_SIZE, tmp_buffer, offset_to_next_entry); data_pointer_index++; free(tmp_buffer); } else { // We have to skip a whole data pointer: offset_to_next_entry -= (DEFAULT_DATA_POINTER_SIZE * SECTOR_SIZE); data_pointer_index++; } } time_t current_time; time ( ¤t_time ); //lsfs_disk_update_timestamps(&mif_record); file->size = new_filesize; // update file size file->access_time = current_time; file->modification_time = current_time; save_modified_file_information(file); //write_data_to_disk(file->file_id, 4, &p_control.master_table[file->file_id]); // Should return the total new file size return amount_written; } time_t lsfs_disk_truncate_file(lsfs_file *file, off_t offset) { //mif file_mif; //read_data_from_disk(file_id, &file_mif); time_t result = lsfs_disk_update_timestamps(file); file->size = (int) offset; // p_control.master_table.entries[i].data_pointer[0]; //; save_modified_file_information(file); //write_data_to_disk(file->file_id, 4, NULL); return result; } int lsfs_disk_rename_file(const char* old_filename, const char* new_filename) { lsfs_file *old_file = calloc(1, sizeof(lsfs_file)); lsfs_file *new_file = calloc(1, sizeof(lsfs_file)); lsfs_disk_getattr(old_file, old_filename); lsfs_disk_create_entry(new_filename, ENTRY_FILE); lsfs_disk_getattr(new_file, new_filename); new_file->file_id = old_file->file_id; new_file->size = old_file->size; // TODO(Jørn) The data pointer assignt to the new file should be released. memcpy(new_file->data_pointer, old_file->data_pointer, NUM_DATA_POINTERS * 8); save_modified_file_information(new_file); lsfs_disk_delete_file(old_file); return 0; } int lsfs_disk_delete_file(lsfs_file *file) { Table_Entry *zero_buffer = calloc(1, (DEFAULT_ENTRY_SIZE * SECTOR_SIZE)); //read_data_from_disk(file_id, 1, mif_record); write_data_to_disk(file->table_entry_sector_index, DEFAULT_ENTRY_SIZE, zero_buffer); free(zero_buffer); return 1; } 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; if ((p_control.fsci.next_free_sector + DEFAULT_TABLE_SIZE) > p_control.fsci.last_sector_index_on_partition) { // We don't have space, report error return -EINVAL; } p_control.fsci.next_free_sector += DEFAULT_TABLE_SIZE; fseek ( disk , ((p_control.fsci.this_partition_offset_on_disk) * SECTOR_SIZE), SEEK_SET ); fwrite(&p_control.fsci, 1, SECTOR_SIZE, disk); printf("Table has got assigned Sector: %d\n", return_index); return return_index; } int get_free_sectors(int num_sectors_needed, lsfs_sector_offset* output_array) { if ((p_control.fsci.next_free_sector + num_sectors_needed) > p_control.fsci.last_sector_index_on_partition ) { // We cannot assign what we want. return -EINVAL; } for (int i = 0; i < num_sectors_needed; ++i) { output_array[i] = p_control.fsci.next_free_sector; p_control.fsci.next_free_sector += DEFAULT_FILE_SIZE; } fseek ( disk , (p_control.fsci.this_partition_offset_on_disk) * SECTOR_SIZE, SEEK_SET ); fwrite(&p_control.fsci, 1, SECTOR_SIZE, disk); return 0; } int create_file_system(char* disk_name, char* hdd_or_partition, uint64_t filesystem_size_in_MB) { //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. FSCI *fsci = calloc(1, sizeof(FSCI)); // Create disk on host system: disk = fopen ( disk_name , "wb" ); ftruncate(fileno(disk), (filesystem_size_in_MB * 2048 * 512)); if (hdd_or_partition[0] == '1') { // This is the create hdd case // This means that we setup the partition table. Master_Boot_record *mbr = calloc(1, sizeof(Master_Boot_record)); mbr->partitions[0].partition_type = 0x18; mbr->partitions[0].LBA_abs_first_sector = 2048; mbr->partitions[0].number_of_sectors = filesystem_size_in_MB * 2048; mbr->mbr_signature = 0xaa55; write_data_to_disk(0, 1, mbr); // Write this to the first sector of the disk. } if ((hdd_or_partition[0] == '1') || (hdd_or_partition[0] == '2')) { printf("HEY YUP\n"); // This is just a single partition // And then the file system is the only thing in the system. sprintf(fsci->filesystem_information, "LSFS v1.0.0-a1\n(LessSimpelFileSystem)(Generated by the disk_manager_utility.c)\nDeveloped to SingOS\nby Jorn Guldberg\n"); if (hdd_or_partition[0] == '1') { fsci->this_partition_offset_on_disk = SPACE_MBR_RECORD + SPACE_VBR_RECORD; } else { fsci->this_partition_offset_on_disk = SPACE_VBR_RECORD; } fsci->master_table_index = fsci->this_partition_offset_on_disk + SIZE_FSCI_RECORD; fsci->next_free_sector = fsci->master_table_index + DEFAULT_TABLE_SIZE; fsci->next_uniqe_id = 1; fsci->next_sector_reuse_pointer = 0; fsci->last_sector_index_on_partition = filesystem_size_in_MB * 2048; // Todo, this is the ssectors pr MB, this should not be hardcoded. fsci->maximum_sectors_on_disk = filesystem_size_in_MB * 2048; //TODO(Jørn) Not in use yet fsci->sector_size_on_disk = SECTOR_SIZE; } else { // This is an error case, and we should not hit this case. assert(NULL); } write_data_to_disk(fsci->this_partition_offset_on_disk, 1, fsci); return 0; } int lsfs_disk_load_disk() { // Find the partition talbe: // This makes is BIOS dependent. // UEFI is not supported. time(×tamp_loading); Master_Boot_record mbr; fseek( disk , 0 * SECTOR_SIZE, SEEK_SET ); fread(&mbr, 1, sizeof(mbr), disk); if (mbr.mbr_signature != 0xaa55 ) { // Means that it is a sigle partition we try to mount fseek(disk, (SPACE_VBR_RECORD * 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, (p_control.fsci.master_table_index * SECTOR_SIZE), SEEK_SET ); fread(&p_control.master_table, 1, DEFAULT_TABLE_SIZE * SECTOR_SIZE , disk); return 1; } else { for (int i = 0; i < NUMBER_OF_MBR_PARTITIONS; ++i) { // TODO (Jørn) We maybe wnat to optimize, such that we can detect if we have more than one partition opn the system. if (mbr.partitions[i].partition_type == 0x18) { printf("%d\n", mbr.partitions[i].LBA_abs_first_sector); // First we find the File system control information. fseek(disk , ((mbr.partitions[i].LBA_abs_first_sector + SPACE_VBR_RECORD) * 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, (p_control.fsci.master_table_index * SECTOR_SIZE), SEEK_SET ); fread(&p_control.master_table, 1, DEFAULT_TABLE_SIZE * SECTOR_SIZE , disk); return 1; } } } return 0; } int lsfs_disk_create_entry(const char* path, Table_Entry_Kind entry_kind) { // First check if file exist: lsfs_file *file = calloc(1, sizeof(lsfs_file)); if (lsfs_disk_getattr(file, path)) { return -EINVAL; } free(file); // Start from the master table int free_index = -1; // -1 is no index found. Directory_Table *dir_table = calloc(1, sizeof(Directory_Table)); read_data_from_disk(p_control.fsci.master_table_index, DEFAULT_TABLE_SIZE, dir_table); lsfs_sector_offset table_disk_position = p_control.fsci.master_table_index; lsfs_string_array split_path = lsfs_string_split_c(path, '/', false); lsfs_string filename = split_path.strings[split_path.length-1]; //printf("spilt length: %d\n", split_path.length); for (int i = 0; i < split_path.length -1; ++i) { for (int j = 0; j < DEFAULT_TABLE_SIZE; ++j) { if (strcmp(dir_table->entries[j].filename, split_path.strings[i].chars) == 0) { // We have found the next directory to traverse. //printf("Get next dir at sector: "); table_disk_position = dir_table->entries[j].data_pointer[0]; //printf("%d\n", table_disk_position); read_data_from_disk(table_disk_position, DEFAULT_TABLE_SIZE, dir_table); break; } } } for (int table_index = 0; table_index < DEFAULT_TABLE_SIZE; ++table_index) { // Find free index. if (dir_table->entries[table_index].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", table_index); table_disk_position += table_index; // Abselout index in file system free_index = table_index; } } } if (free_index == -1) { // The table is full, and we cannot create an entry return -EINVAL; } // 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; dir_table->entries[free_index].table_entry_sector_index = table_disk_position; if (entry_kind == ENTRY_DIRECTORY) { dir_table->entries[free_index].data_pointer[0] = get_free_sectors_table(); dir_table->entries[free_index].file_size = DEFAULT_TABLE_SIZE * SECTOR_SIZE; } else if (entry_kind == ENTRY_FILE) { // We assign one data pointer consiting of DEFAULT_FILE_SIZE sectors dir_table->entries[free_index].file_size = 0; get_free_sectors(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 ); */ //printf("File is written to sector: %d\n", table_disk_position); write_data_to_disk(table_disk_position, DEFAULT_ENTRY_SIZE, &dir_table->entries[free_index]); return 0; } int save_modified_file_information(lsfs_file* file) { // Write the file struct into the table_entry, such that we can save the data correct. Table_Entry *entry = calloc(1, sizeof(Table_Entry)); read_data_from_disk(file->table_entry_sector_index, DEFAULT_ENTRY_SIZE, entry); //entry.file_id = file->file_id; memcpy(entry->filename, file->filename, 256); entry->file_size = file->size; // p_control.master_table.entries[i].data_pointer[0]; //; memcpy(entry->data_pointer, file->data_pointer, NUM_DATA_POINTERS * 8); write_data_to_disk(file->table_entry_sector_index, DEFAULT_ENTRY_SIZE, entry); return 0; } int write_data_to_disk(lsfs_sector_offset index, uint32_t file_block_size, 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, (file_block_size * SECTOR_SIZE), disk); return written; } int write_data_to_disk_off(lsfs_sector_offset index, uint32_t file_block_size, 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, ((file_block_size * SECTOR_SIZE) - offset), disk); return written; } int read_data_from_disk(lsfs_sector_offset index, uint32_t file_block_size, 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, (file_block_size * SECTOR_SIZE), disk); return read; } #endif