ext: Properly follow relative symlinks
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 e915a8c6..0565587f 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;
+ }
+ }
+}
