2 * wlfuzz.c - Randomized tests for wimlib
6 * Copyright 2015-2023 Eric Biggers
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 * This program is a randomized test runner for wimlib. It must be linked
24 * against a build of the library compiled with --enable-test-support.
26 * Various types of tests are run. Most important is the "apply and capture"
27 * test, which works as follows:
29 * 1. Generate an in-memory WIM image containing a random directory tree
30 * 2. Persist the image into a WIM file
31 * 3. Apply the WIM image to somewhere
32 * 4. Re-capture the applied image
33 * 5. Compare the directory tree of the re-captured image to the original
35 * Note that this is an "apply and capture" test, not a "capture and apply"
36 * test. By using the filesystem as the intermediary rather than as the
37 * starting point and ending point, the tests will run nearly unchanged
38 * regardless of filesystem type (e.g. UNIX, Windows, or NTFS-3G). This style
39 * of test has been effective at finding bugs in wimlib as well as bugs in
40 * NTFS-3G where its behavior differs from that of Windows.
42 * Care is taken to exercise different options, such as different compression
43 * formats, when multiple are available.
50 #ifndef ENABLE_TEST_SUPPORT
51 # error "This program requires that wimlib was configured with --enable-test-support."
66 # include <sys/wait.h>
72 # include <winternl.h>
73 # include <ntstatus.h>
75 # include <linux/magic.h>
80 #include "wimlib_tchar.h"
81 #include "wimlib/test_support.h"
82 #include "wimlib/wof.h"
88 #define ARRAY_LEN(A) (sizeof(A) / sizeof((A)[0]))
90 #define TMP_TARGET_NAME T("wlfuzz-tmp-target")
91 #define MAX_NUM_WIMS 4
93 static bool wimfile_in_use[MAX_NUM_WIMS];
94 static int in_use_wimfile_indices[MAX_NUM_WIMS];
95 static int num_wimfiles_in_use = 0;
97 static u32 filesystem_type;
101 assertion_failed(int line, const char *format, ...)
105 va_start(va, format);
106 fprintf(stderr, "ASSERTION FAILED at line %d: ", line);
107 vfprintf(stderr, format, va);
114 #define ASSERT(expr, msg, ...) \
116 if (__builtin_expect(!(expr), 0)) \
117 assertion_failed(__LINE__, (msg), ##__VA_ARGS__); \
120 #define CHECK_RET(ret) \
123 ASSERT(!r, "%"TS, wimlib_get_error_string(r)); \
127 change_to_temporary_directory(void)
130 const wchar_t *tmpdir = _wgetenv(T("TMPDIR"));
132 ASSERT(tmpdir != NULL, "TMPDIR must be set");
134 ASSERT(!_wchdir(tmpdir),
135 "failed to change to temporary directory '%ls'", tmpdir);
137 const char *tmpdir = getenv("TMPDIR") ?: P_tmpdir;
141 ASSERT(!chdir(tmpdir),
142 "failed to change to temporary directory '%s': %m", tmpdir);
143 ASSERT(!statfs(".", &fs), "statfs of '%s' failed: %m", tmpdir);
144 filesystem_type = fs.f_type;
148 static void __attribute__((unused))
149 copy_file(const tchar *src, const tchar *dst)
151 int in_fd = topen(src, O_RDONLY|O_BINARY);
152 int out_fd = topen(dst, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644);
154 ssize_t bytes_read, bytes_written, i;
156 ASSERT(in_fd >= 0, "%"TS": open error: %m", src);
157 ASSERT(out_fd >= 0, "%"TS": open error: %m", dst);
158 while ((bytes_read = read(in_fd, buf, sizeof(buf))) > 0) {
159 for (i = 0; i < bytes_read; i += bytes_written) {
160 bytes_written = write(out_fd, &buf[i], bytes_read - i);
161 ASSERT(bytes_written > 0, "%"TS": write error: %m", dst);
164 ASSERT(bytes_read == 0, "%"TS": read error: %m", src);
171 create_ntfs_volume(const char *name)
176 static const char buffer[1] = {0};
178 fd = open(name, O_WRONLY|O_TRUNC|O_CREAT|O_NOFOLLOW, 0644);
179 ASSERT(fd >= 0, "%s: open error: %m", name);
181 ASSERT(lseek(fd, 999999999, SEEK_SET) != -1, "%s: lseek error: %m", name);
183 ASSERT(write(fd, buffer, 1) == 1, "%s: write error: %m", name);
185 ASSERT(close(fd) == 0, "%s: close error: %m", name);
188 ASSERT(pid >= 0, "fork error: %m");
190 close(STDOUT_FILENO);
191 close(STDERR_FILENO);
192 execlp("mkntfs", "mkntfs", "--force", "--fast",
194 ASSERT(false, "Failed to execute mkntfs: %m");
197 ASSERT(wait(&status) != -1, "wait error: %m");
198 ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0,
199 "mkntfs error: exited with status %d", status);
201 #endif /* WITH_NTFS_3G */
205 WINAPI NTSTATUS NtQueryDirectoryFile(HANDLE FileHandle,
207 PIO_APC_ROUTINE ApcRoutine,
209 PIO_STATUS_BLOCK IoStatusBlock,
210 PVOID FileInformation,
212 FILE_INFORMATION_CLASS FileInformationClass,
213 BOOLEAN ReturnSingleEntry,
214 PUNICODE_STRING FileName,
215 BOOLEAN RestartScan);
218 delete_directory_tree_recursive(HANDLE cur_dir, UNICODE_STRING *name)
220 OBJECT_ATTRIBUTES attr = { .Length = sizeof(attr), };
221 IO_STATUS_BLOCK iosb;
222 FILE_BASIC_INFORMATION basic = { .FileAttributes = FILE_ATTRIBUTE_NORMAL, };
224 const size_t bufsize = 8192;
230 flags = FILE_DELETE_ON_CLOSE |
231 FILE_OPEN_REPARSE_POINT |
232 FILE_OPEN_FOR_BACKUP_INTENT |
233 FILE_SYNCHRONOUS_IO_NONALERT |
234 FILE_SEQUENTIAL_ONLY;
236 name->MaximumLength = name->Length;
238 attr.RootDirectory = cur_dir;
239 attr.ObjectName = name;
241 perms = DELETE | SYNCHRONIZE | FILE_LIST_DIRECTORY | FILE_TRAVERSE;
243 status = NtOpenFile(&h, perms, &attr, &iosb, FILE_SHARE_VALID_FLAGS, flags);
244 if (!NT_SUCCESS(status)) {
245 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
247 if (status == STATUS_CANNOT_DELETE && (perms & DELETE)) {
249 flags &= ~FILE_DELETE_ON_CLOSE;
250 perms |= FILE_WRITE_ATTRIBUTES;
253 ASSERT(false, "NtOpenFile() for deletion failed; status=0x%08"PRIx32, status);
255 if (perms & FILE_WRITE_ATTRIBUTES) {
256 status = NtSetInformationFile(h, &iosb, &basic,
257 sizeof(basic), FileBasicInformation);
259 if (!NT_SUCCESS(status)) {
260 ASSERT(false, "NtSetInformationFile() for deletion "
261 "failed; status=0x%08"PRIx32, status);
263 perms &= ~FILE_WRITE_ATTRIBUTES;
265 flags |= FILE_DELETE_ON_CLOSE;
269 buf = malloc(bufsize);
270 ASSERT(buf != NULL, "out of memory!");
272 while (NT_SUCCESS(status = NtQueryDirectoryFile(h, NULL, NULL, NULL,
274 FileNamesInformation,
275 FALSE, NULL, FALSE)))
277 const FILE_NAMES_INFORMATION *info = buf;
279 if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') &&
280 !(info->FileNameLength == 4 && info->FileName[0] == L'.' &&
281 info->FileName[1] == L'.'))
283 name->Buffer = (wchar_t *)info->FileName;
284 name->Length = info->FileNameLength;
285 delete_directory_tree_recursive(h, name);
287 if (info->NextEntryOffset == 0)
289 info = (const FILE_NAMES_INFORMATION *)
290 ((const char *)info + info->NextEntryOffset);
294 ASSERT(status == STATUS_NO_MORE_FILES || /* end of directory */
295 status == STATUS_INVALID_PARAMETER, /* not a directory */
296 "NtQueryDirectoryFile() for deletion failed; "
297 "status=0x%08"PRIx32, status);
304 delete_directory_tree(const wchar_t *name)
306 UNICODE_STRING uname;
309 ASSERT(RtlDosPathNameToNtPathName_U(name, &uname, NULL, NULL),
310 "Unable to translate %ls to NT namespace path", name);
311 buffer = uname.Buffer;
312 delete_directory_tree_recursive(NULL, &uname);
313 HeapFree(GetProcessHeap(), 0, buffer);
314 ASSERT(GetFileAttributes(name) == 0xFFFFFFFF, "Deletion didn't work!");
320 delete_directory_tree_recursive(int dirfd, const char *name)
326 if (!unlinkat(dirfd, name, 0) || errno == ENOENT)
328 ASSERT(errno == EISDIR, "%s: unlink error: %m", name);
330 fd = openat(dirfd, name, O_RDONLY | O_NOFOLLOW | O_DIRECTORY);
331 ASSERT(fd >= 0, "%m");
334 ASSERT(dir != NULL, "%m");
335 while (errno = 0, (ent = readdir(dir)))
336 if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
337 delete_directory_tree_recursive(fd, ent->d_name);
340 ASSERT(!unlinkat(dirfd, name, AT_REMOVEDIR), "%m");
344 delete_directory_tree(const tchar *name)
346 delete_directory_tree_recursive(AT_FDCWD, name);
351 static u64 random_state;
356 /* A simple linear congruential generator */
357 random_state = (random_state * 25214903917 + 11) % (1ULL << 48);
358 return random_state >> 16;
370 return ((u64)rand32() << 32) | rand32();
373 static tchar wimfile[32];
376 get_wimfile(int index)
378 tsprintf(wimfile, T("wim%d"), index);
383 select_random_wimfile_index(void)
385 return in_use_wimfile_indices[rand32() % num_wimfiles_in_use];
389 select_new_wimfile(void)
393 while (wimfile_in_use[index])
396 in_use_wimfile_indices[num_wimfiles_in_use++] = index;
397 wimfile_in_use[index] = true;
399 return get_wimfile(index);
405 const tchar *wimfile = get_wimfile(index);
409 open_flags |= randbool() ? 0 : WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
411 printf("Opening %"TS" with flags 0x%08x\n", wimfile, open_flags);
413 CHECK_RET(wimlib_open_wim(wimfile, open_flags, &wim));
419 open_random_wim(void)
421 return open_wim(select_random_wimfile_index());
425 get_image_count(WIMStruct *wim)
427 struct wimlib_wim_info info;
429 CHECK_RET(wimlib_get_wim_info(wim, &info));
431 return info.image_count;
436 is_wimboot_capable(WIMStruct *wim)
438 struct wimlib_wim_info info;
440 CHECK_RET(wimlib_get_wim_info(wim, &info));
442 return info.wim_version == 0x10D00 &&
443 ((info.compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS &&
444 (info.chunk_size == 4096 || info.chunk_size == 8192 ||
445 info.chunk_size == 16384 || info.chunk_size == 32768)) ||
446 (info.compression_type == WIMLIB_COMPRESSION_TYPE_LZX &&
447 info.chunk_size == 32768));
452 overwrite_wim(WIMStruct *wim)
455 struct wimlib_wim_info info;
457 CHECK_RET(wimlib_get_wim_info(wim, &info));
459 switch (rand32() % 4) {
461 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
464 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
468 switch (rand32() % 8) {
470 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
473 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
477 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_RECOMPRESS;
478 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_FSYNC;
479 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_REBUILD;
480 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SOFT_DELETE;
481 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG;
482 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_RETAIN_GUID;
483 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES;
484 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
486 if (rand32() % 8 == 0 &&
487 !(write_flags & WIMLIB_WRITE_FLAG_PIPABLE) &&
488 (!info.pipable || (write_flags & WIMLIB_WRITE_FLAG_NOT_PIPABLE)))
489 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
491 if (randbool() && !info.pipable &&
492 !(write_flags & (WIMLIB_WRITE_FLAG_RECOMPRESS |
493 WIMLIB_WRITE_FLAG_PIPABLE)))
494 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
496 printf("overwrite with flags: 0x%08x\n", write_flags);
498 CHECK_RET(wimlib_overwrite(wim, write_flags, 0));
502 get_random_write_flags(void)
506 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
507 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES;
508 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
509 switch (rand32() % 8) {
511 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
514 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
522 get_random_chunk_size(int min_order, int max_order)
524 return 1 << (min_order + (rand32() % (max_order - min_order + 1)));
528 op__create_new_wim(void)
530 printf(":::op__create_new_wim\n");
532 const tchar *wimfile;
533 enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_NONE;
535 u32 solid_chunk_size = 0;
539 if (num_wimfiles_in_use == MAX_NUM_WIMS)
542 wimfile = select_new_wimfile();
544 /* Select a random compression type and chunk size. */
545 switch (rand32() % 4) {
549 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
550 chunk_size = get_random_chunk_size(12, 16);
553 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
555 chunk_size = 1 << 15;
557 chunk_size = get_random_chunk_size(15, 21);
560 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
561 chunk_size = get_random_chunk_size(15, 28);
563 solid_chunk_size = get_random_chunk_size(15, 26);
565 solid_chunk_size = get_random_chunk_size(26, 28);
569 /* Select random write flags. */
570 write_flags = get_random_write_flags();
572 printf("Creating %"TS" with write flags 0x%08x, compression_type=%"TS", chunk_size=%u, solid_chunk_size=%u\n",
573 wimfile, write_flags,
574 wimlib_get_compression_type_string(ctype),
575 chunk_size, solid_chunk_size);
577 CHECK_RET(wimlib_create_new_wim(ctype, &wim));
579 CHECK_RET(wimlib_set_output_chunk_size(wim, chunk_size));
580 if (solid_chunk_size != 0)
581 CHECK_RET(wimlib_set_output_pack_chunk_size(wim, solid_chunk_size));
583 CHECK_RET(wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES, write_flags, 0));
589 op__add_empty_image_to_random_wim(void)
591 printf(":::op__add_empty_image_to_random_wim\n");
596 if (num_wimfiles_in_use < 1)
599 wim = open_random_wim();
600 CHECK_RET(wimlib_add_empty_image(wim, NULL, &new_idx));
601 printf("Adding empty image to %"TS" at index %d\n", wimfile, new_idx);
607 op__delete_random_image_from_random_wim(void)
609 printf(":::op__delete_random_image_from_random_wim\n");
615 if (num_wimfiles_in_use == 0)
618 wim = open_random_wim();
619 image_count = get_image_count(wim);
620 if (image_count != 0) {
621 image = 1 + (rand32() % image_count);
622 CHECK_RET(wimlib_delete_image(wim, image));
623 printf("Deleting image %d from %"TS"\n", image, wimfile);
630 op__delete_random_wim(void)
632 printf(":::op__delete_random_wim\n");
634 const tchar *wimfile;
638 if (num_wimfiles_in_use == 0)
641 which = rand32() % num_wimfiles_in_use;
642 index = in_use_wimfile_indices[which];
644 wimfile = get_wimfile(index);
646 ASSERT(!tunlink(wimfile), "failed to unlink %"TS": %m", wimfile);
648 printf("Deleted %"TS"\n", wimfile);
650 for (int i = which; i < num_wimfiles_in_use - 1; i++)
651 in_use_wimfile_indices[i] = in_use_wimfile_indices[i + 1];
652 num_wimfiles_in_use--;
653 wimfile_in_use[index] = false;
657 op__verify_random_wim(void)
659 printf(":::op__verify_random_wim\n");
663 if (num_wimfiles_in_use == 0)
666 wim = open_random_wim();
667 CHECK_RET(wimlib_verify_wim(wim, 0));
668 printf("Verified %"TS"\n", wimfile);
673 op__overwrite_with_no_changes(void)
675 printf(":::op__overwrite_with_no_changes\n");
679 if (num_wimfiles_in_use == 0)
682 wim = open_random_wim();
688 op__export_random_image(void)
690 printf(":::op__export_random_image\n");
692 int src_wimfile_index;
693 int dst_wimfile_index;
701 if (num_wimfiles_in_use < 2)
704 src_wimfile_index = select_random_wimfile_index();
706 dst_wimfile_index = select_random_wimfile_index();
707 } while (dst_wimfile_index == src_wimfile_index);
709 src_wim = open_wim(src_wimfile_index);
710 dst_wim = open_wim(dst_wimfile_index);
712 src_image_count = get_image_count(src_wim);
713 dst_image_count = get_image_count(dst_wim);
715 /* Choose a random source image --- single or all. */
716 src_image = WIMLIB_ALL_IMAGES;
717 if (src_image_count != 0 && randbool())
718 src_image = 1 + (rand32() % src_image_count);
720 printf("Exporting image %d of %d from wim %d into wim %d\n",
721 src_image, src_image_count, src_wimfile_index, dst_wimfile_index);
722 CHECK_RET(wimlib_export_image(src_wim, src_image, dst_wim, NULL, NULL, 0));
724 overwrite_wim(dst_wim);
725 wimlib_free(dst_wim);
727 dst_wim = open_wim(dst_wimfile_index);
729 /* Compare the images. */
730 dst_image = dst_image_count;
731 for (int image = (src_image == WIMLIB_ALL_IMAGES ? 1 : src_image);
732 image <= (src_image == WIMLIB_ALL_IMAGES ? src_image_count : src_image);
735 CHECK_RET(wimlib_compare_images(src_wim, image, dst_wim, ++dst_image, 0));
738 wimlib_free(src_wim);
739 wimlib_free(dst_wim);
743 op__apply_and_capture_test(void)
745 printf(":::op__apply_and_capture_test\n");
750 int extract_flags = 0;
754 if (num_wimfiles_in_use == 0)
757 /* Generate a random image. */
758 index = select_random_wimfile_index();
759 wim = open_wim(index);
761 CHECK_RET(wimlib_add_image(wim, (void *)rand32, NULL, NULL,
762 WIMLIB_ADD_FLAG_GENERATE_TEST_DATA |
763 WIMLIB_ADD_FLAG_NORPFIX));
765 image = get_image_count(wim);
767 printf("generated wim%d image %d\n", index, image);
771 * Compare the in-memory version of the generated image with a
772 * version written to disk
776 CHECK_RET(wimlib_write(wim, T("tmp.wim"), image, 0, 0));
777 CHECK_RET(wimlib_open_wim(T("tmp.wim"), 0, &tmp_wim));
778 CHECK_RET(wimlib_compare_images(wim, image, tmp_wim, 1, 0));
779 wimlib_free(tmp_wim);
785 /* Apply the generated image. */
786 wim = open_wim(index);
787 delete_directory_tree(TMP_TARGET_NAME);
790 printf("applying in NTFS mode\n");
791 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
792 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
793 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES;
794 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS;
795 add_flags |= WIMLIB_ADD_FLAG_NTFS;
796 cmp_flags |= WIMLIB_CMP_FLAG_NTFS_3G_MODE;
797 create_ntfs_volume(TMP_TARGET_NAME);
802 printf("applying in Windows mode\n");
803 cmp_flags |= WIMLIB_CMP_FLAG_WINDOWS_MODE;
805 printf("applying in UNIX mode\n");
806 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
807 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
808 cmp_flags |= WIMLIB_CMP_FLAG_UNIX_MODE;
809 if (filesystem_type == EXT4_SUPER_MAGIC)
810 cmp_flags |= WIMLIB_CMP_FLAG_EXT4;
813 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
814 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
817 /* Sometimes extract twice so that we test overwriting existing files.
819 if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) && randbool()) {
820 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
824 /* Capture the applied image. */
825 CHECK_RET(wimlib_add_image(wim, TMP_TARGET_NAME, NULL, NULL, add_flags));
829 /* Compare the generated image with the captured image. */
830 wim = open_wim(index);
831 CHECK_RET(wimlib_compare_images(wim, image, wim, image + 1, cmp_flags));
838 * Enumerate and unregister all backing WIMs from the volume containing the
842 unregister_all_backing_wims(void)
844 wchar_t full_path[MAX_PATH];
849 DWORD bytes_returned;
850 const WIM_PROVIDER_OVERLAY_ENTRY *entry;
852 WOF_EXTERNAL_INFO wof_info;
853 WIM_PROVIDER_REMOVE_OVERLAY_INPUT wim;
856 path_len = GetFullPathName(L".", ARRAY_LEN(full_path), full_path, NULL);
858 "Failed to get full path of current directory; error=%u",
859 (unsigned)GetLastError());
861 wsprintf(volume, L"\\\\.\\%lc:", full_path[0]);
862 h = CreateFile(volume, GENERIC_READ | GENERIC_WRITE,
863 FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
864 FILE_FLAG_BACKUP_SEMANTICS, NULL);
865 ASSERT(h != INVALID_HANDLE_VALUE,
866 "Failed to open %ls; error=%u", volume, (unsigned)GetLastError());
868 overlay_list = malloc(32768);
869 ASSERT(overlay_list != NULL, "out of memory");
871 in.wof_info.Version = WOF_CURRENT_VERSION;
872 in.wof_info.Provider = WOF_PROVIDER_WIM;
874 if (!DeviceIoControl(h, FSCTL_ENUM_OVERLAY,
875 &in, sizeof(WOF_EXTERNAL_INFO),
876 overlay_list, 32768, &bytes_returned, NULL))
878 ASSERT(GetLastError() == ERROR_INVALID_FUNCTION ||
879 GetLastError() == ERROR_INVALID_PARAMETER ||
880 GetLastError() == ERROR_FILE_NOT_FOUND,
881 "FSCTL_ENUM_OVERLAY failed; error=%u", GetLastError());
885 entry = overlay_list;
887 printf("Unregistering data source ID %"PRIu64"\n",
888 entry->DataSourceId.QuadPart);
889 in.wim.DataSourceId = entry->DataSourceId;
890 ASSERT(DeviceIoControl(h, FSCTL_REMOVE_OVERLAY, &in, sizeof(in),
891 NULL, 0, &bytes_returned, NULL),
892 "FSCTL_REMOVE_OVERLAY failed; error=%u",
893 (unsigned)GetLastError());
894 if (entry->NextEntryOffset == 0)
896 entry = (const WIM_PROVIDER_OVERLAY_ENTRY *)
897 ((const u8 *)entry + entry->NextEntryOffset);
904 op__wimboot_test(void)
913 if (num_wimfiles_in_use == 0)
916 index = select_random_wimfile_index();
918 unregister_all_backing_wims();
919 copy_file(get_wimfile(index), L"wimboot.wim");
921 CHECK_RET(wimlib_open_wim(L"wimboot.wim", 0, &wim));
923 image_count = get_image_count(wim);
924 if (image_count == 0 || !is_wimboot_capable(wim)) {
930 image = 1 + (rand32() % image_count);
932 printf("WIMBOOT test; wim%d image %d\n", index, image);
934 delete_directory_tree(TMP_TARGET_NAME);
936 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
937 WIMLIB_EXTRACT_FLAG_WIMBOOT));
940 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
941 WIMLIB_EXTRACT_FLAG_WIMBOOT));
944 index2 = select_random_wimfile_index();
945 wim2 = open_wim(index2);
946 image_count = get_image_count(wim2);
948 CHECK_RET(wimlib_add_image(wim2, TMP_TARGET_NAME, NULL, NULL,
949 WIMLIB_ADD_FLAG_NORPFIX));
954 wim2 = open_wim(index2);
956 printf("comparing wimboot.wim:%d with wim%d:%d\n",
957 image, index2, image_count + 1);
959 CHECK_RET(wimlib_compare_images(wim, image, wim2, image_count + 1,
960 WIMLIB_CMP_FLAG_WINDOWS_MODE));
968 is_solid_resource(const struct wimlib_resource_entry *resource, void *_ctx)
970 return resource->packed;
974 wim_contains_solid_resources(WIMStruct *wim)
976 return wimlib_iterate_lookup_table(wim, 0, is_solid_resource, NULL);
982 printf(":::op__split_test\n");
986 WIMStruct *joined_wim;
989 const tchar *globs[] = { T("tmp*.swm") };
992 if (num_wimfiles_in_use == 0)
995 /* split, join, and compare */
997 wim = open_random_wim();
999 if (wim_contains_solid_resources(wim)) {
1000 /* Can't split a WIM containing solid resources */
1005 image_count = get_image_count(wim);
1007 part_size = 10000 + (rand32() % 1000000);
1008 write_flags = get_random_write_flags();
1009 write_flags &= ~WIMLIB_WRITE_FLAG_SOLID;
1011 printf("splitting WIM %"TS": part_size=%"PRIu64", write_flags=0x%08x\n",
1012 wimfile, part_size, write_flags);
1014 CHECK_RET(wimlib_split(wim, T("tmp.swm"), part_size, write_flags));
1016 CHECK_RET(wimlib_open_wim(T("tmp.swm"), WIMLIB_OPEN_FLAG_CHECK_INTEGRITY,
1019 CHECK_RET(wimlib_reference_resource_files(swm, globs, 1,
1020 WIMLIB_REF_FLAG_GLOB_ENABLE |
1021 WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH,
1022 WIMLIB_OPEN_FLAG_CHECK_INTEGRITY));
1024 CHECK_RET(wimlib_verify_wim(swm, 0));
1026 CHECK_RET(wimlib_write(swm, T("joined.wim"), WIMLIB_ALL_IMAGES, write_flags, 0));
1029 CHECK_RET(wimlib_open_wim(T("joined.wim"), 0, &joined_wim));
1030 for (int i = 1; i <= image_count; i++)
1031 CHECK_RET(wimlib_compare_images(wim, 1, joined_wim, 1, 0));
1032 CHECK_RET(wimlib_verify_wim(joined_wim, 0));
1033 wimlib_free(joined_wim);
1036 tunlink(T("tmp.swm"));
1037 for (int i = 2; ; i++) {
1039 tsprintf(name, T("tmp%d.swm"), i);
1046 op__set_compression_level(void)
1048 printf(":::op__set_compression_level\n");
1050 unsigned int level = rand32() % 100;
1051 printf("Changing compression levels to %d\n", level);
1052 wimlib_set_default_compression_level(-1, level);
1055 typedef void (*operation_func)(void);
1057 static const operation_func operation_table[] = {
1059 op__add_empty_image_to_random_wim,
1060 op__delete_random_image_from_random_wim,
1061 op__delete_random_wim,
1062 op__delete_random_wim,
1063 op__verify_random_wim,
1064 op__overwrite_with_no_changes,
1065 op__export_random_image,
1066 op__apply_and_capture_test,
1067 op__apply_and_capture_test,
1068 op__apply_and_capture_test,
1069 op__apply_and_capture_test,
1070 op__apply_and_capture_test,
1072 op__set_compression_level,
1079 int wmain(int argc, wchar_t **argv);
1084 main(int argc, tchar **argv)
1086 unsigned long time_limit = 0;
1090 /* If you want to make the tests deterministic, delete this line. */
1091 random_state = ((u64)time(NULL) << 16) ^ getpid();
1094 time_limit = tstrtoul(argv[1], NULL, 10);
1096 if (time_limit == 0)
1097 printf("Starting wlfuzz with no time limit\n");
1099 printf("Starting wlfuzz with time limit of %lu seconds\n",
1102 CHECK_RET(wimlib_global_init(WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES |
1103 WIMLIB_INIT_FLAG_STRICT_CAPTURE_PRIVILEGES));
1104 wimlib_set_print_errors(true);
1105 wimlib_seed_random(rand64());
1107 change_to_temporary_directory();
1109 for (i = 0; i < MAX_NUM_WIMS; i++)
1110 ASSERT(!tunlink(get_wimfile(i)) || errno == ENOENT, "unlink: %m");
1113 start_time = time(NULL);
1114 while (time_limit == 0 || time(NULL) < start_time + time_limit) {
1115 printf("--> iteration %"PRIu64"\n", ++i);
1116 (*operation_table[rand32() % ARRAY_LEN(operation_table)])();
1119 wimlib_global_cleanup();