:: commit 9a8730875dd587beb7add773b2d16644e027edc9

mintsuki <mintsuki@protonmail.com> — 2022-08-20 22:50

parents: 0c9bf88a57

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(&current_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(&current_inode, fd)) {
+                        if (!symlink_to_inode(&current_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;
+        }
+    }
+}
tab: 248 wrap: offon