diff --git a/inc/print.h b/inc/print.h index c232cd5..ca9b03c 100644 --- a/inc/print.h +++ b/inc/print.h @@ -3,6 +3,8 @@ #include +#include + #include "config.h" #include "color.h" @@ -15,8 +17,10 @@ #if ENABLE_STDOUT == 1 #define pr_stdout(...) { printf(__VA_ARGS__); } + #define write_stdout(data, size) { write(1, (data), (size)); } #else #define pr_stdout(...) {} + #define write_stdout(data, size) {} #endif diff --git a/src/fs.c b/src/fs.c index 6e0a7a1..d381257 100644 --- a/src/fs.c +++ b/src/fs.c @@ -587,6 +587,312 @@ int fs_open(void *d) return 0; } +int fs_seek(void *d) +{ + int fd = ((int *) d)[0]; + int offset = ((int *) d)[1]; + + if (!fs_file_descriptions[fd].inode) { + pr_err("fd %d is not open\n", fd); + return 0; + } + + struct fs_inode f; + read_block(fs_file_descriptions[fd].inode, (void *) &f); + + if (offset > f.size) { + pr_err("offset is larger than file (%d > %d)\n", offset, f.size); + return 0; + } + + pr("Moving rw_offset of fd %d: %d -> %d\n", + fd, fs_file_descriptions[fd].rw_offset, offset); + fs_file_descriptions[fd].rw_offset = offset; + + return 0; +} + +static void read_fd_block(unsigned int fd, unsigned int block_index, unsigned char *buf) +{ + struct fs_inode f; + read_block(fs_file_descriptions[fd].inode, (void *) &f); + + if (block_index < BLOCK_ADDRESSES_PER_INODE) { + if (f.blocks[block_index]) + read_block(f.blocks[block_index], (void *) buf); + else + memset(buf, 0, FS_BLOCK_SIZE); + } else { + int ext_no = (block_index - BLOCK_ADDRESSES_PER_INODE) / BLOCK_ADDRESSES_PER_INODE_EXTENSION; + int ext_offset = (block_index - BLOCK_ADDRESSES_PER_INODE) % BLOCK_ADDRESSES_PER_INODE_EXTENSION; + + unsigned int target_extension_address; + unsigned int i; + + // seek to target extension address + for ( + target_extension_address = f.next_extension, i = 0; + (i < ext_no) && target_extension_address; + i++ + ) { + struct fs_inode_extension ext; + read_block(target_extension_address, (void *) &ext); + target_extension_address = ext.next_extension; + } + + if (!target_extension_address) { + memset(buf, 0, FS_BLOCK_SIZE); + } else { + struct fs_inode_extension ext; + read_block(target_extension_address, (void *) &ext); + + if (!ext.blocks[ext_offset]) + memset(buf, 0, FS_BLOCK_SIZE); + else + read_block(ext.blocks[ext_offset], (void *) buf); + } + } +} + +static int write_fd_block(unsigned int fd, unsigned int block_index, unsigned char *buf) +{ + struct fs_inode f; + read_block(fs_file_descriptions[fd].inode, (void *) &f); + + if (block_index < BLOCK_ADDRESSES_PER_INODE) { + if (!f.blocks[block_index]) { + int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to write to fd=%d block=%d: can't allocate block for writing file data\n", + fd, block_index); + return -1; + } + mark_used(new_block); + + f.blocks[block_index] = new_block; + write_block(fs_file_descriptions[fd].inode, (void *) &f); + } + + write_block(f.blocks[block_index], (void *) buf); + } else { + int ext_no = (block_index - BLOCK_ADDRESSES_PER_INODE) / BLOCK_ADDRESSES_PER_INODE_EXTENSION; + int ext_offset = (block_index - BLOCK_ADDRESSES_PER_INODE) % BLOCK_ADDRESSES_PER_INODE_EXTENSION; + + // treat switch from base inode to inode extension as special case + if (!f.next_extension) { + int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to write to fd=%d block=%d: can't allocate block for inode extension\n", + fd, block_index); + return -1; + } + mark_used(new_block); + + struct fs_inode_extension ext; + memset(&ext, 0, sizeof(struct fs_inode_extension)); + + f.next_extension = new_block; + write_block(fs_file_descriptions[fd].inode, (void *) &f); + } + + unsigned int target_extension_address; + unsigned int previous_extension_address = f.next_extension; + unsigned int i; + + // seek to target extension address + for ( + target_extension_address = previous_extension_address, i = 0; + (i < ext_no); + i++ + ) { + if (!target_extension_address) { + int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to write to fd=%d block=%d: can't allocate block for inode extension\n", + fd, block_index); + return -1; + } + mark_used(new_block); + + // updating previous inode extension + struct fs_inode_extension prev_ext; + read_block(previous_extension_address, (void *) &prev_ext); // read + prev_ext.next_extension = new_block; // modify + write_block(previous_extension_address, (void *) &prev_ext); // write + + // writing new inode extension + struct fs_inode_extension ext; + memset(&ext, 0, sizeof(struct fs_inode_extension)); + write_block(target_extension_address, (void *) &ext); + } + + struct fs_inode_extension ext; + read_block(target_extension_address, (void *) &ext); + previous_extension_address = target_extension_address; + target_extension_address = ext.next_extension; + } + + if (!target_extension_address) { + int new_block = find_free_block(); + if (!new_block) { + pr_err("failed to write to fd=%d block=%d: can't allocate block for inode extension\n", + fd, block_index); + return -1; + } + + struct fs_inode_extension prev_ext; + read_block(previous_extension_address, (void *) &prev_ext); // read + prev_ext.next_extension = new_block; // modify + write_block(previous_extension_address, (void *) &prev_ext); // write + + int new_data_block = find_free_block(); + if (!new_data_block) { + pr_err("failed to write to fd=%d block=%d: can't allocate block for data\n", + fd, block_index); + return -1; + } + + mark_used(new_block); + mark_used(new_data_block); + + // writing new inode extension + struct fs_inode_extension ext; + memset(&ext, 0, sizeof(struct fs_inode_extension)); + ext.blocks[ext_offset] = new_data_block; + write_block(target_extension_address, (void *) &ext); + + // writing new data block + write_block(new_data_block, (void *) buf); + } else { + struct fs_inode_extension ext; + read_block(target_extension_address, (void *) &ext); + + if (!ext.blocks[ext_offset]) { + int new_data_block = find_free_block(); + if (!new_data_block) { + pr_err("failed to write to fd=%d block=%d: can't allocate block for data\n", + fd, block_index); + return -1; + } + mark_used(new_data_block); + + ext.blocks[ext_offset] = new_data_block; + write_block(target_extension_address, (void *) &ext); + } + + write_block(ext.blocks[ext_offset], (void *) buf); + } + } +} + +int fs_read(void *d) +{ + int fd = ((int *) d)[0]; + int amount = ((int *) d)[1]; + + if (!fs_file_descriptions[fd].inode) { + pr_err("fd %d is not open\n", fd); + return 0; + } + + if (amount <= 0) + return 0; + + struct fs_inode f; + read_block(fs_file_descriptions[fd].inode, (void *) &f); + + if (fs_file_descriptions[fd].rw_offset + amount > f.size) { + pr_err("can not read outside of a file (offset %d + amount %d > f.size %d)\n", + fs_file_descriptions[fd].rw_offset, amount, f.size); + return 0; + } + + pr("Reading %d bytes from fd %d (offset %d)\n", + amount, fd, fs_file_descriptions[fd].rw_offset); + + unsigned char *read_data = malloc(amount); + unsigned int total_read_data_amount = 0; + + // read from first block + unsigned char block_buffer[FS_BLOCK_SIZE]; + + int block_index = fs_file_descriptions[fd].rw_offset / FS_BLOCK_SIZE; + int block_offset = fs_file_descriptions[fd].rw_offset % FS_BLOCK_SIZE; + + read_fd_block(fd, block_index, block_buffer); + + if (block_offset + amount <= FS_BLOCK_SIZE) { + memcpy(read_data, &(block_buffer[block_offset]), amount); + goto print_read_data; + } else { + memcpy(read_data, &(block_buffer[block_offset]), FS_BLOCK_SIZE - block_offset); + total_read_data_amount += FS_BLOCK_SIZE - block_offset; + } + + // read from all next blocks + while (amount - total_read_data_amount > 0) { + block_index++; + read_fd_block(fd, block_index, block_buffer); + int bytes_to_read = (amount - total_read_data_amount > FS_BLOCK_SIZE) + ? FS_BLOCK_SIZE : amount - total_read_data_amount; + + memcpy(&(read_data[total_read_data_amount]), block_buffer, bytes_to_read); + total_read_data_amount += bytes_to_read; + } + +print_read_data: + pr("Updating fd %d offset: %d -> %d\n", + fd, fs_file_descriptions[fd].rw_offset, fs_file_descriptions[fd].rw_offset+amount); + fs_file_descriptions[fd].rw_offset += amount; + + write_stdout(read_data, amount); + pr_stdout("\n"); + + free(read_data); + + return 0; +} + +int fs_write(void *d) +{ + int fd = ((int *) d)[0]; + char *str = *((char **) d+4); + int str_len = strlen(str); + + if (!fs_file_descriptions[fd].inode) { + pr_err("fd %d is not open\n", fd); + return 0; + } + + if (str_len == 0) + return 0; + + int block_index = fs_file_descriptions[fd].rw_offset / FS_BLOCK_SIZE; + int block_offset = fs_file_descriptions[fd].rw_offset % FS_BLOCK_SIZE; + + unsigned char data_buffer[FS_BLOCK_SIZE]; + + if (block_offset + str_len <= FS_BLOCK_SIZE) { + read_fd_block(fd, block_index, data_buffer); + memcpy(&(data_buffer[block_offset]), str, str_len); + write_fd_block(fd, block_index, data_buffer); + } else { + read_fd_block(fd, block_index, data_buffer); + memcpy(&(data_buffer[block_offset]), str, FS_BLOCK_SIZE - block_offset); + write_fd_block(fd, block_index, data_buffer); + + read_fd_block(fd, block_index+1, data_buffer); + memcpy(data_buffer, &(str[FS_BLOCK_SIZE - block_offset]), str_len - block_offset); + write_fd_block(fd, block_index+1, data_buffer); + } + + pr("Moving fd %d offset: %d -> %d\n", + fd, fs_file_descriptions[fd].rw_offset, fs_file_descriptions[fd].rw_offset + str_len); + fs_file_descriptions[fd].rw_offset += str_len; + + return 0; +} + int fs_close(void *d) { int fd = *((int *) d);