diff --git a/config.h b/config.h index d183c48..aa62e59 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,7 @@ #ifndef CONFIG_FILE #define CONFIG_FILE + /* Global config options */ #define DEBUG 1 #define LOG_LEVEL 4 @@ -23,6 +24,7 @@ #define FS_BLOCK_SIZE 4096 #define FS_MAX_BITMAP_SIZE 64 #define FS_MAX_PATH_LEN 512 +#define FS_MAX_OPEN_FD 32 #endif diff --git a/inc/fs.h b/inc/fs.h index a854ebe..97e9df9 100644 --- a/inc/fs.h +++ b/inc/fs.h @@ -41,6 +41,11 @@ struct fs_directory_record { unsigned int inode_no; }; +struct fs_file_description { + unsigned int inode; + unsigned int rw_offset; +}; + char *fs_get_cwd(void); int fs_create(void *d); @@ -50,5 +55,7 @@ int fs_mkfs(void *d); int fs_ls(void *d); int fs_la(void *d); int fs_rm(void *d); +int fs_open(void *d); +int fs_close(void *d); int fs_allow_write(void *d); int fs_prohibit_write(void *d); diff --git a/src/cli.c b/src/cli.c index dd14a1e..6afa691 100644 --- a/src/cli.c +++ b/src/cli.c @@ -15,9 +15,16 @@ static const struct CliCommandEntry cmd[] = { // mandatory commands {"mkfs", 1, (enum CliArgType[]) {INT}, fs_mkfs}, {"create", 1, (enum CliArgType[]) {STR}, fs_create}, + //{"stat", 1, (enum CliArgType[]) {STR}, fs_stat}, {"ls", 0, NULL, fs_ls}, {"ln", 2, (enum CliArgType[]) {STR, STR}, fs_ln}, {"rm", 1, (enum CliArgType[]) {STR}, fs_rm}, + //{"truncate", 2, (enum CliArgType[]) {STR, INT}, fs_truncate}, + {"open", 1, (enum CliArgType[]) {STR}, fs_open}, + //{"seek", 2, (enum CliArgType[]) {INT, INT}, fs_seek}, + //{"read", 2, (enum CliArgType[]) {INT, INT}, fs_read}, + //{"write", 2, (enum CliArgType[]) {INT, STR}, fs_write}, + {"close", 1, (enum CliArgType[]) {INT}, fs_close}, // custom commands {"use", 1, (enum CliArgType[]) {STR}, fs_use}, diff --git a/src/fs.c b/src/fs.c index bb1e615..6269e33 100644 --- a/src/fs.c +++ b/src/fs.c @@ -21,6 +21,8 @@ static int write_permitted; static char fs_cwd[FS_MAX_PATH_LEN+1]; static unsigned int fs_cwd_inode_ptr; +static struct fs_file_description fs_file_descriptions[FS_MAX_OPEN_FD]; + static int read_block(unsigned int block_no, void *data) { @@ -384,9 +386,10 @@ static int *find_filename_in_directory(unsigned int dir_inode_ptr, char *fname) continue; // filename found - int *r = malloc(sizeof(int) * 2); + int *r = malloc(sizeof(int) * 3); r[0] = i; r[1] = k; + r[2] = recs[k].inode_no; return r; } } @@ -477,35 +480,77 @@ static int fs_remove_fname_from_directory(unsigned int dir_inode_ptr, char *fnam for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE; i++) { struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK]; - if (dir.blocks[i]) { - read_block(dir.blocks[i], (void *) &recs); + if (!dir.blocks[i]) + continue; - for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) { - if (!recs[k].inode_no) - continue; + read_block(dir.blocks[i], (void *) &recs); - if (!strcmp(fname, recs[k].fname)) { - pr("Directory record for '%s' found in block=%d, record=%d, removing\n", fname, i, k); + for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) { + if (!recs[k].inode_no) + continue; + + if (strcmp(fname, recs[k].fname)) + continue; + + pr("Directory record for '%s' found in block=%d, record=%d, removing\n", fname, i, k); + + unsigned int inode_location_cache = read_inode_ptr(recs[k].inode_no); + + // decrement ref_count + struct fs_inode f; + read_block(read_inode_ptr(recs[k].inode_no), (void *) &f); + f.ref_count--; + write_block(read_inode_ptr(recs[k].inode_no), (void *) &f); + + // if it drops to zero, nullify inode_ptr pointing to this inode + if (!f.ref_count) { + pr("ref_count=0, clearing inode_ptr\n"); + write_inode_ptr(recs[k].inode_no, 0); + + // if no fd reference this inode, clean it up altogether + int i; + for (i = 0; i < FS_MAX_OPEN_FD; i++) + if (fs_file_descriptions[i].inode == inode_location_cache) + break; + + if (i == FS_MAX_OPEN_FD) { + pr("No open fd reference inode %d, cleaning up\n", inode_location_cache); - // decrement ref_count struct fs_inode f; - read_block(read_inode_ptr(recs[k].inode_no), (void *) &f); - f.ref_count--; - write_block(read_inode_ptr(recs[k].inode_no), (void *) &f); + read_block(inode_location_cache, (void *) &f); - // if it drops to zero, nullify inode_ptr pointing to this inode - if (!f.ref_count) { - pr("ref_count=0, clearing inode_ptr\n"); - write_inode_ptr(recs[k].inode_no, 0); + // clear blocks referenced in base inode + for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE; i++) { + if (f.blocks[i]) + mark_free(f.blocks[i]); } + mark_free(inode_location_cache); - // clear directory record inode_ptr - recs[k].inode_no = 0; - write_block(dir.blocks[i], (void *) &recs); + // clear blocks referenced in inode extensions + struct fs_inode_extension ext; + unsigned int next_extension = f.next_extension; - goto fs_remove_fname_from_directory_finish; + while (next_extension) { + mark_free(next_extension); + read_block(next_extension, (void *) &ext); + next_extension = ext.next_extension; + + for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE_EXTENSION; i++) { + if (ext.blocks[i]) + mark_free(ext.blocks[i]); + } + } + } else { + pr("Inode %d is still referenced by fd %d, not removing it\n", + inode_location_cache, i); } } + + // clear directory record inode_ptr + recs[k].inode_no = 0; + write_block(dir.blocks[i], (void *) &recs); + + goto fs_remove_fname_from_directory_finish; } } @@ -520,6 +565,93 @@ fs_remove_fname_from_directory_finish: return 0; } +int fs_open(void *d) +{ + char *fname = *((char **) d); + + // find file inode + struct fs_file_description fd; + { + int *r = find_filename_in_directory(fs_cwd_inode_ptr, fname); + if (!r) { + pr_err("no such file: '%s'\n", fname); + return 0; + } + + fd.inode = read_inode_ptr(r[2]); + free(r); + } + fd.rw_offset = 0; + + // find free file descriptor + int free_fd; + for (free_fd = 0; (free_fd < FS_MAX_OPEN_FD) && (fs_file_descriptions[free_fd].inode); free_fd++); + if (free_fd == FS_MAX_OPEN_FD) { + pr_err("no free file descriptor found\n"); + return 0; + } + + memcpy(&(fs_file_descriptions[free_fd]), &fd, sizeof(struct fs_file_description)); + + pr_stdout("%d\n", free_fd); + + return 0; +} + +int fs_close(void *d) +{ + int fd = *((int *) d); + + // remove inode number from fd + if (!fs_file_descriptions[fd].inode) { + pr_err("fd %d is not open\n", fd); + return 0; + } + + unsigned int inode_location_cache = fs_file_descriptions[fd].inode; + + fs_file_descriptions[fd].inode = 0; + pr("fd %d closed\n", fd); + + // cleanup file data on disk if ref_count=0 + // and no other open descriptor references it's inode + + struct fs_inode f; + read_block(inode_location_cache, (void *) &f); + if (f.ref_count) + return 0; + + for (int i = 0; i < FS_MAX_OPEN_FD; i++) + if (fs_file_descriptions[i].inode == inode_location_cache) + return 0; + + // if ended up here, the inode is not referenced anywhere + pr("No open fd reference inode %d, cleaning up\n", inode_location_cache); + + // clear blocks referenced in base inode + for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE; i++) { + if (f.blocks[i]) + mark_free(f.blocks[i]); + } + mark_free(inode_location_cache); + + // clear blocks referenced in inode extensions + struct fs_inode_extension ext; + unsigned int next_extension = f.next_extension; + + while (next_extension) { + mark_free(next_extension); + read_block(next_extension, (void *) &ext); + next_extension = ext.next_extension; + + for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE_EXTENSION; i++) { + if (ext.blocks[i]) + mark_free(ext.blocks[i]); + } + } +} + + int fs_create(void *d) { if (!write_permitted) {