From 28ac2415edd04d2e5c8fbc9d8b04c7ad18839d78 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sat, 25 Oct 2014 21:04:56 +0100 Subject: [PATCH] WIP Signed-off-by: Daira Hopwood --- .../windows/installer/installer/installer.cpp | 115 +++++++++++++++++- 1 file changed, 110 insertions(+), 5 deletions(-) diff --git a/misc/build_helpers/windows/installer/installer/installer.cpp b/misc/build_helpers/windows/installer/installer/installer.cpp index e8c8e357..6fd38413 100644 --- a/misc/build_helpers/windows/installer/installer/installer.cpp +++ b/misc/build_helpers/windows/installer/installer/installer.cpp @@ -17,6 +17,7 @@ int wmain(int argc, wchar_t *argv[]); void self_extract(wchar_t *destination_dir); +unsigned __int64 read_uint64_le(unsigned char *b); bool have_acceptable_python(); void install_python(); wchar_t * get_default_destination_dir(); @@ -30,7 +31,16 @@ void fail(char *s); #define PYTHON_INSTALLER_32BIT (L"python-" INSTALL_PYTHON_VERSION L".msi") #define PYTHON_INSTALLER_64BIT (L"python-" INSTALL_PYTHON_VERSION L".amd64.msi") +void noop_handler(const wchar_t * expression, + const wchar_t * function, + const wchar_t * file, + unsigned int line, + uintptr_t pReserved) { +} + int wmain(int argc, wchar_t *argv[]) { + _set_invalid_parameter_handler(noop_handler); + if (argc >= 2 && wcscmp(argv[1], L"--help") == 0) { printf("installer \n"); } @@ -54,7 +64,97 @@ void self_extract(wchar_t *destination_dir) { GetModuleFileNameW(hModule, executable_path, MAX_PATH); fail_unless(GetLastError() == ERROR_SUCCESS, "Could not get the path of the current executable."); - unzip(executable_path, destination_dir); + // shell32's zipped folder implementation is strict about the zip format and + // does not support unzipping a self-extracting exe directly. So we copy the + // original zip file that was appended to the exe to a temporary directory, + // and use shell32 to unzip it from there. To get the length of the zip file, + // we look at its "end of central directory record" (not to be confused with + // a "Zip64 end of central directory record"), which is documented at + // . + // For simplicity we only handle the case of a zip file that has no archive + // comment. This code is based loosely on the _EndRecData function in + // . + + // APPNOTE.TXT sections 4.3.15 and 4.3.16. + const size_t sizeof_zip64eocdl = 20, + sizeof_eocd = 22; + unsigned char end_data[sizeof_zip64eocdl + sizeof_eocd]; + unsigned char zip64eocdl_signature[] = {0x50, 0x4B, 0x06, 0x07}; + unsigned char eocd_signature[] = {0x50, 0x4B, 0x05, 0x06}; + unsigned char comment_length[] = {0x00, 0x00}; + unsigned char zip64eocdl_disk_num[] = {0x00, 0x00, 0x00, 0x00}; + unsigned char zip64eocdl_disk_count[] = {0x01, 0x00, 0x00, 0x00}; + + errno = 0; + FILE *f = _wfopen(L"C:\\tahoe\\foo.zip", L"rb"); + fail_unless(f != NULL && errno == 0 && ferror(f) == 0, + "Could not open executable file."); + + _fseeki64(f, -(__int64) sizeof(end_data), SEEK_END); + fail_unless(errno == 0 && ferror(f) == 0, + "Could not seek to end records."); + + __int64 zip64eocdl_offset = _ftelli64(f); + fail_unless(errno == 0 && ferror(f) == 0 && zip64eocdl_offset >= 0, + "Could not read position of end records."); + + printf("zip64eocdl_offset = %ld\n", zip64eocdl_offset); + size_t n = fread(end_data, sizeof(end_data), 1, f); + printf("n = %ld\n", n); + for (size_t i = 0; i < sizeof(end_data); i++) { + printf("%02X ", end_data[i]); + } + printf("\n"); + fail_unless(n == 1 && errno == 0 && ferror(f) == 0, + "Could not read end records."); + + fail_unless(memcmp(end_data + sizeof(end_data) - sizeof(comment_length), + comment_length, sizeof(comment_length)) == 0, + "Cannot read a zip file that has an archive comment."); + + unsigned char *eocd = end_data + sizeof_zip64eocdl; + fail_unless(memcmp(eocd, eocd_signature, sizeof(eocd_signature)) == 0, + "Could not find the end-of-central-directory signature."); + + fail_unless(memcmp(end_data, zip64eocdl_signature, sizeof(zip64eocdl_signature)) == 0, + "Could not find the zip64-end-of-central-directory-locator signature."); + + fail_unless(memcmp(eocd + 4, zip64eocdl_disk_num, sizeof(zip64eocdl_disk_num)) == 0 && + memcmp(eocd + 6, zip64eocdl_disk_num, sizeof(zip64eocdl_disk_num)) == 0 && + memcmp(end_data + 4, zip64eocdl_disk_count, sizeof(zip64eocdl_disk_count)) == 0, + "Cannot read a zipfile that spans disks."); + + unsigned __int64 eocd_relative_offset = read_uint64_le(end_data + 8); + unsigned __int64 eocd_offset = zip64eocdl_offset + sizeof_zip64eocdl; + fail_unless(eocd_relative_offset <= 0x7FFFFFFFFFFFFFFFi64 && eocd_offset <= 0x7FFFFFFFFFFFFFFFi64, + "Could not calculate zipfile offset due to potential integer overflow."); + + __int64 zipfile_offset = eocd_offset - eocd_relative_offset; + fail_unless(zipfile_offset >= 0 && zipfile_offset <= zip64eocdl_offset, + "Unexpected result from zipfile offset calculation."); + + printf("zipfile_offset = %ld\n", zipfile_offset); + _fseeki64(f, zipfile_offset, SEEK_SET); + fail_unless(errno == 0 && ferror(f) == 0, + "Could not seek to zipfile offset."); + + printf("%ld\n", zipfile_offset); + //unzip(L"C:\\tahoe\\foo.zip", destination_dir); +} + +// read unsigned little-endian 64-bit integer +unsigned __int64 read_uint64_le(unsigned char *b) { + return ((unsigned __int64) b[0] ) | + ((unsigned __int64) b[1] << 8) | + ((unsigned __int64) b[2] << 16) | + ((unsigned __int64) b[3] << 24) | + ((unsigned __int64) b[4] << 32) | + ((unsigned __int64) b[5] << 40) | + ((unsigned __int64) b[6] << 48) | + ((unsigned __int64) b[7] << 56); +} + +void read_from_end(FILE *f, size_t offset, unsigned char *dest, size_t length) { } bool have_acceptable_python() { @@ -76,6 +176,8 @@ wchar_t * get_default_destination_dir() { } void unzip(wchar_t *zip_path, wchar_t *destination_dir) { + // Based on + wprintf(L"Extracting %ls\nto %ls\n", zip_path, destination_dir); // SysAllocString: @@ -103,17 +205,20 @@ void unzip(wchar_t *zip_path, wchar_t *destination_dir) { // Folder.NameSpace: Folder *zip_folder = NULL; res = shell->NameSpace(zip_path_var, &zip_folder); - printf("%d", res); - fail_unless(zip_folder != NULL, "Could not create zip Folder object."); + fail_unless(res == S_OK && zip_folder != NULL, "Could not create zip Folder object."); Folder *destination_folder = NULL; - shell->NameSpace(destination_dir_var, &destination_folder); - fail_unless(destination_folder != NULL, "Could not create destination Folder object."); + res = shell->NameSpace(destination_dir_var, &destination_folder); + fail_unless(res == S_OK && destination_folder != NULL, "Could not create destination Folder object."); FolderItems *zip_folderitems = NULL; zip_folder->Items(&zip_folderitems); fail_unless(zip_folderitems != NULL, "Could not create zip FolderItems object."); + long files_count = 0; + zip_folderitems->get_Count(&files_count); + printf("count %d\n", files_count); + VARIANT zip_idispatch_var; zip_idispatch_var.vt = VT_DISPATCH; zip_idispatch_var.pdispVal = NULL; -- 2.45.2