#include #include #include #include #include "print.h" #include "fs.h" #include "config.h" 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); 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) { if (lseek(used_file_fd, block_no * FS_BLOCK_SIZE, SEEK_SET) < 0) { pr_err("failed to seek to block %d (bs=%d) on device '%s'\n", block_no, FS_BLOCK_SIZE, used_file_path); return -1; } return read(used_file_fd, data, FS_BLOCK_SIZE); } static int write_block(unsigned int block_no, void *data) { if (lseek(used_file_fd, block_no * FS_BLOCK_SIZE, SEEK_SET) < 0) { pr_err("failed to seek to block %d (bs=%d) on device '%s'\n", block_no, FS_BLOCK_SIZE, used_file_path); return -1; } return write(used_file_fd, data, FS_BLOCK_SIZE); } static 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) { struct fs_header read_buf; { int read_amount = read_block(0, (void *) &read_buf); if (read_amount < 0) { 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 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; } } 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) { if (used_file_fd <= 0) { pr_err("no device present\n"); return 0; } pr_info("Allowing write operations on device '%s'\n", used_file_path); write_permitted = 1; return 0; } int fs_prohibit_write(void *d) { if (used_file_fd <= 0) { pr_err("no device present\n"); return 0; } pr_info("Prohibiting write operations on device '%s'\n", used_file_path); write_permitted = 0; return 0; } int fs_create(void *d) { pr("[mock] Regular file '%s' created\n", *((char **) d)); return 0; } int fs_use(void *d) { char *fname = *((char **) d); int name_len = strlen(fname); if (name_len > FS_MAX_DEVICE_FILE_NAME_LEN) { pr_err("device filename too long (> %d)\n", FS_MAX_DEVICE_FILE_NAME_LEN); return 0; } pr("Using file '%s' as storage device\n", fname); strcpy(used_file_path, fname); if (used_file_fd > 0) close(used_file_fd); used_file_fd = open(fname, O_RDWR); if (used_file_fd < 0) { pr_err("failed to open filename '%s'\n", fname); return 0; } int fs_version = identify_fs(); if (!fs_version) { pr_info("filesystem could not be identified on device '%s'\n", fname); write_permitted = 0; } else if (fs_version != 1) { pr_warn("filesystem is corrupted or has unsupported version (0x%hhx)\n", fs_version); write_permitted = 0; } else if (fs_version == 1) { pr_info("filesystem v1 has been identified on device '%s'\n", fname); write_permitted = 1; } 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; } int fs_mkfs(void *d) { if (!write_permitted) { pr_err("device '%s' is write-protected\n", used_file_path); return 0; } if (used_file_fd <= 0) { pr_err("storage device not present\n"); return 0; } int max_inode_count = *((int *)d); 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); return 0; } int block_count = st.st_size / FS_BLOCK_SIZE; int blocks_used_for_bitmap = block_count / (FS_BLOCK_SIZE * 8); if (block_count % (FS_BLOCK_SIZE * 8)) blocks_used_for_bitmap++; if (blocks_used_for_bitmap > FS_MAX_BITMAP_SIZE) { pr_err("memory bitmap is too large (%d blocks > %d)\n", blocks_used_for_bitmap, FS_MAX_BITMAP_SIZE); return 0; } pr("Formatting storage device '%s' of size %d (total_block_count = %d, bitmap_blocks = %d) with %d allowed inode pointers\n", used_file_path, st.st_size, block_count, blocks_used_for_bitmap, max_inode_count); struct fs_header fsh = {}; fsh.version = 0x1; fsh.max_inode_count = max_inode_count; fsh.block_count = block_count; pr("header size is %d bytes, writing it to the first block\n", sizeof(fsh)); int result = write_block(0, (void *) &fsh); if (FS_BLOCK_SIZE != result) { if (result < 0) { pr_err("failed to write fs header to device '%s'\n", used_file_path); } else { pr_err("failed to write full block to device, written %d/%d bytes\n", result, FS_BLOCK_SIZE); } return 0; } // 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; 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; }