]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
WIP
authorDaira Hopwood <daira@jacaranda.org>
Sat, 25 Oct 2014 20:04:56 +0000 (21:04 +0100)
committerDaira Hopwood <daira@jacaranda.org>
Sat, 25 Oct 2014 20:04:56 +0000 (21:04 +0100)
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
misc/build_helpers/windows/installer/installer/installer.cpp

index e8c8e3577a52e75edb39293bcf1abf8188baf565..6fd3841342e731e0b191efc644ef9ac6bf3594c4 100644 (file)
@@ -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 <destination_dir>\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
+       // <http://www.pkware.com/documents/casestudies/APPNOTE.TXT>.
+       // 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
+       // <https://hg.python.org/cpython/file/2.7/Lib/zipfile.py>.
+
+       // 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 <https://social.msdn.microsoft.com/Forums/vstudio/en-US/45668d18-2840-4887-87e1-4085201f4103/visual-c-to-unzip-a-zip-file-to-a-specific-directory?forum=vclanguage>
+
        wprintf(L"Extracting %ls\nto %ls\n", zip_path, destination_dir);
 
        // SysAllocString: <http://msdn.microsoft.com/en-gb/library/windows/desktop/ms221458(v=vs.85).aspx>
@@ -103,17 +205,20 @@ void unzip(wchar_t *zip_path, wchar_t *destination_dir) {
        // Folder.NameSpace: <http://msdn.microsoft.com/en-gb/library/windows/desktop/gg537721(v=vs.85).aspx>
        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;