ext: Properly follow relative symlinks

This commit is contained in:
mintsuki 2022-08-21 00:50:12 +02:00
parent af4fb9a387
commit fe637af458
3 changed files with 107 additions and 5 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}
}