From fe637af45879d874a9e4a5920209d6613b24da8e Mon Sep 17 00:00:00 2001 From: mintsuki Date: Sun, 21 Aug 2022 00:50:12 +0200 Subject: [PATCH] ext: Properly follow relative symlinks --- common/fs/ext2.s2.c | 44 +++++++++++++++++++++++++---- common/lib/blib.h | 2 ++ common/lib/blib.s2.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/common/fs/ext2.s2.c b/common/fs/ext2.s2.c index 01003a33..3bf26228 100644 --- a/common/fs/ext2.s2.c +++ b/common/fs/ext2.s2.c @@ -339,15 +339,28 @@ static uint32_t *create_alloc_map(struct ext2_file_handle *fd, return alloc_map; } -static bool symlink_to_inode(struct ext2_inode *inode, struct ext2_file_handle *fd) { +static bool symlink_to_inode(struct ext2_inode *inode, struct ext2_file_handle *fd, + const char *cwd, size_t cwd_len) { // I cannot find whether this is 0-terminated or not, so I'm gonna take the // safe route here and assume it is not. if (inode->i_size < 59) { struct ext2_dir_entry dir; char *symlink = (char *)inode->i_blocks; symlink[59] = 0; - if (!ext2_parse_dirent(&dir, fd, symlink)) + + char *abs = ext_mem_alloc(4096); + char *cwd_copy = ext_mem_alloc(cwd_len + 1); + memcpy(cwd_copy, cwd, cwd_len); + get_absolute_path(abs, symlink, cwd_copy); + + pmm_free(cwd_copy, cwd_len + 1); + + if (!ext2_parse_dirent(&dir, fd, abs)) { + pmm_free(abs, 4096); return false; + } + pmm_free(abs, 4096); + ext2_get_inode(inode, fd, dir.inode); return true; } else { @@ -367,10 +380,15 @@ static bool ext2_parse_dirent(struct ext2_dir_entry *dir, struct ext2_file_handl bool ret; + const char *cwd = path; + size_t cwd_len = 0; + next: memset(token, 0, 256); - for (size_t i = 0; i < 255 && *path != '/' && *path != '\0'; i++, path++) + size_t next_cwd_len = cwd_len; + + for (size_t i = 0; i < 255 && *path != '/' && *path != '\0'; i++, path++, next_cwd_len++) token[i] = *path; if (*path == '\0') @@ -404,7 +422,7 @@ next: ext2_get_inode(¤t_inode, fd, dir->inode); while ((current_inode.i_mode & FMT_MASK) != S_IFDIR) { if ((current_inode.i_mode & FMT_MASK) == S_IFLNK) { - if (!symlink_to_inode(¤t_inode, fd)) { + if (!symlink_to_inode(¤t_inode, fd, cwd, cwd_len)) { ret = false; goto out; } @@ -415,6 +433,7 @@ next: } } pmm_free(alloc_map, current_inode.i_blocks_count * sizeof(uint32_t)); + cwd_len = next_cwd_len; goto next; } } @@ -471,7 +490,18 @@ struct file_handle *ext2_open(struct volume *part, const char *path) { struct ext2_dir_entry entry; + size_t cwd_len = 0; + char *cwd = ext_mem_alloc(4096); + for (int i = strlen(path) - 1; i > 0; i--) { + if (path[i] == '/' || path[i] == 0) { + cwd_len = i; + break; + } + } + memcpy(cwd, path, cwd_len); + if (!ext2_parse_dirent(&entry, ret, path)) { + pmm_free(cwd, 4096); pmm_free(ret, sizeof(struct ext2_file_handle)); return NULL; } @@ -480,17 +510,21 @@ struct file_handle *ext2_open(struct volume *part, const char *path) { while ((ret->inode.i_mode & FMT_MASK) != S_IFREG) { if ((ret->inode.i_mode & FMT_MASK) == S_IFLNK) { - if (!symlink_to_inode(&ret->inode, ret)) { + if (!symlink_to_inode(&ret->inode, ret, cwd, cwd_len)) { + pmm_free(cwd, 4096); pmm_free(ret, sizeof(struct ext2_file_handle)); return NULL; } } else { print("ext2: Entity is not regular file nor symlink\n"); + pmm_free(cwd, 4096); pmm_free(ret, sizeof(struct ext2_file_handle)); return NULL; } } + pmm_free(cwd, 4096); + ret->size = ret->inode.i_size; ret->alloc_map = create_alloc_map(ret, &ret->inode); diff --git a/common/lib/blib.h b/common/lib/blib.h index b992d6dd..a6ffa374 100644 --- a/common/lib/blib.h +++ b/common/lib/blib.h @@ -37,6 +37,8 @@ extern bool quiet, serial, editor_enabled; bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf); +void get_absolute_path(char *path_ptr, const char *path, const char *pwd); + uint32_t get_crc32(void *_stream, size_t len); uint32_t oct2bin(uint8_t *str, uint32_t max); diff --git a/common/lib/blib.s2.c b/common/lib/blib.s2.c index 09e84006..079581b2 100644 --- a/common/lib/blib.s2.c +++ b/common/lib/blib.s2.c @@ -41,3 +41,69 @@ uint64_t strtoui(const char *s, const char **end, int base) { } return n; } + +void get_absolute_path(char *path_ptr, const char *path, const char *pwd) { + char *orig_ptr = path_ptr; + + if (!*path) { + strcpy(path_ptr, pwd); + return; + } + + if (*path != '/') { + strcpy(path_ptr, pwd); + path_ptr += strlen(path_ptr); + } else { + *path_ptr = '/'; + path_ptr++; + path++; + } + + goto first_run; + + for (;;) { + switch (*path) { + case '/': + path++; +first_run: + if (*path == '/') continue; + if ((!strncmp(path, ".\0", 2)) + || (!strncmp(path, "./\0", 3))) { + goto term; + } + if ((!strncmp(path, "..\0", 3)) + || (!strncmp(path, "../\0", 4))) { + while (*path_ptr != '/') path_ptr--; + if (path_ptr == orig_ptr) path_ptr++; + goto term; + } + if (!strncmp(path, "../", 3)) { + while (*path_ptr != '/') path_ptr--; + if (path_ptr == orig_ptr) path_ptr++; + path += 2; + *path_ptr = 0; + continue; + } + if (!strncmp(path, "./", 2)) { + path += 1; + continue; + } + if (((path_ptr - 1) != orig_ptr) && (*(path_ptr - 1) != '/')) { + *path_ptr = '/'; + path_ptr++; + } + continue; + case '\0': +term: + if ((*(path_ptr - 1) == '/') && ((path_ptr - 1) != orig_ptr)) + path_ptr--; + *path_ptr = 0; + return; + default: + *path_ptr = *path; + path++; + path_ptr++; + continue; + } + } +}