diff --git a/config.h b/config.h index 3a1d245..e611c00 100644 --- a/config.h +++ b/config.h @@ -20,7 +20,8 @@ /* FS config section */ #define FS_MAX_DEVICE_FILE_NAME_LEN 512 #define FS_BLOCK_SIZE 4096 -#define FS_MAX_BITMAP_SIZE 256 +#define FS_MAX_BITMAP_SIZE 64 +#define FS_MAX_PATH_LEN 512 #endif diff --git a/inc/fs.h b/inc/fs.h index 86cf1a7..f5ef234 100644 --- a/inc/fs.h +++ b/inc/fs.h @@ -14,6 +14,7 @@ struct fs_header { unsigned int inode_ptrs[(FS_BLOCK_SIZE-sizeof(int)*3) / sizeof(int)]; }; +__attribute__((packed)) struct fs_header_extension { unsigned int next_extension; unsigned int inode_ptrs[(FS_BLOCK_SIZE-sizeof(int)) / sizeof(int)]; @@ -28,11 +29,20 @@ struct fs_inode { unsigned int blocks[(FS_BLOCK_SIZE-sizeof(int)*3) / sizeof(int)]; }; +__attribute__((packed)) struct fs_inode_extension { unsigned int next_extension; unsigned int blocks[(FS_BLOCK_SIZE-sizeof(int)) / sizeof(int)]; }; +__attribute__((packed)) +struct fs_directory_block_data { + unsigned char fname[60]; + unsigned int inode_no; +}; + +char *fs_get_cwd(void); + int fs_create(void *d); int fs_use(void *d); int fs_mkfs(void *d); diff --git a/src/cli.c b/src/cli.c index 1820948..5d2d606 100644 --- a/src/cli.c +++ b/src/cli.c @@ -190,7 +190,13 @@ abandon_struct: unsigned int cli_poll_process_next(void) { for ( ;; ) { - fputs("$ ", stdout); + fputs(fs_get_cwd(), stdout); + + if (fs_get_cwd()[0]) { + fputs(" $ ", stdout); + } else { + fputs("$ ", stdout); + } size_t buf_size = 0; char *line = NULL; diff --git a/src/fs.c b/src/fs.c index b7b428a..1f6a769 100644 --- a/src/fs.c +++ b/src/fs.c @@ -8,7 +8,7 @@ #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 = (FS_BLOCK_SIZE-sizeof(int)*3) / sizeof(int); const static int BLOCK_ADDRESSES_PER_INODE_EXTENSION = (FS_BLOCK_SIZE-sizeof(int)) / sizeof(int); @@ -16,6 +16,10 @@ static char used_file_path[FS_MAX_DEVICE_FILE_NAME_LEN+1]; static int used_file_fd; static int write_permitted; +static char fs_cwd[FS_MAX_PATH_LEN+1]; +static struct fs_header fsh_cache; +static char fs_bitmap_cache[FS_BLOCK_SIZE * FS_MAX_BITMAP_SIZE]; + static int read_block(unsigned int block_no, void *data) { @@ -37,33 +41,151 @@ static int write_block(unsigned int block_no, void *data) return write(used_file_fd, data, FS_BLOCK_SIZE); } +static void mark_used(unsigned int block_no) +{ + if (block_no > fsh_cache.block_count) { + pr_err("block %d is out of filesystem block range (%d)\n", block_no, fsh_cache.block_count); + return; + } + + unsigned char bitmap_bit = 1 << (block_no & 0x7); + unsigned int bitmap_block_offset = (block_no >> 3) % FS_BLOCK_SIZE; + unsigned int bitmap_block_index = (block_no >> 3) / FS_BLOCK_SIZE; + + fs_bitmap_cache[bitmap_block_index * FS_BLOCK_SIZE + bitmap_block_offset] |= bitmap_bit; + + // write changes to device + write_block(bitmap_block_index+1, (void *) &(fs_bitmap_cache[bitmap_block_index*FS_BLOCK_SIZE])); + + pr("Marked block_no=%d (block=%d, offset=%d, bit=%d) as used\n", + block_no, bitmap_block_index, bitmap_block_offset, block_no & 0x7); +} + +static void mark_free(unsigned int block_no) +{ + if (block_no > fsh_cache.block_count) { + pr_err("block %d is out of fimesystem block range (%d)\n", block_no, fsh_cache.block_count); + return; + } + + unsigned char bitmap_bit = 1 << (block_no & 0x7); + unsigned int bitmap_block_offset = (block_no >> 3) % FS_BLOCK_SIZE; + unsigned int bitmap_block_index = (block_no >> 3) / FS_BLOCK_SIZE; + + fs_bitmap_cache[bitmap_block_index * FS_BLOCK_SIZE + bitmap_block_offset] &= ~bitmap_bit; + + // write changes to device + write_block(bitmap_block_index+1, (void *) &(fs_bitmap_cache[bitmap_block_index*FS_BLOCK_SIZE])); + + pr("Marked block_no=%d (block=%d, offset=%d, bit=%d) as free\n", + block_no, bitmap_block_index, bitmap_block_offset, bitmap_bit); +} 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]; + struct fs_header read_buf; { - int read_amount = read(used_file_fd, read_buf, FS_BLOCK_SIZE); + int read_amount = read_block(0, (void *) &read_buf); if (read_amount < 0) { - pr_err("failed to read from storage device\n"); + pr_err("failed to read fs_header from storage device '%s'\n", used_file_path); 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) { + if (read_amount < FS_BLOCK_SIZE) { + pr_warn("failed to read full block (read %d/%d bytes)\n", read_amount, FS_BLOCK_SIZE); + } else if (read_amount == 0) { pr_err("storage device size is 0\n"); return 0; } } - return read_buf[0]; + if (read_buf.next_extension) { + pr_info("identified filesystem version 0x%hhx with %d max inodes (on %d blocks), next header extension is at block 0x%x\n", + read_buf.version, read_buf.max_inode_count, read_buf.block_count, read_buf.next_extension); + } else { + pr_info("identified filesystem version 0x%hhx with %d max inodes (on %d blocks), with no header extensions\n", + read_buf.version, read_buf.max_inode_count, read_buf.block_count); + } + + return read_buf.version; +} + +static unsigned int find_free_block(void) +{ + unsigned int b = 0; + + int blocks_used_for_bitmap = fsh_cache.block_count / (FS_BLOCK_SIZE * 8); + if (fsh_cache.block_count % (FS_BLOCK_SIZE * 8)) + blocks_used_for_bitmap++; + + for (int i = 0; i < blocks_used_for_bitmap; i++) { + for (int j = 0; j < FS_BLOCK_SIZE; j++) { + if (!(~(fs_bitmap_cache[i*FS_BLOCK_SIZE + j]))) { + b += 8; + } else { + for (int k = 0; k < 8; k++, b++) { + if (!((fs_bitmap_cache[i*FS_BLOCK_SIZE + j]) & (1 << k))) { + return b; + } + } + } + } + } + + return 0; +} + +static void write_inode_ptr(unsigned int inode_ptr, unsigned int block_ptr) +{ + if ((inode_ptr / BLOCK_ADDRESSES_PER_INODE) == 0) { + // inode_ptr is in the fs_header + struct fs_header fsh; + int read_result = read_block(0, &fsh); + if (FS_BLOCK_SIZE != read_result) { + if (read_result < 0) { + pr_err("failed to read fs header from device '%s'\n", used_file_path); + } else { + pr_err("failed to read full header from device (read %d/%d bytes)\n", read_result, FS_BLOCK_SIZE); + } + + return; + } + + fsh.inode_ptrs[inode_ptr] = block_ptr; + + int write_result = write_block(0, (void *) &fsh); + if (FS_BLOCK_SIZE != write_result) { + if (write_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", write_result, FS_BLOCK_SIZE); + } + + return; + } + + pr("Updated inode ptr %d -> %d\n", inode_ptr, block_ptr); + } else { + // TODO: find block with relevant inode_ptr, extend fs_header if needed + /* + unsigned int relevant_block_index = ((free_block_index - BLOCK_ADDRESSES_PER_INODE) / BLOCK_ADDRESSES_PER_INODE_EXTENSION) + 1; + unsigned int relevant_block_record_offset = (free_block_index - BLOCK_ADDRESSES_PER_INODE) % BLOCK_ADDRESSES_PER_INODE_EXTENSION; + */ + } +} + +char *fs_get_cwd(void) +{ + return fs_cwd; +} + +int fs_chdir(void *d) +{ + memset(fs_cwd, 0, sizeof(fs_cwd)); + strcpy(fs_cwd, *((char**)d)); + + return 0; } int fs_allow_write(void *d) @@ -75,6 +197,8 @@ int fs_allow_write(void *d) pr_info("Allowing write operations on device '%s'\n", used_file_path); write_permitted = 1; + + return 0; } int fs_prohibit_write(void *d) @@ -86,6 +210,8 @@ int fs_prohibit_write(void *d) pr_info("Prohibiting write operations on device '%s'\n", used_file_path); write_permitted = 0; + + return 0; } int fs_create(void *d) @@ -131,6 +257,55 @@ int fs_use(void *d) write_permitted = 1; } + if (write_permitted) { + { + int bytes_read = read_block(0, (void *) &fsh_cache); + if (bytes_read < 0) { + pr_err("failed to cache filesystem header\n"); + write_permitted = 0; + return 0; + } else if (bytes_read < FS_BLOCK_SIZE) { + pr_err("failed to read full filesystem header (read %d/%d bytes)\n", bytes_read, FS_BLOCK_SIZE); + write_permitted = 0; + return 0; + } else { + pr("Cached filesystem header\n"); + } + } + + int blocks_used_for_bitmap = fsh_cache.block_count / (FS_BLOCK_SIZE * 8); + if (fsh_cache.block_count % (FS_BLOCK_SIZE * 8)) + blocks_used_for_bitmap++; + + if (blocks_used_for_bitmap > FS_MAX_BITMAP_SIZE) { + pr_err("filesystem bitmap too large (%d blocks > %d)\n", blocks_used_for_bitmap, FS_MAX_BITMAP_SIZE); + write_permitted = 0; + return 0; + } + + { + for (int i = 0; i < blocks_used_for_bitmap; i++) { + int bytes_read = read_block(i+1, &(fs_bitmap_cache[FS_BLOCK_SIZE*i])); + + if (bytes_read < 0) { + pr_err("failed to cache filesystem bitmap block %d/%d\n", i+1, blocks_used_for_bitmap); + write_permitted = 0; + return 0; + } else if (bytes_read < FS_BLOCK_SIZE) { + pr_err("failed to read full filesystem bitmap block %d/%d (read %d/%d bytes)\n", + i+1, blocks_used_for_bitmap, bytes_read, FS_BLOCK_SIZE); + write_permitted = 0; + return 0; + } else { + pr("Cached filesystem bitmap block %d/%d\n", i+1, blocks_used_for_bitmap); + } + } + } + + char *root_dir_path = "/"; + fs_chdir((void *) &root_dir_path); + } + return 0; } @@ -148,6 +323,11 @@ int fs_mkfs(void *d) int max_inode_count = *((int *)d); + if (max_inode_count <= 0) { + pr_err("max inode count must be positive (got %d)\n", max_inode_count); + return 0; + } + struct stat st; if (fstat(used_file_fd, &st) < 0) { pr_err("could not stat device '%s'\n", used_file_path); @@ -184,12 +364,83 @@ int fs_mkfs(void *d) return 0; } - unsigned char bitmap[blocks_used_for_bitmap * FS_BLOCK_SIZE]; - memset(bitmap, 0, blocks_used_for_bitmap * FS_BLOCK_SIZE); + // update fsh cache + memcpy(&fsh_cache, &fsh, FS_BLOCK_SIZE); + + // clear fs_bitmap_cache + memset(&fs_bitmap_cache, 0, sizeof(fs_bitmap_cache)); int blocks_used = 1 + blocks_used_for_bitmap; - //for (int i = 0; i < FS_MAX_BITMAP_SIZE; i++) + unsigned char bitmap_block[FS_BLOCK_SIZE]; + unsigned int j = 0; + + for (int i = 0; i < blocks_used_for_bitmap; i++) { + memset(bitmap_block, 0, FS_BLOCK_SIZE); + for (int k = 0; k < FS_BLOCK_SIZE; k++) { + for (int t = 0; t < 8; t++, j++) { + if (j == blocks_used) + goto finish_current_block; + + bitmap_block[i] |= (1 << t); + } + } + +finish_current_block: + { + int bytes_written = write_block(i+1, (void *) bitmap_block); + + if (bytes_written < 0) { + pr_err("failed to write bitmap block %d/%d on device '%s'\n", + i+1, blocks_used_for_bitmap, used_file_path); + return 0; + } else if (bytes_written < FS_BLOCK_SIZE) { + pr_err("failed to write full bitmap block %d/%d on device '%s' (written %d/%d bytes)\n", + i+1, blocks_used_for_bitmap, used_file_path, bytes_written, FS_BLOCK_SIZE); + return 0; + } else { + pr("Written bitmap block %d/%d on device '%s'\n", i+1, blocks_used_for_bitmap, used_file_path); + } + + // update fs_bitmap_cache + memcpy(&(fs_bitmap_cache[FS_BLOCK_SIZE*i]), &bitmap_block, FS_BLOCK_SIZE); + } + } + + + // create root directory automatically + + struct fs_inode root_dir = {}; + root_dir.ftype = DIRECTORY; + root_dir.ref_count = 1; + + unsigned int free_block_index = find_free_block(); + + if (!free_block_index) { + pr_err("failed to find free block for root directory\n"); + return 0; + } + + { + int bytes_written = write_block(free_block_index, (void *) &root_dir); + if (bytes_written < 0) { + pr_err("failed to write root directory block on device '%s'\n", used_file_path); + return 0; + } else if (bytes_written < FS_BLOCK_SIZE) { + pr_err("failed to write full root directory block on device '%s' (written %d/%d bytes)\n", + used_file_path, bytes_written, FS_BLOCK_SIZE); + return 0; + } else { + pr("Written root directory block on device '%s'\n", used_file_path); + } + } + mark_used(free_block_index); + + // inode0 -> root_dir_block + write_inode_ptr(0, free_block_index); + + char *root_dir_path = "/"; + fs_chdir((void *) &root_dir_path); return 0; }