diff --git a/inc/fs.h b/inc/fs.h index 12ccf30..95bf6cf 100644 --- a/inc/fs.h +++ b/inc/fs.h @@ -37,7 +37,7 @@ struct fs_inode_extension { __attribute__((packed)) struct fs_directory_record { - unsigned char fname[60]; + unsigned char fname[FS_MAX_FNAME_LEN+1]; unsigned int inode_no; }; diff --git a/src/fs.c b/src/fs.c index 0b2c65c..02adcaa 100644 --- a/src/fs.c +++ b/src/fs.c @@ -26,6 +26,19 @@ static struct fs_file_description fs_file_descriptions[FS_MAX_OPEN_FD]; static int read_block(unsigned int block_no, void *data) { + // failsafe + struct stat st; + if (fstat(used_file_fd, &st) < 0) { + pr_err("could not stat device '%s'\n", used_file_path); + return 0; + } + + if (block_no * FS_BLOCK_SIZE >= st.st_size) { + pr_err("read beyond device address space denied (%d >= %d)\n", + block_no * FS_BLOCK_SIZE, st.st_size); + return -1; + } + 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; @@ -36,6 +49,19 @@ static int read_block(unsigned int block_no, void *data) static int write_block(unsigned int block_no, void *data) { + // failsafe + struct stat st; + if (fstat(used_file_fd, &st) < 0) { + pr_err("could not stat device '%s'\n", used_file_path); + return 0; + } + + if (block_no * FS_BLOCK_SIZE >= st.st_size) { + pr_err("write beyond device address space denied (%d >= %d)\n", + block_no * FS_BLOCK_SIZE, st.st_size); + return -1; + } + if (!write_permitted) { pr_err("write operations are prohibited\n"); return -1; @@ -224,13 +250,40 @@ static unsigned int read_inode_ptr(unsigned int inode_ptr) return fsh.inode_ptrs[inode_ptr]; } else { - pr_err("inode_ptr %d is in fs_header_extension, reading is not implemented\n", inode_ptr); - return 0; - // TODO: find block with relevant inode_ptr - /* - 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; - */ + // inode_ptr is in fs_header_extension + int extension_no = (inode_ptr - BLOCK_ADDRESSES_PER_INODE) / BLOCK_ADDRESSES_PER_INODE_EXTENSION; + int extension_offset = (inode_ptr - BLOCK_ADDRESSES_PER_INODE) % BLOCK_ADDRESSES_PER_INODE_EXTENSION; + + 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 0; + } + + if (!fsh.next_extension) + return 0; + + unsigned int next_ext = fsh.next_extension; + unsigned int curr_ext = 0; + + struct fs_header_extension ext; + + for (unsigned int i = 0; i < extension_no + 1; i++) { + if (!next_ext) + return 0; + + curr_ext = next_ext; + read_block(curr_ext, (void *) &ext); + next_ext = ext.next_extension; + } + + return ext.inode_ptrs[extension_offset]; } } @@ -265,12 +318,73 @@ static void write_inode_ptr(unsigned int inode_ptr, unsigned int block_ptr) pr("Updated inode ptr %d -> %d\n", inode_ptr, block_ptr); } else { - pr_err("inode_ptr %d is in fs_header_extension, writing is not implemented\n", inode_ptr); - // 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; - */ + // inode_ptr is in fs_header_extension + int extension_no = (inode_ptr - BLOCK_ADDRESSES_PER_INODE) / BLOCK_ADDRESSES_PER_INODE_EXTENSION; + int extension_offset = (inode_ptr - BLOCK_ADDRESSES_PER_INODE) % BLOCK_ADDRESSES_PER_INODE_EXTENSION; + + 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; + } + + // special case: if header has no extensions, create one + // if it was not needed, we would not have ended here in the first place + if (!fsh.next_extension) { + unsigned int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to allocate block for fs_header_extension\n"); + return; + } + mark_used(new_block); + + struct fs_header_extension ext; + memset(&ext, 0, sizeof(struct fs_header_extension)); + write_block(new_block, (void *) &ext); + + fsh.next_extension = new_block; + write_block(0, (void *) &fsh); + } + + unsigned int next_ext = fsh.next_extension; + unsigned int curr_ext = 0; + + struct fs_header_extension ext; + + for (unsigned int i = 0; i < extension_no + 1; i++) { + if (!next_ext) { + unsigned int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to allocate block for fs_header_extension\n"); + return; + } + mark_used(new_block); + + struct fs_header_extension new_ext; + memset(&new_ext, 0, sizeof(struct fs_header_extension)); + write_block(new_block, (void *) &new_ext); + + ext.next_extension = new_block; + write_block(curr_ext, (void *) &ext); + + next_ext = new_block; + } + + curr_ext = next_ext; + read_block(curr_ext, (void *) &ext); + next_ext = ext.next_extension; + } + + ext.inode_ptrs[extension_offset] = block_ptr; + write_block(curr_ext, (void *) &ext); + + pr("Updated inode ptr %d -> %d\n", inode_ptr, block_ptr); } } @@ -329,7 +443,7 @@ static int fs_find_free_directory_record(unsigned int dir_inode_ptr) if (!dir.blocks[i]) { if (found_block_no < fsh.max_inode_count) - return (found_block_no); + return found_block_no; else return -1; } @@ -348,9 +462,43 @@ static int fs_find_free_directory_record(unsigned int dir_inode_ptr) } // search in inode extensions - // TODO + struct fs_inode_extension ext; - return -1; + unsigned int next_ext = dir.next_extension; + unsigned int curr_ext = 0; + + while (next_ext) { + read_block(next_ext, (void *) &ext); + next_ext = ext.next_extension; + + for (unsigned int i = 0; i < BLOCK_ADDRESSES_PER_INODE_EXTENSION; i++) { + struct fs_directory_record r[DIRECTORY_RECORDS_PER_BLOCK]; + + if (!ext.blocks[i]) { + if (found_block_no < fsh.max_inode_count) + return found_block_no; + else + return -1; + } + + read_block(ext.blocks[i], (void *) &r); + + for (int j = 0; j < DIRECTORY_RECORDS_PER_BLOCK; j++, found_block_no++) { + if (r[j].inode_no) + continue; + + if (found_block_no >= fsh.max_inode_count) + return -1; + + return found_block_no; + } + } + } + + if (found_block_no >= fsh.max_inode_count) + return -1; + else + return found_block_no; } static int *find_filename_in_directory(unsigned int dir_inode_ptr, char *fname) @@ -401,19 +549,19 @@ static int fs_add_fname_to_directory(unsigned int dir_inode_ptr, unsigned int ta int new_directory_record_index = fs_find_free_directory_record(dir_inode_ptr); if (new_directory_record_index < 0) { - pr_err("no free inode pointer found\n"); + pr_err("no free directory record found\n"); return -1; } + struct fs_inode dir; + read_block(read_inode_ptr(dir_inode_ptr), (void *) &dir); + if (new_directory_record_index < DIRECTORY_RECORDS_PER_BLOCK * BLOCK_ADDRESSES_PER_INODE) { // record is located in base inode, writing to it int block_no = new_directory_record_index / DIRECTORY_RECORDS_PER_BLOCK; int block_offset = new_directory_record_index % DIRECTORY_RECORDS_PER_BLOCK; - struct fs_inode dir_inode; - read_block(read_inode_ptr(dir_inode_ptr), (void *) &dir_inode); - - if (dir_inode.blocks[block_no] == 0) { + if (dir.blocks[block_no] == 0) { // allocate new block unsigned int new_block = find_free_block(); if (new_block == 0) { @@ -430,31 +578,115 @@ static int fs_add_fname_to_directory(unsigned int dir_inode_ptr, unsigned int ta mark_used(new_block); - dir_inode.blocks[block_no] = new_block; - if (dir_inode.size <= block_no * FS_BLOCK_SIZE) { + dir.blocks[block_no] = new_block; + if (dir.size <= block_no * FS_BLOCK_SIZE) { pr("Updated directory size at inode_ptr=%d: %d -> %d\n", - dir_inode_ptr, dir_inode.size, (block_no + 1) * FS_BLOCK_SIZE); - dir_inode.size = (block_no + 1) * FS_BLOCK_SIZE; - write_block(read_inode_ptr(dir_inode_ptr), (void *) &dir_inode); + dir_inode_ptr, dir.size, (block_no + 1) * FS_BLOCK_SIZE); + dir.size = (block_no + 1) * FS_BLOCK_SIZE; + write_block(read_inode_ptr(dir_inode_ptr), (void *) &dir); } } struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; - read_block(dir_inode.blocks[block_no], (void *) &recs); + read_block(dir.blocks[block_no], (void *) &recs); strcpy(recs[block_offset].fname, fname); recs[block_offset].inode_no = target_inode_ptr; - write_block(dir_inode.blocks[block_no], (void *) &recs); + write_block(dir.blocks[block_no], (void *) &recs); pr("Written new directory record #%d for file '%s' (-> inode_ptr=%d) in inode_ptr=%d\n", new_directory_record_index, fname, target_inode_ptr, dir_inode_ptr); - - return 0; } else { // record is located in inode extension - // TODO + unsigned int extension_no = (new_directory_record_index - (DIRECTORY_RECORDS_PER_BLOCK * BLOCK_ADDRESSES_PER_INODE)) + / (DIRECTORY_RECORDS_PER_BLOCK * BLOCK_ADDRESSES_PER_INODE_EXTENSION); + unsigned int extension_block_index = (new_directory_record_index - (DIRECTORY_RECORDS_PER_BLOCK * BLOCK_ADDRESSES_PER_INODE)) + % (DIRECTORY_RECORDS_PER_BLOCK * BLOCK_ADDRESSES_PER_INODE_EXTENSION); + unsigned int extension_block_offset = (new_directory_record_index - (DIRECTORY_RECORDS_PER_BLOCK * BLOCK_ADDRESSES_PER_INODE)) + % DIRECTORY_RECORDS_PER_BLOCK; + + // seek to next extension + // special case + if (!dir.next_extension) { + unsigned int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to allocate block for fs_inode_extension\n"); + return -1; + } + mark_used(new_block); + + struct fs_inode_extension ext; + memset(&ext, 0, sizeof(struct fs_inode_extension)); + write_block(new_block, (void *) &ext); + + dir.next_extension = new_block; + write_block(read_inode_ptr(dir_inode_ptr), (void *) &dir); + } + + struct fs_inode_extension ext; + unsigned int next_ext = dir.next_extension; + unsigned int curr_ext = 0; + + for (int i = 0; i < extension_no + 1; i++) { + if (!next_ext) { + unsigned int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to allocate block for fs_inode_extension\n"); + return -1; + } + mark_used(new_block); + + struct fs_inode_extension new_ext; + memset(&new_ext, 0, sizeof(struct fs_inode_extension)); + write_block(new_block, (void *) &new_ext); + + ext.next_extension = new_block; + write_block(curr_ext, (void *) &ext); + + next_ext = new_block; + } + + curr_ext = next_ext; + read_block(curr_ext, (void *) &ext); + next_ext = ext.next_extension; + } + + struct fs_directory_record r[DIRECTORY_RECORDS_PER_BLOCK]; + + if (!ext.blocks[extension_block_index]) { + unsigned int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to allocate block for file data\n"); + return -1; + } + mark_used(new_block); + + ext.blocks[extension_block_index] = new_block; + write_block(curr_ext, (void *) &ext); + + memset(&r, 0, sizeof(r)); + + unsigned int dir_size = BLOCK_ADDRESSES_PER_INODE * FS_BLOCK_SIZE + + extension_no * BLOCK_ADDRESSES_PER_INODE_EXTENSION * FS_BLOCK_SIZE + + (extension_block_index + 1) * FS_BLOCK_SIZE; + if (dir.size < dir_size) { + dir.size = dir_size; + write_block(read_inode_ptr(dir_inode_ptr), (void *) &dir); + } + } else { + read_block(ext.blocks[extension_block_index], (void *) &r); + } + + r[extension_block_offset].inode_no = target_inode_ptr; + strcpy(r[extension_block_offset].fname, fname); + write_block(ext.blocks[extension_block_index], (void *) &r); + + pr("Written new directory record #%d for file '%s' (-> inode_ptr=%d) in inode_ptr=%d\n", + new_directory_record_index, fname, target_inode_ptr, dir_inode_ptr); } + + return 0; } static int fs_remove_fname_from_directory(unsigned int dir_inode_ptr, char *fname) @@ -724,6 +956,8 @@ static int write_fd_block(unsigned int fd, unsigned int block_index, unsigned ch struct fs_inode_extension ext; memset(&ext, 0, sizeof(struct fs_inode_extension)); write_block(target_extension_address, (void *) &ext); + + target_extension_address = new_block; } struct fs_inode_extension ext; @@ -894,7 +1128,8 @@ int fs_write(void *d) read_block(fs_file_descriptions[fd].inode, (void *) &f); if (fs_file_descriptions[fd].rw_offset > f.size) { - pr("Increasing fd %d file size: %d -> %d\n", fd, f.size, f.size + str_len); + pr("Increasing fd %d file size: %d -> %d\n", + fd, f.size, fs_file_descriptions[fd].rw_offset); f.size = fs_file_descriptions[fd].rw_offset; write_block(fs_file_descriptions[fd].inode, (void *) &f); } @@ -1082,8 +1317,8 @@ int fs_create(void *d) } } - if (fname_len > 59) { - pr_err("filename too long (%d > %d)\n", fname_len, 59); + if (fname_len > FS_MAX_FNAME_LEN) { + pr_err("filename too long (%d > %d)\n", fname_len, FS_MAX_FNAME_LEN); return 0; } @@ -1154,8 +1389,8 @@ int fs_ln(void *d) } - if (new_fname_len > 59) { - pr_err("new filename too long (%d > 59)\n", new_fname_len); + if (new_fname_len > FS_MAX_FNAME_LEN) { + pr_err("new filename too long (%d > %d)\n", new_fname_len, FS_MAX_FNAME_LEN); return 0; } @@ -1246,22 +1481,44 @@ int fs_ls(void *d) // list entries from base inode for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE; i++) { - struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; + if (!dir.blocks[i]) + continue; - if (dir.blocks[i]) { - read_block(dir.blocks[i], (void *) &recs); + struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; + read_block(dir.blocks[i], (void *) &recs); + + for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) { + if (!recs[k].inode_no) + continue; + + pr_stdout("%s -> inode_ptr=%d\n", recs[k].fname, recs[k].inode_no); + } + } + + // list entries from inode extension + struct fs_inode_extension ext; + unsigned int next_ext = dir.next_extension; + + while (next_ext) { + read_block(next_ext, (void *) &ext); + next_ext = ext.next_extension; + + for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE_EXTENSION; i++) { + if (!ext.blocks[i]) + continue; + + struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; + read_block(ext.blocks[i], (void *) &recs); for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) { if (!recs[k].inode_no) continue; - pr_stdout("%s\n", recs[k].fname); + pr_stdout("%s -> inode_ptr=%d\n", recs[k].fname, recs[k].inode_no); } } } - // list entries from inode extension - // TODO return 0; } @@ -1282,10 +1539,46 @@ int fs_la(void *d) // list entries from base inode for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE; i++) { - struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; + if (!dir.blocks[i]) + continue; - if (dir.blocks[i]) { - read_block(dir.blocks[i], (void *) &recs); + struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; + read_block(dir.blocks[i], (void *) &recs); + + for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) { + if (!recs[k].inode_no) + continue; + + struct fs_inode f_inode; + read_block(read_inode_ptr(recs[k].inode_no), (void *) &f_inode); + + if (f_inode.ftype == DIRECTORY) { + pr_stdout(COLOR_BLUE "%s" COLOR_RESET + " (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=dir)\n", + recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no), + f_inode.ref_count, f_inode.size); + } else { + pr_stdout("%s (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=reg)\n", + recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no), + f_inode.ref_count, f_inode.size); + } + } + } + + // list entries from inode extension + struct fs_inode_extension ext; + unsigned int next_ext = dir.next_extension; + + while (next_ext) { + read_block(next_ext, (void *) &ext); + next_ext = ext.next_extension; + + for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE_EXTENSION; i++) { + if (!ext.blocks[i]) + continue; + + struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; + read_block(ext.blocks[i], (void *) &recs); for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) { if (!recs[k].inode_no) @@ -1296,7 +1589,7 @@ int fs_la(void *d) if (f_inode.ftype == DIRECTORY) { pr_stdout(COLOR_BLUE "%s" COLOR_RESET - "(inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=dir)\n", + " (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=dir)\n", recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no), f_inode.ref_count, f_inode.size); } else { @@ -1308,9 +1601,6 @@ int fs_la(void *d) } } - // list entries from inode extension - // TODO - return 0; }