diff -Nupr a/squashfs-tools/Makefile b/squashfs-tools/Makefile --- a/squashfs-tools/Makefile 2023-06-26 23:18:33.104153303 -0500 +++ b/squashfs-tools/Makefile 2023-06-26 23:19:49.230107688 -0500 @@ -156,8 +156,8 @@ MKSQUASHFS_OBJS = mksquashfs.o read_fs.o caches-queues-lists.o UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \ - unsquash-4.o unsquash-123.o unsquash-34.o unsquash-1234.o swap.o \ - compressor.o unsquashfs_info.o + unsquash-4.o unsquash-123.o unsquash-34.o unsquash-1234.o unsquash-12.o \ + swap.o compressor.o unsquashfs_info.o CFLAGS ?= -O2 CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \ @@ -353,6 +353,8 @@ unsquash-34.o: unsquashfs.h unsquash-34. unsquash-1234.o: unsquash-1234.c +unsquash-12.o: unsquash-12.c unsquashfs.h + unsquashfs_xattr.o: unsquashfs_xattr.c unsquashfs.h squashfs_fs.h xattr.h unsquashfs_info.o: unsquashfs.h squashfs_fs.h diff -Nupr a/squashfs-tools/unsquash-1234.c b/squashfs-tools/unsquash-1234.c --- a/squashfs-tools/unsquash-1234.c 2023-06-26 23:18:33.104153303 -0500 +++ b/squashfs-tools/unsquash-1234.c 2023-06-26 23:19:49.230107688 -0500 @@ -25,8 +25,8 @@ * unsquash-4. */ -#define TRUE 1 -#define FALSE 0 +#include "unsquashfs.h" + /* * Check name for validity, name should not * - be ".", "./", or @@ -56,3 +56,40 @@ int check_name(char *name, int size) return TRUE; } + + +void squashfs_closedir(struct dir *dir) +{ + struct dir_ent *ent = dir->dirs; + + while(ent) { + struct dir_ent *tmp = ent; + + ent = ent->next; + free(tmp->name); + free(tmp); + } + + free(dir); +} + + +/* + * Check directory for duplicate names. As the directory should be sorted, + * duplicates will be consecutive. Obviously we also need to check if the + * directory has been deliberately unsorted, to evade this check. + */ +int check_directory(struct dir *dir) +{ + int i; + struct dir_ent *ent; + + if (dir->dir_count < 2) + return TRUE; + + for (ent = dir->dirs, i = 0; i < dir->dir_count - 1; ent = ent->next, i++) + if (strcmp(ent->name, ent->next->name) >= 0) + return FALSE; + + return TRUE; +} diff -Nupr a/squashfs-tools/unsquash-12.c b/squashfs-tools/unsquash-12.c --- a/squashfs-tools/unsquash-12.c 1969-12-31 18:00:00.000000000 -0600 +++ b/squashfs-tools/unsquash-12.c 2023-06-26 23:19:49.230107688 -0500 @@ -0,0 +1,103 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2021 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquash-12.c + * + * Helper functions used by unsquash-1 and unsquash-2. + */ + +#include "unsquashfs.h" + +/* + * Bottom up linked list merge sort. + * + */ +void sort_directory(struct dir *dir) +{ + struct dir_ent *cur, *l1, *l2, *next; + int len1, len2, stride = 1; + + if (dir->dir_count < 2) + return; + + /* + * We can consider our linked-list to be made up of stride length + * sublists. Each iteration around this loop merges adjacent + * stride length sublists into larger 2*stride sublists. We stop + * when stride becomes equal to the entire list. + * + * Initially stride = 1 (by definition a sublist of 1 is sorted), and + * these 1 element sublists are merged into 2 element sublists, which + * are then merged into 4 element sublists and so on. + */ + do { + l2 = dir->dirs; /* head of current linked list */ + cur = NULL; /* empty output list */ + + /* + * Iterate through the linked list, merging adjacent sublists. + * On each interation l2 points to the next sublist pair to be + * merged (if there's only one sublist left this is simply added + * to the output list) + */ + while (l2) { + l1 = l2; + for (len1 = 0; l2 && len1 < stride; len1 ++, l2 = l2->next); + len2 = stride; + + /* + * l1 points to first sublist. + * l2 points to second sublist. + * Merge them onto the output list + */ + while (len1 && l2 && len2) { + if(strcmp(l1->name, l2->name) <= 0) { + next = l1; + l1 = l1->next; + len1 --; + } else { + next = l2; + l2 = l2->next; + len2 --; + } + + if (cur) { + cur->next = next; + cur = next; + } else + dir->dirs = cur = next; + } + /* + * One sublist is now empty, copy the other one onto the + * output list + */ + for (; len1; len1 --, l1 = l1->next) { + if(cur) { + cur->next = l1; + cur = l1; + } else + dir->dirs = cur = l1; + } + } + cur->next = NULL; + stride = stride << 1; + } while(stride < dir->dir_count); +} diff -Nupr a/squashfs-tools/unsquash-1.c b/squashfs-tools/unsquash-1.c --- a/squashfs-tools/unsquash-1.c 2023-06-26 23:18:33.104153303 -0500 +++ b/squashfs-tools/unsquash-1.c 2023-06-26 23:19:49.230107688 -0500 @@ -207,7 +207,7 @@ static struct dir *squashfs_opendir(unsi long long start; int bytes; int dir_count, size; - struct dir_ent *new_dir; + struct dir_ent *ent, *cur_ent = NULL; struct dir *dir; TRACE("squashfs_opendir: inode start block %d, offset %d\n", @@ -220,7 +220,7 @@ static struct dir *squashfs_opendir(unsi EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; - dir->cur_entry = 0; + dir->cur_entry = NULL; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; @@ -295,29 +295,36 @@ static struct dir *squashfs_opendir(unsi TRACE("squashfs_opendir: directory entry %s, inode " "%d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); - if((dir->dir_count % DIR_ENT_SIZE) == 0) { - new_dir = realloc(dir->dirs, (dir->dir_count + - DIR_ENT_SIZE) * sizeof(struct dir_ent)); - if(new_dir == NULL) - EXIT_UNSQUASH("squashfs_opendir: " - "realloc failed!\n"); - dir->dirs = new_dir; - } - strcpy(dir->dirs[dir->dir_count].name, dire->name); - dir->dirs[dir->dir_count].start_block = - dirh.start_block; - dir->dirs[dir->dir_count].offset = dire->offset; - dir->dirs[dir->dir_count].type = dire->type; + + ent = malloc(sizeof(struct dir_ent)); + if(ent == NULL) + MEM_ERROR(); + + ent->name = strdup(dire->name); + ent->start_block = dirh.start_block; + ent->offset = dire->offset; + ent->type = dire->type; + ent->next = NULL; + if (cur_ent == NULL) + dir->dirs = ent; + else + cur_ent->next = ent; + cur_ent = ent; dir->dir_count ++; bytes += dire->size + 1; } } + /* check directory for duplicate names. Need to sort directory first */ + sort_directory(dir); + if (check_directory(dir) == FALSE) { + ERROR("File system corrupted: directory has duplicate names\n"); + goto corrupted; + } return dir; corrupted: - free(dir->dirs); - free(dir); + squashfs_closedir(dir); return NULL; } diff -Nupr a/squashfs-tools/unsquash-2.c b/squashfs-tools/unsquash-2.c --- a/squashfs-tools/unsquash-2.c 2023-06-26 23:18:33.104153303 -0500 +++ b/squashfs-tools/unsquash-2.c 2023-06-26 23:19:49.230107688 -0500 @@ -29,6 +29,7 @@ static squashfs_fragment_entry_2 *fragme static unsigned int *uid_table, *guid_table; static char *inode_table, *directory_table; static squashfs_operations ops; +int needs_sorting = FALSE; static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks) { @@ -308,7 +309,7 @@ static struct dir *squashfs_opendir(unsi long long start; int bytes; int dir_count, size; - struct dir_ent *new_dir; + struct dir_ent *ent, *cur_ent = NULL; struct dir *dir; TRACE("squashfs_opendir: inode start block %d, offset %d\n", @@ -321,7 +322,7 @@ static struct dir *squashfs_opendir(unsi EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; - dir->cur_entry = 0; + dir->cur_entry = NULL; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; @@ -396,29 +397,41 @@ static struct dir *squashfs_opendir(unsi TRACE("squashfs_opendir: directory entry %s, inode " "%d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); - if((dir->dir_count % DIR_ENT_SIZE) == 0) { - new_dir = realloc(dir->dirs, (dir->dir_count + - DIR_ENT_SIZE) * sizeof(struct dir_ent)); - if(new_dir == NULL) - EXIT_UNSQUASH("squashfs_opendir: " - "realloc failed!\n"); - dir->dirs = new_dir; - } - strcpy(dir->dirs[dir->dir_count].name, dire->name); - dir->dirs[dir->dir_count].start_block = - dirh.start_block; - dir->dirs[dir->dir_count].offset = dire->offset; - dir->dirs[dir->dir_count].type = dire->type; + + ent = malloc(sizeof(struct dir_ent)); + if (ent == NULL) + MEM_ERROR(); + + ent->name = strdup(dire->name); + ent->start_block = dirh.start_block; + ent->offset = dire->offset; + ent->type = dire->type; + ent->next = NULL; + if (cur_ent == NULL) + dir->dirs = ent; + else + cur_ent->next = ent; + cur_ent = ent; dir->dir_count ++; bytes += dire->size + 1; } } + if (needs_sorting) + sort_directory(dir); + + /* check directory for duplicate names and sorting */ + if (check_directory(dir) == FALSE) { + if (needs_sorting) + ERROR("File system corrupted: directory has duplicate names\n"); + else + ERROR("File system corrupted: directory has duplicate names or is unsorted\n"); + goto corrupted; + } return dir; corrupted: - free(dir->dirs); - free(dir); + squashfs_closedir(dir); return NULL; } diff -Nupr a/squashfs-tools/unsquash-3.c b/squashfs-tools/unsquash-3.c --- a/squashfs-tools/unsquash-3.c 2023-06-26 23:18:33.105153302 -0500 +++ b/squashfs-tools/unsquash-3.c 2023-06-26 23:19:49.230107688 -0500 @@ -334,7 +334,7 @@ static struct dir *squashfs_opendir(unsi long long start; int bytes; int dir_count, size; - struct dir_ent *new_dir; + struct dir_ent *ent, *cur_ent = NULL; struct dir *dir; TRACE("squashfs_opendir: inode start block %d, offset %d\n", @@ -347,7 +347,7 @@ static struct dir *squashfs_opendir(unsi EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; - dir->cur_entry = 0; + dir->cur_entry = NULL; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; @@ -423,29 +423,36 @@ static struct dir *squashfs_opendir(unsi TRACE("squashfs_opendir: directory entry %s, inode " "%d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); - if((dir->dir_count % DIR_ENT_SIZE) == 0) { - new_dir = realloc(dir->dirs, (dir->dir_count + - DIR_ENT_SIZE) * sizeof(struct dir_ent)); - if(new_dir == NULL) - EXIT_UNSQUASH("squashfs_opendir: " - "realloc failed!\n"); - dir->dirs = new_dir; - } - strcpy(dir->dirs[dir->dir_count].name, dire->name); - dir->dirs[dir->dir_count].start_block = - dirh.start_block; - dir->dirs[dir->dir_count].offset = dire->offset; - dir->dirs[dir->dir_count].type = dire->type; + + ent = malloc(sizeof(struct dir_ent)); + if (ent == NULL) + MEM_ERROR(); + + ent->name = strdup(dire->name); + ent->start_block = dirh.start_block; + ent->offset = dire->offset; + ent->type = dire->type; + ent->next = NULL; + if (cur_ent == NULL) + dir->dirs = ent; + else + cur_ent->next = ent; + cur_ent = ent; dir->dir_count ++; bytes += dire->size + 1; } } + /* check directory for duplicate names and sorting */ + if (check_directory(dir) == FALSE) { + ERROR("File system corrupted: directory has duplicate names or is unsorted\n"); + goto corrupted; + } + return dir; corrupted: - free(dir->dirs); - free(dir); + squashfs_closedir(dir); return NULL; } diff -Nupr a/squashfs-tools/unsquash-4.c b/squashfs-tools/unsquash-4.c --- a/squashfs-tools/unsquash-4.c 2023-06-26 23:18:33.105153302 -0500 +++ b/squashfs-tools/unsquash-4.c 2023-06-26 23:19:49.231107688 -0500 @@ -281,7 +281,7 @@ static struct dir *squashfs_opendir(unsi long long start; long long bytes; int dir_count, size; - struct dir_ent *new_dir; + struct dir_ent *ent, *cur_ent = NULL; struct dir *dir; TRACE("squashfs_opendir: inode start block %d, offset %d\n", @@ -294,7 +294,7 @@ static struct dir *squashfs_opendir(unsi EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; - dir->cur_entry = 0; + dir->cur_entry = NULL; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; @@ -359,29 +359,36 @@ static struct dir *squashfs_opendir(unsi TRACE("squashfs_opendir: directory entry %s, inode " "%d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); - if((dir->dir_count % DIR_ENT_SIZE) == 0) { - new_dir = realloc(dir->dirs, (dir->dir_count + - DIR_ENT_SIZE) * sizeof(struct dir_ent)); - if(new_dir == NULL) - EXIT_UNSQUASH("squashfs_opendir: " - "realloc failed!\n"); - dir->dirs = new_dir; - } - strcpy(dir->dirs[dir->dir_count].name, dire->name); - dir->dirs[dir->dir_count].start_block = - dirh.start_block; - dir->dirs[dir->dir_count].offset = dire->offset; - dir->dirs[dir->dir_count].type = dire->type; + + ent = malloc(sizeof(struct dir_ent)); + if (ent == NULL) + MEM_ERROR(); + + ent->name = strdup(dire->name); + ent->start_block = dirh.start_block; + ent->offset = dire->offset; + ent->type = dire->type; + ent->next = NULL; + if (cur_ent == NULL) + dir->dirs = ent; + else + cur_ent->next = ent; + cur_ent = ent; dir->dir_count ++; bytes += dire->size + 1; } } + /* check directory for duplicate names and sorting */ + if (check_directory(dir) == FALSE) { + ERROR("File system corrupted: directory has duplicate names or is unsorted\n"); + goto corrupted; + } + return dir; corrupted: - free(dir->dirs); - free(dir); + squashfs_closedir(dir); return NULL; } diff -Nupr a/squashfs-tools/unsquashfs.c b/squashfs-tools/unsquashfs.c --- a/squashfs-tools/unsquashfs.c 2023-06-26 23:18:33.105153302 -0500 +++ b/squashfs-tools/unsquashfs.c 2023-06-26 23:19:49.231107688 -0500 @@ -78,6 +78,7 @@ int user_xattrs = FALSE; int ignore_errors = FALSE; int strict_errors = FALSE; int use_localtime = TRUE; +extern int needs_sorting; int lookup_type[] = { 0, @@ -1287,26 +1288,23 @@ failed: int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, unsigned int *offset, unsigned int *type) { - if(dir->cur_entry == dir->dir_count) + if(dir->cur_entry == NULL) + dir->cur_entry = dir->dirs; + else + dir->cur_entry = dir->cur_entry->next; + + if (dir->cur_entry == NULL) return FALSE; - *name = dir->dirs[dir->cur_entry].name; - *start_block = dir->dirs[dir->cur_entry].start_block; - *offset = dir->dirs[dir->cur_entry].offset; - *type = dir->dirs[dir->cur_entry].type; - dir->cur_entry ++; + *name = dir->cur_entry->name; + *start_block = dir->cur_entry->start_block; + *offset = dir->cur_entry->offset; + *type = dir->cur_entry->type; return TRUE; } -void squashfs_closedir(struct dir *dir) -{ - free(dir->dirs); - free(dir); -} - - char *get_component(char *target, char **targname) { char *start; @@ -1959,6 +1957,10 @@ int read_super(char *source) * 1.x, 2.x and 3.x filesystems use gzip compression. */ comp = lookup_compressor("gzip"); + + if (sBlk_3.s_minor == 0) + needs_sorting = TRUE; + return TRUE; failed_mount: diff -Nupr a/squashfs-tools/unsquashfs.h b/squashfs-tools/unsquashfs.h --- a/squashfs-tools/unsquashfs.h 2023-06-26 23:18:33.106153301 -0500 +++ b/squashfs-tools/unsquashfs.h 2023-06-26 23:19:49.231107688 -0500 @@ -165,21 +165,22 @@ struct queue { #define DIR_ENT_SIZE 16 struct dir_ent { - char name[SQUASHFS_NAME_LEN + 1]; + char *name; unsigned int start_block; unsigned int offset; unsigned int type; + struct dir_ent *next; }; struct dir { int dir_count; - int cur_entry; unsigned int mode; uid_t uid; gid_t guid; unsigned int mtime; unsigned int xattr; struct dir_ent *dirs; + struct dir_ent *cur_entry; }; struct file_entry { @@ -264,4 +265,9 @@ extern long long *alloc_index_table(int) /* unsquash-1234.c */ extern int check_name(char *, int); +extern void squashfs_closedir(struct dir *); +extern int check_directory(struct dir *); + +/* unsquash-12.c */ +extern void sort_directory(struct dir *); #endif