diff --git a/src/fs.c b/src/fs.c index 2e8b92c..6bf3e41 100644 --- a/src/fs.c +++ b/src/fs.c @@ -907,18 +907,21 @@ fs_remove_fname_from_directory_finish: return 0; } -static void resolve_path(struct resolved_path *rp, char *path, unsigned int params) +static void resolve_path(struct resolved_path * const rp, const char * const original_path, const unsigned int params) { rp->parent_inode_ptr = -1; rp->target_inode_ptr = -1; - int path_len = strlen(path); + int path_len = strlen(original_path); if (!path_len) { pr_err("no path specified\n"); return; } + char path[FS_MAX_PATH_LEN+1]; + strcpy(path, original_path); + if (!strcmp(path, "/")) { rp->parent_inode_ptr = 0; rp->target_inode_ptr = 0; @@ -961,25 +964,54 @@ static void resolve_path(struct resolved_path *rp, char *path, unsigned int para if (i+1 == path_len) continue; - { - int res = find_fname_in_directory(current_inode_ptr, next_fname); - if (res < 0) { - pr_warn("directory '%s' does not exist\n", next_fname); + int res = find_fname_in_directory(current_inode_ptr, next_fname); + if (res < 0) { + pr_warn("directory '%s' does not exist\n", next_fname); + rp->parent_inode_ptr = -1; + break; + } + + struct fs_inode d; + read_block(read_inode_ptr(res), &d); + + if (d.ftype == SYMLINK) { + char symlink_path[FS_MAX_PATH_LEN+1]; + int symlink_path_len = read_symlink(res, symlink_path); + + if (symlink_path_len < 0) { + pr_warn("broken symlink\n"); rp->parent_inode_ptr = -1; break; } - { - struct fs_inode d; - read_block(read_inode_ptr(res), &d); - - if (d.ftype == REGULAR) { - pr_warn("'%s' is a regular file\n", next_fname); - rp->parent_inode_ptr = -1; - break; - } + if (!strcmp(symlink_path, "/")) { + rp->parent_inode_ptr = 0; + rp->target_inode_ptr = 0; + strcpy(rp->parent_fname, "/"); + strcpy(rp->target_fname, "/"); + return; } + if (symlink_path[0] == '/') { + res = 0; + memmove(&(path[symlink_path_len]), &(path[i+1]), path_len-i-1); + path[symlink_path_len-1] = '/'; + memcpy(path, &(symlink_path[1]), symlink_path_len-1); + path_len = symlink_path_len + path_len - i + 1; + i = -1; + strcpy(next_fname, "/"); + } else { + memmove(&(path[symlink_path_len+1]), &(path[i+1]), path_len-i-1); + path[symlink_path_len] = '/'; + memcpy(path, symlink_path, symlink_path_len); + path_len = symlink_path_len + path_len - i + 2; + i = -1; + } + } else if (d.ftype == REGULAR) { + pr_warn("'%s' is a regular file\n", next_fname); + rp->parent_inode_ptr = -1; + break; + } else if (d.ftype == DIRECTORY) { pr("Found directory '%s'\n", next_fname); current_inode_ptr = res; rp->parent_inode_ptr = res; @@ -1002,6 +1034,17 @@ static void resolve_path(struct resolved_path *rp, char *path, unsigned int para if (res < 0) { pr_warn("target fname '%s' does not exist\n", next_fname); rp->target_inode_ptr = -1; + } + + struct fs_inode i; + read_block(res, &i); + + if (i.ftype == SYMLINK && (params & FOLLOW_LAST_SYMLINK)) + struct + /* + rp->target_inode_ptr = res; + strcpy(rp->target_fname, next_fname); + */ } else { rp->target_inode_ptr = res; strcpy(rp->target_fname, next_fname); @@ -1631,6 +1674,73 @@ int fs_ln(void *d) return 0; } +static int read_symlink(int symlink_inode_ptr, char *path) +{ + struct fs_inode i; + read_block(read_inode_ptr(symlink_inode_ptr), &i); + + path[0] = '\0'; + int bytes_read = 0; + + for (int j = 0; j < BLOCK_ADDRESSES_PER_INODE; j++) { + if (!i.blocks[j]) { + pr_err("inode size (%d) indicates existing block but it's address is absent in inode, symlink at inode_ptr=%d might be broken\n", + i.size, symlink_inode_ptr); + return -1; + } + + char data[FS_BLOCK_SIZE]; + read_block(i.blocks[j], data); + + if (i.size - bytes_read < FS_BLOCK_SIZE) { + memcpy(&(path[bytes_read]), data, i.size - bytes_read); + bytes_read = i.size; + path[bytes_read] = '\0'; + } else { + memcpy(&(path[bytes_read]), data, FS_BLOCK_SIZE); + bytes_read += FS_BLOCK_SIZE; + path[bytes_read] = '\0'; + } + + if (i.size == bytes_read) + goto finish_reading; + } + + for (unsigned int next_ext = i.next_extension; ; ) { + struct fs_inode_extension ext; + read_block(next_ext, &ext); + + for (int j = 0; j < BLOCK_ADDRESSES_PER_INODE_EXTENSION; j++) { + if (!ext.blocks[j]) { + pr_err("inode size (%d) indicates existing block but it's address is absent in inode extension, symlink at inode_ptr=%d might be broken\n", + i.size, symlink_inode_ptr); + return -1; + } + + char data[FS_BLOCK_SIZE]; + read_block(ext.blocks[j], data); + + if (i.size - bytes_read < FS_BLOCK_SIZE) { + memcpy(&(path[bytes_read]), data, i.size - bytes_read); + bytes_read = i.size; + path[bytes_read] = '\0'; + } else { + memcpy(&(path[bytes_read]), data, FS_BLOCK_SIZE); + bytes_read += FS_BLOCK_SIZE; + path[bytes_read] = '\0'; + } + + if (i.size == bytes_read) + goto finish_reading; + } + + next_ext = ext.next_extension; + } + +finish_reading: + return bytes_read; +} + int fs_symlink(void *d) { char *target_path = ((char **) d)[0];