]> wimlib.net Git - wimlib/blobdiff - tests/wlfuzz.c
Fix wlfuzz and enable in CI
[wimlib] / tests / wlfuzz.c
index 51607869fb11913b3ddc0a0b9e7093e0658eb34f..e3fe909dd7d7059720a28a570bf50ce64e9dd4ec 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2015-2016 Eric Biggers
+ * Copyright 2015-2023 Eric Biggers
  *
  * 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
@@ -16,7 +16,7 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 /*
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #ifdef WITH_NTFS_3G
 #  include <sys/wait.h>
 #endif
 #include <unistd.h>
 
-#ifdef __WIN32__
+#ifdef _WIN32
 #  include <windows.h>
 #  include <winternl.h>
 #  include <ntstatus.h>
+#else
+#  include <linux/magic.h>
+#  include <sys/vfs.h>
 #endif
 
 #include "wimlib.h"
@@ -89,6 +93,9 @@
 static bool wimfile_in_use[MAX_NUM_WIMS];
 static int in_use_wimfile_indices[MAX_NUM_WIMS];
 static int num_wimfiles_in_use = 0;
+#ifndef _WIN32
+static u32 filesystem_type;
+#endif
 
 static void
 assertion_failed(int line, const char *format, ...)
@@ -119,16 +126,23 @@ assertion_failed(int line, const char *format, ...)
 static void
 change_to_temporary_directory(void)
 {
-#ifdef __WIN32__
-       ASSERT(SetCurrentDirectory(L"E:\\"),
-              "failed to change directory to E:\\");
-#else
-       const char *tmpdir = getenv("TMPDIR");
-       if (!tmpdir)
-               tmpdir = P_tmpdir;
+#ifdef _WIN32
+       const wchar_t *tmpdir = _wgetenv(T("TMPDIR"));
+
+       ASSERT(tmpdir != NULL, "TMPDIR must be set");
+       _wmkdir(tmpdir);
+       ASSERT(!_wchdir(tmpdir),
+              "failed to change to temporary directory '%ls'", tmpdir);
+#else /* _WIN32 */
+       const char *tmpdir = getenv("TMPDIR") ?: P_tmpdir;
+       struct statfs fs;
+
+       mkdir(tmpdir, 0700);
        ASSERT(!chdir(tmpdir),
-              "failed to change to temporary directory \"%s\": %m", tmpdir);
-#endif
+              "failed to change to temporary directory '%s': %m", tmpdir);
+       ASSERT(!statfs(".", &fs), "statfs of '%s' failed: %m", tmpdir);
+       filesystem_type = fs.f_type;
+#endif /* !_WIN32 */
 }
 
 static void __attribute__((unused))
@@ -186,19 +200,19 @@ create_ntfs_volume(const char *name)
 }
 #endif /* WITH_NTFS_3G */
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
-extern WINAPI NTSTATUS NtQueryDirectoryFile (HANDLE FileHandle,
-                                            HANDLE Event,
-                                            PIO_APC_ROUTINE ApcRoutine,
-                                            PVOID ApcContext,
-                                            PIO_STATUS_BLOCK IoStatusBlock,
-                                            PVOID FileInformation,
-                                            ULONG Length,
-                                            FILE_INFORMATION_CLASS FileInformationClass,
-                                            BOOLEAN ReturnSingleEntry,
-                                            PUNICODE_STRING FileName,
-                                            BOOLEAN RestartScan);
+WINAPI NTSTATUS NtQueryDirectoryFile(HANDLE FileHandle,
+                                    HANDLE Event,
+                                    PIO_APC_ROUTINE ApcRoutine,
+                                    PVOID ApcContext,
+                                    PIO_STATUS_BLOCK IoStatusBlock,
+                                    PVOID FileInformation,
+                                    ULONG Length,
+                                    FILE_INFORMATION_CLASS FileInformationClass,
+                                    BOOLEAN ReturnSingleEntry,
+                                    PUNICODE_STRING FileName,
+                                    BOOLEAN RestartScan);
 
 static void
 delete_directory_tree_recursive(HANDLE cur_dir, UNICODE_STRING *name)
@@ -300,7 +314,7 @@ delete_directory_tree(const wchar_t *name)
        ASSERT(GetFileAttributes(name) == 0xFFFFFFFF, "Deletion didn't work!");
 }
 
-#else /* __WIN32__ */
+#else /* _WIN32 */
 
 static void
 delete_directory_tree_recursive(int dirfd, const char *name)
@@ -332,22 +346,28 @@ delete_directory_tree(const tchar *name)
        delete_directory_tree_recursive(AT_FDCWD, name);
 }
 
-#endif /* !__WIN32__ */
+#endif /* !_WIN32 */
+
+static u64 random_state;
 
-static uint32_t
+static u32
 rand32(void)
 {
-       static uint64_t state;
-
-       /* A simple linear congruential generator  */
-       state = (state * 25214903917 + 11) & (((uint64_t)1 << 48) - 1);
-       return state >> 16;
+       /* A simple linear congruential generator */
+       random_state = (random_state * 25214903917 + 11) % (1ULL << 48);
+       return random_state >> 16;
 }
 
-static inline bool
+static bool
 randbool(void)
 {
-       return rand32() & 1;
+       return rand32() % 2;
+}
+
+static u64
+rand64(void)
+{
+       return ((u64)rand32() << 32) | rand32();
 }
 
 static tchar wimfile[32];
@@ -411,7 +431,7 @@ get_image_count(WIMStruct *wim)
        return info.image_count;
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 static bool
 is_wimboot_capable(WIMStruct *wim)
 {
@@ -426,7 +446,7 @@ is_wimboot_capable(WIMStruct *wim)
                 (info.compression_type == WIMLIB_COMPRESSION_TYPE_LZX &&
                  info.chunk_size == 32768));
 }
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
 
 static void
 overwrite_wim(WIMStruct *wim)
@@ -498,48 +518,67 @@ get_random_write_flags(void)
        return write_flags;
 }
 
+static u32
+get_random_chunk_size(int min_order, int max_order)
+{
+       return 1 << (min_order + (rand32() % (max_order - min_order + 1)));
+}
+
 static void
 op__create_new_wim(void)
 {
        printf(":::op__create_new_wim\n");
 
        const tchar *wimfile;
-       WIMStruct *wim;
+       enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_NONE;
+       u32 chunk_size = 0;
+       u32 solid_chunk_size = 0;
        int write_flags;
+       WIMStruct *wim;
 
        if (num_wimfiles_in_use == MAX_NUM_WIMS)
                return;
 
        wimfile = select_new_wimfile();
 
-       CHECK_RET(wimlib_create_new_wim(WIMLIB_COMPRESSION_TYPE_NONE, &wim));
-
        /* Select a random compression type and chunk size.  */
-       switch (rand32() % 8) {
-       default:
-               CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_NONE));
+       switch (rand32() % 4) {
+       case 0:
                break;
-       case 3 ... 4:
-               CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_XPRESS));
-               CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << (12 + rand32() % 5)));
+       case 1:
+               ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
+               chunk_size = get_random_chunk_size(12, 16);
                break;
-       case 5 ... 6:
-               CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_LZX));
+       case 2:
+               ctype = WIMLIB_COMPRESSION_TYPE_LZX;
                if (randbool())
-                       CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << 15));
+                       chunk_size = 1 << 15;
                else
-                       CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << (15 + rand32() % 7)));
+                       chunk_size = get_random_chunk_size(15, 21);
                break;
-       case 7:
-               CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_LZMS));
-               CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << (15 + rand32() % 12)));
+       case 3:
+               ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
+               chunk_size = get_random_chunk_size(15, 28);
+               if (randbool())
+                       solid_chunk_size = get_random_chunk_size(15, 26);
+               else
+                       solid_chunk_size = get_random_chunk_size(26, 28);
                break;
        }
 
        /* Select random write flags.  */
        write_flags = get_random_write_flags();
 
-       printf("Creating %"TS" with write flags 0x%08x\n", wimfile, write_flags);
+       printf("Creating %"TS" with write flags 0x%08x, compression_type=%"TS", chunk_size=%u, solid_chunk_size=%u\n",
+              wimfile, write_flags,
+              wimlib_get_compression_type_string(ctype),
+              chunk_size, solid_chunk_size);
+
+       CHECK_RET(wimlib_create_new_wim(ctype, &wim));
+       if (chunk_size != 0)
+               CHECK_RET(wimlib_set_output_chunk_size(wim, chunk_size));
+       if (solid_chunk_size != 0)
+               CHECK_RET(wimlib_set_output_pack_chunk_size(wim, solid_chunk_size));
 
        CHECK_RET(wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES, write_flags, 0));
 
@@ -722,14 +761,29 @@ op__apply_and_capture_test(void)
        CHECK_RET(wimlib_add_image(wim, (void *)rand32, NULL, NULL,
                                   WIMLIB_ADD_FLAG_GENERATE_TEST_DATA |
                                   WIMLIB_ADD_FLAG_NORPFIX));
+
+       image = get_image_count(wim);
+
+       printf("generated wim%d image %d\n", index, image);
+
+       {
+               /*
+                * Compare the in-memory version of the generated image with a
+                * version written to disk
+                */
+               WIMStruct *tmp_wim;
+
+               CHECK_RET(wimlib_write(wim, T("tmp.wim"), image, 0, 0));
+               CHECK_RET(wimlib_open_wim(T("tmp.wim"), 0, &tmp_wim));
+               CHECK_RET(wimlib_compare_images(wim, image, tmp_wim, 1, 0));
+               wimlib_free(tmp_wim);
+       }
+
        overwrite_wim(wim);
        wimlib_free(wim);
 
        /* Apply the generated image.  */
        wim = open_wim(index);
-       image = get_image_count(wim);
-       printf("apply and capture wim%d; generated image is index %d\n",
-              index, image);
        delete_directory_tree(TMP_TARGET_NAME);
 #ifdef WITH_NTFS_3G
        if (rand32() & 1) {
@@ -739,21 +793,22 @@ op__apply_and_capture_test(void)
                extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES;
                extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS;
                add_flags |= WIMLIB_ADD_FLAG_NTFS;
-               cmp_flags |= WIMLIB_CMP_FLAG_COMPRESSION_NOT_PRESERVED;
+               cmp_flags |= WIMLIB_CMP_FLAG_NTFS_3G_MODE;
                create_ntfs_volume(TMP_TARGET_NAME);
        } else
 #endif
        {
-#ifdef __WIN32__
+#ifdef _WIN32
                printf("applying in Windows mode\n");
-#else /* __WIN32__ */
+               cmp_flags |= WIMLIB_CMP_FLAG_WINDOWS_MODE;
+#else /* _WIN32 */
                printf("applying in UNIX mode\n");
-               cmp_flags |= WIMLIB_CMP_FLAG_SHORT_NAMES_NOT_PRESERVED;
-               cmp_flags |= WIMLIB_CMP_FLAG_ATTRIBUTES_NOT_PRESERVED;
-               cmp_flags |= WIMLIB_CMP_FLAG_SECURITY_NOT_PRESERVED;
-               cmp_flags |= WIMLIB_CMP_FLAG_ADS_NOT_PRESERVED;
-               cmp_flags |= WIMLIB_CMP_FLAG_IMAGE2_SHOULD_HAVE_SYMLINKS;
-#endif /* !__WIN32__ */
+               extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
+               add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
+               cmp_flags |= WIMLIB_CMP_FLAG_UNIX_MODE;
+               if (filesystem_type == EXT4_SUPER_MAGIC)
+                       cmp_flags |= WIMLIB_CMP_FLAG_EXT4;
+#endif /* !_WIN32 */
        }
        add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
        CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
@@ -777,24 +832,33 @@ op__apply_and_capture_test(void)
        wimlib_free(wim);
 }
 
-#ifdef __WIN32__
+#ifdef _WIN32
 
-/* Enumerate and unregister all backing WIMs from the specified volume  */
+/*
+ * Enumerate and unregister all backing WIMs from the volume containing the
+ * current directory.
+ */
 static void
-unregister_all_backing_wims(const tchar drive_letter)
+unregister_all_backing_wims(void)
 {
+       wchar_t full_path[MAX_PATH];
+       DWORD path_len;
        wchar_t volume[7];
        HANDLE h;
        void *overlay_list;
        DWORD bytes_returned;
-       const struct wim_provider_overlay_entry *entry;
+       const WIM_PROVIDER_OVERLAY_ENTRY *entry;
        struct {
-               struct wof_external_info wof_info;
-               struct wim_provider_remove_overlay_input wim;
+               WOF_EXTERNAL_INFO wof_info;
+               WIM_PROVIDER_REMOVE_OVERLAY_INPUT wim;
        } in;
 
-       wsprintf(volume, L"\\\\.\\%lc:", drive_letter);
+       path_len = GetFullPathName(L".", ARRAY_LEN(full_path), full_path, NULL);
+       ASSERT(path_len > 0,
+              "Failed to get full path of current directory; error=%u",
+              (unsigned)GetLastError());
 
+       wsprintf(volume, L"\\\\.\\%lc:", full_path[0]);
        h = CreateFile(volume, GENERIC_READ | GENERIC_WRITE,
                       FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS, NULL);
@@ -804,14 +868,15 @@ unregister_all_backing_wims(const tchar drive_letter)
        overlay_list = malloc(32768);
        ASSERT(overlay_list != NULL, "out of memory");
 
-       in.wof_info.version = WOF_CURRENT_VERSION;
-       in.wof_info.provider = WOF_PROVIDER_WIM;
+       in.wof_info.Version = WOF_CURRENT_VERSION;
+       in.wof_info.Provider = WOF_PROVIDER_WIM;
 
        if (!DeviceIoControl(h, FSCTL_ENUM_OVERLAY,
-                            &in, sizeof(struct wof_external_info),
+                            &in, sizeof(WOF_EXTERNAL_INFO),
                             overlay_list, 32768, &bytes_returned, NULL))
        {
                ASSERT(GetLastError() == ERROR_INVALID_FUNCTION ||
+                      GetLastError() == ERROR_INVALID_PARAMETER ||
                       GetLastError() == ERROR_FILE_NOT_FOUND,
                       "FSCTL_ENUM_OVERLAY failed; error=%u", GetLastError());
                return;
@@ -820,16 +885,16 @@ unregister_all_backing_wims(const tchar drive_letter)
        entry = overlay_list;
        for (;;) {
                printf("Unregistering data source ID %"PRIu64"\n",
-                      entry->data_source_id);
-               in.wim.data_source_id = entry->data_source_id;
+                      entry->DataSourceId.QuadPart);
+               in.wim.DataSourceId = entry->DataSourceId;
                ASSERT(DeviceIoControl(h, FSCTL_REMOVE_OVERLAY, &in, sizeof(in),
                                       NULL, 0, &bytes_returned, NULL),
                       "FSCTL_REMOVE_OVERLAY failed; error=%u",
-                      (unsigned )GetLastError());
-               if (entry->next_entry_offset == 0)
+                      (unsigned)GetLastError());
+               if (entry->NextEntryOffset == 0)
                        break;
-               entry = (const struct wim_provider_overlay_entry *)
-                       ((const uint8_t *)entry + entry->next_entry_offset);
+               entry = (const WIM_PROVIDER_OVERLAY_ENTRY *)
+                       ((const u8 *)entry + entry->NextEntryOffset);
        }
        free(overlay_list);
        CloseHandle(h);
@@ -850,7 +915,7 @@ op__wimboot_test(void)
 
        index = select_random_wimfile_index();
 
-       unregister_all_backing_wims(L'E');
+       unregister_all_backing_wims();
        copy_file(get_wimfile(index), L"wimboot.wim");
 
        CHECK_RET(wimlib_open_wim(L"wimboot.wim", 0, &wim));
@@ -891,12 +956,13 @@ op__wimboot_test(void)
        printf("comparing wimboot.wim:%d with wim%d:%d\n",
               image, index2, image_count + 1);
 
-       CHECK_RET(wimlib_compare_images(wim, image, wim2, image_count + 1, 0));
+       CHECK_RET(wimlib_compare_images(wim, image, wim2, image_count + 1,
+                                       WIMLIB_CMP_FLAG_WINDOWS_MODE));
 
        wimlib_free(wim);
        wimlib_free(wim2);
 }
-#endif /* __WIN32__ */
+#endif /* _WIN32 */
 
 static int
 is_solid_resource(const struct wimlib_resource_entry *resource, void *_ctx)
@@ -918,7 +984,7 @@ op__split_test(void)
        WIMStruct *wim;
        WIMStruct *swm;
        WIMStruct *joined_wim;
-       uint64_t part_size;
+       u64 part_size;
        int write_flags;
        const tchar *globs[] = { T("tmp*.swm") };
        int image_count;
@@ -1004,40 +1070,49 @@ static const operation_func operation_table[] = {
        op__apply_and_capture_test,
        op__split_test,
        op__set_compression_level,
-#ifdef __WIN32__
+#ifdef _WIN32
        op__wimboot_test,
 #endif
 };
 
-#ifdef __WIN32__
-extern int wmain(int argc, wchar_t **argv);
+#ifdef _WIN32
+int wmain(int argc, wchar_t **argv);
 #define main wmain
 #endif
 
 int
 main(int argc, tchar **argv)
 {
-       unsigned long long num_iterations;
-
-       if (argc < 2) {
-               num_iterations = ULLONG_MAX;
-               printf("Starting test runner\n");
-       } else {
-               num_iterations = tstrtoull(argv[1], NULL, 10);
-               printf("Starting test runner with %llu iterations\n",
-                      num_iterations);
-       }
+       unsigned long time_limit = 0;
+       time_t start_time;
+       u64 i;
+
+       /* If you want to make the tests deterministic, delete this line. */
+       random_state = ((u64)time(NULL) << 16) ^ getpid();
+
+       if (argc >= 2)
+               time_limit = tstrtoul(argv[1], NULL, 10);
+
+       if (time_limit == 0)
+               printf("Starting wlfuzz with no time limit\n");
+       else
+               printf("Starting wlfuzz with time limit of %lu seconds\n",
+                      time_limit);
 
-       CHECK_RET(wimlib_global_init(0));
+       CHECK_RET(wimlib_global_init(WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES |
+                                    WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES));
        wimlib_set_print_errors(true);
+       wimlib_seed_random(rand64());
 
        change_to_temporary_directory();
 
-       for (int i = 0; i < MAX_NUM_WIMS; i++)
+       for (i = 0; i < MAX_NUM_WIMS; i++)
                ASSERT(!tunlink(get_wimfile(i)) || errno == ENOENT, "unlink: %m");
 
-       for (unsigned long long i = 0; i < num_iterations; i++) {
-               printf("--> iteration %llu\n", i);
+       i = 0;
+       start_time = time(NULL);
+       while (time_limit == 0 || time(NULL) < start_time + time_limit) {
+               printf("--> iteration %"PRIu64"\n", ++i);
                (*operation_table[rand32() % ARRAY_LEN(operation_table)])();
        }