diff --git a/unpack.c b/unpack.c index 8756e91..26401ed 100644 --- a/unpack.c +++ b/unpack.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -19,6 +20,11 @@ typedef struct { char error_message[256]; } ExtractedArchive; +typedef struct { + char *linkname; + char *target; +} SymlinkInfo; + ExtractedArchive* error_handler(ExtractedArchive* result, const char *error_message, struct archive* archive) { if (!result || !archive) { @@ -33,6 +39,43 @@ ExtractedArchive* error_handler(ExtractedArchive* result, const char *error_mess return result; } +static char* join_paths(const char *dir, const char *relative) { + if (!dir || !*dir) return strdup(relative); + size_t len = strlen(dir) + 1 + strlen(relative) + 1; + char *buf = malloc(len); + snprintf(buf, len, "%s/%s", dir, relative); + return buf; +} + +static const FileData *resolve_symlink( + const FileData *files, size_t file_count, + const SymlinkInfo *symlinks, size_t symlink_count, + const char *target, int depth +) { + if (!target || depth > 32) // prevent infinite recursion + return NULL; + + // First, check if target is a regular file + for (size_t i = 0; i < file_count; i++) { + if (strcmp(files[i].filename, target) == 0) { + if (files[i].data && files[i].data_size > 0) { + return &files[i]; // Found real file + } + } + } + + // If not found among files, maybe it's another symlink + for (size_t i = 0; i < symlink_count; i++) { + if (strcmp(symlinks[i].linkname, target) == 0) { + // Recurse into that symlink's target + return resolve_symlink(files, file_count, symlinks, symlink_count, + symlinks[i].target, depth + 1); + } + } + + return NULL; // Not found +} + EMSCRIPTEN_KEEPALIVE ExtractedArchive* extract_archive(uint8_t* inputData, size_t inputSize ) { struct archive* archive; @@ -40,7 +83,8 @@ ExtractedArchive* extract_archive(uint8_t* inputData, size_t inputSize ) { size_t files_struct_length = 100; FileData* files = NULL; size_t files_count = 0; - const char *error_message; + const char* error_message; + bool hasSymLinks = false; ExtractedArchive* result = (ExtractedArchive*)malloc(sizeof(ExtractedArchive)); if (!result) { @@ -57,24 +101,32 @@ ExtractedArchive* extract_archive(uint8_t* inputData, size_t inputSize ) { archive_read_support_format_all(archive); if (archive_read_open_memory(archive, inputData, inputSize) != ARCHIVE_OK) { - return error_handler(result,archive_error_string(archive), archive); + return error_handler(result,archive_error_string(archive), archive); } files = malloc(sizeof(FileData) * files_struct_length); while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) { const char* filename = archive_entry_pathname(entry); size_t entrySize = archive_entry_size(entry); + + // Ignore symbolic links for now + if (archive_entry_filetype(entry) == AE_IFLNK) { + hasSymLinks = true; + continue; + } + if (files_count + 1 > files_struct_length) { files_struct_length *= 2; // double the length FileData* oldfiles = files; - files= realloc(files, sizeof(FileData) * files_struct_length); + files = realloc(files, sizeof(FileData) * files_struct_length); if (!files) { result->fileCount = files_count; result->files = oldfiles; // otherwise memory is lost, alternatively also everything can be freed. error_message = "Memory allocation error for file data."; return error_handler(result, error_message, archive); - } + } } + files[files_count].filename = strdup(filename); files[files_count].data = malloc(entrySize); files[files_count].data_size = entrySize; @@ -105,6 +157,94 @@ ExtractedArchive* extract_archive(uint8_t* inputData, size_t inputSize ) { files_count++; } + // Resolve symlinks + if (hasSymLinks) { + // Reopen the archive to iterate over symlinks + archive_read_free(archive); + archive = archive_read_new(); + archive_read_support_filter_all(archive); + archive_read_support_format_all(archive); + + if (archive_read_open_memory(archive, inputData, inputSize) != ARCHIVE_OK) { + return error_handler(result, archive_error_string(archive), archive); + } + + struct archive_entry *symlink_entry; + + size_t symlink_count = 0; + size_t symlink_alloc = 16; + SymlinkInfo *symlinks = malloc(sizeof(SymlinkInfo) * symlink_alloc); + + // Collect all symlink entries + while (archive_read_next_header(archive, &symlink_entry) == ARCHIVE_OK) { + if (archive_entry_filetype(symlink_entry) != AE_IFLNK) + continue; + + const char *tgt = archive_entry_symlink(symlink_entry); + + if (!tgt) { + continue; + } + + if (symlink_count + 1 > symlink_alloc) { + symlink_alloc *= 2; + symlinks = realloc(symlinks, sizeof(SymlinkInfo) * symlink_alloc); + } + + // Compute directory of the symlink + char *link_dir = strdup(archive_entry_pathname(symlink_entry)); + char *dir = dirname(link_dir); + char *resolved_target_path = join_paths(dir, tgt); + free(dir); + free(link_dir); + + symlinks[symlink_count].linkname = strdup(archive_entry_pathname(symlink_entry)); + symlinks[symlink_count].target = strdup(resolved_target_path); + symlink_count++; + } + + // Resolve and populate symlinks + for (size_t i = 0; i < symlink_count; i++) { + const char *linkname = symlinks[i].linkname; + const char *target = symlinks[i].target; + + const FileData *resolved = resolve_symlink(files, files_count, + symlinks, symlink_count, + target, 0); + + if (!resolved) { + error_message = "Failed to resolve symlink."; + return error_handler(result, error_message, archive); + } + + if (files_count + 1 > files_struct_length) { + files_struct_length *= 2; + FileData *oldfiles = files; + files = realloc(files, sizeof(FileData) * files_struct_length); + if (!files) { + result->fileCount = files_count; + result->files = oldfiles; + error_message = "Memory allocation error for symlink data."; + return error_handler(result, error_message, archive); + } + } + + files[files_count].filename = strdup(linkname); + + files[files_count].data_size = resolved->data_size; + files[files_count].data = malloc(resolved->data_size); + memcpy(files[files_count].data, resolved->data, resolved->data_size); + + files_count++; + } + + for (size_t i = 0; i < symlink_count; i++) { + free(symlinks[i].linkname); + free(symlinks[i].target); + } + free(symlinks); + } + archive_read_free(archive); result->files = files; result->fileCount = files_count; @@ -150,7 +290,7 @@ ExtractedArchive* decompression(uint8_t* inputData, size_t inputSize) { const size_t buffsize = 64 * 1024; char buff[buffsize]; - size_t total_size = 0; + size_t total_size = 0; const char *error_message; FileData* files = malloc(sizeof(FileData) * (files_count + 1)); @@ -159,7 +299,7 @@ ExtractedArchive* decompression(uint8_t* inputData, size_t inputSize) { printf("Failed to allocate memory for files array\n"); return NULL; } - + ExtractedArchive* result = (ExtractedArchive*)malloc(sizeof(ExtractedArchive)); if (!result) { free(files); @@ -259,4 +399,4 @@ void free_extracted_archive(ExtractedArchive* archive) { } free(archive->files); free(archive); -} \ No newline at end of file +}