2 * wlfuzz.c - Randomized tests for wimlib
6 * Copyright (C) 2015-2021 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 <http://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."
65 # include <sys/wait.h>
71 # include <winternl.h>
72 # include <ntstatus.h>
76 #include "wimlib_tchar.h"
77 #include "wimlib/test_support.h"
78 #include "wimlib/wof.h"
84 #define ARRAY_LEN(A) (sizeof(A) / sizeof((A)[0]))
86 #define TMP_TARGET_NAME T("wlfuzz-tmp-target")
87 #define MAX_NUM_WIMS 4
89 static bool wimfile_in_use[MAX_NUM_WIMS];
90 static int in_use_wimfile_indices[MAX_NUM_WIMS];
91 static int num_wimfiles_in_use = 0;
94 assertion_failed(int line, const char *format, ...)
99 fprintf(stderr, "ASSERTION FAILED at line %d: ", line);
100 vfprintf(stderr, format, va);
107 #define ASSERT(expr, msg, ...) \
109 if (__builtin_expect(!(expr), 0)) \
110 assertion_failed(__LINE__, (msg), ##__VA_ARGS__); \
113 #define CHECK_RET(ret) \
116 ASSERT(!r, "%"TS, wimlib_get_error_string(r)); \
120 change_to_temporary_directory(void)
123 ASSERT(SetCurrentDirectory(L"E:\\"),
124 "failed to change directory to E:\\");
126 const char *tmpdir = getenv("TMPDIR");
129 ASSERT(!chdir(tmpdir),
130 "failed to change to temporary directory \"%s\": %m", tmpdir);
134 static void __attribute__((unused))
135 copy_file(const tchar *src, const tchar *dst)
137 int in_fd = topen(src, O_RDONLY|O_BINARY);
138 int out_fd = topen(dst, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644);
140 ssize_t bytes_read, bytes_written, i;
142 ASSERT(in_fd >= 0, "%"TS": open error: %m", src);
143 ASSERT(out_fd >= 0, "%"TS": open error: %m", dst);
144 while ((bytes_read = read(in_fd, buf, sizeof(buf))) > 0) {
145 for (i = 0; i < bytes_read; i += bytes_written) {
146 bytes_written = write(out_fd, &buf[i], bytes_read - i);
147 ASSERT(bytes_written > 0, "%"TS": write error: %m", dst);
150 ASSERT(bytes_read == 0, "%"TS": read error: %m", src);
157 create_ntfs_volume(const char *name)
162 static const char buffer[1] = {0};
164 fd = open(name, O_WRONLY|O_TRUNC|O_CREAT|O_NOFOLLOW, 0644);
165 ASSERT(fd >= 0, "%s: open error: %m", name);
167 ASSERT(lseek(fd, 999999999, SEEK_SET) != -1, "%s: lseek error: %m", name);
169 ASSERT(write(fd, buffer, 1) == 1, "%s: write error: %m", name);
171 ASSERT(close(fd) == 0, "%s: close error: %m", name);
174 ASSERT(pid >= 0, "fork error: %m");
176 close(STDOUT_FILENO);
177 close(STDERR_FILENO);
178 execlp("mkntfs", "mkntfs", "--force", "--fast",
180 ASSERT(false, "Failed to execute mkntfs: %m");
183 ASSERT(wait(&status) != -1, "wait error: %m");
184 ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0,
185 "mkntfs error: exited with status %d", status);
187 #endif /* WITH_NTFS_3G */
191 extern WINAPI NTSTATUS NtQueryDirectoryFile (HANDLE FileHandle,
193 PIO_APC_ROUTINE ApcRoutine,
195 PIO_STATUS_BLOCK IoStatusBlock,
196 PVOID FileInformation,
198 FILE_INFORMATION_CLASS FileInformationClass,
199 BOOLEAN ReturnSingleEntry,
200 PUNICODE_STRING FileName,
201 BOOLEAN RestartScan);
204 delete_directory_tree_recursive(HANDLE cur_dir, UNICODE_STRING *name)
206 OBJECT_ATTRIBUTES attr = { .Length = sizeof(attr), };
207 IO_STATUS_BLOCK iosb;
208 FILE_BASIC_INFORMATION basic = { .FileAttributes = FILE_ATTRIBUTE_NORMAL, };
210 const size_t bufsize = 8192;
216 flags = FILE_DELETE_ON_CLOSE |
217 FILE_OPEN_REPARSE_POINT |
218 FILE_OPEN_FOR_BACKUP_INTENT |
219 FILE_SYNCHRONOUS_IO_NONALERT |
220 FILE_SEQUENTIAL_ONLY;
222 name->MaximumLength = name->Length;
224 attr.RootDirectory = cur_dir;
225 attr.ObjectName = name;
227 perms = DELETE | SYNCHRONIZE | FILE_LIST_DIRECTORY | FILE_TRAVERSE;
229 status = NtOpenFile(&h, perms, &attr, &iosb, FILE_SHARE_VALID_FLAGS, flags);
230 if (!NT_SUCCESS(status)) {
231 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
233 if (status == STATUS_CANNOT_DELETE && (perms & DELETE)) {
235 flags &= ~FILE_DELETE_ON_CLOSE;
236 perms |= FILE_WRITE_ATTRIBUTES;
239 ASSERT(false, "NtOpenFile() for deletion failed; status=0x%08"PRIx32, status);
241 if (perms & FILE_WRITE_ATTRIBUTES) {
242 status = NtSetInformationFile(h, &iosb, &basic,
243 sizeof(basic), FileBasicInformation);
245 if (!NT_SUCCESS(status)) {
246 ASSERT(false, "NtSetInformationFile() for deletion "
247 "failed; status=0x%08"PRIx32, status);
249 perms &= ~FILE_WRITE_ATTRIBUTES;
251 flags |= FILE_DELETE_ON_CLOSE;
255 buf = malloc(bufsize);
256 ASSERT(buf != NULL, "out of memory!");
258 while (NT_SUCCESS(status = NtQueryDirectoryFile(h, NULL, NULL, NULL,
260 FileNamesInformation,
261 FALSE, NULL, FALSE)))
263 const FILE_NAMES_INFORMATION *info = buf;
265 if (!(info->FileNameLength == 2 && info->FileName[0] == L'.') &&
266 !(info->FileNameLength == 4 && info->FileName[0] == L'.' &&
267 info->FileName[1] == L'.'))
269 name->Buffer = (wchar_t *)info->FileName;
270 name->Length = info->FileNameLength;
271 delete_directory_tree_recursive(h, name);
273 if (info->NextEntryOffset == 0)
275 info = (const FILE_NAMES_INFORMATION *)
276 ((const char *)info + info->NextEntryOffset);
280 ASSERT(status == STATUS_NO_MORE_FILES || /* end of directory */
281 status == STATUS_INVALID_PARAMETER, /* not a directory */
282 "NtQueryDirectoryFile() for deletion failed; "
283 "status=0x%08"PRIx32, status);
290 delete_directory_tree(const wchar_t *name)
292 UNICODE_STRING uname;
295 ASSERT(RtlDosPathNameToNtPathName_U(name, &uname, NULL, NULL),
296 "Unable to translate %ls to NT namespace path", name);
297 buffer = uname.Buffer;
298 delete_directory_tree_recursive(NULL, &uname);
299 HeapFree(GetProcessHeap(), 0, buffer);
300 ASSERT(GetFileAttributes(name) == 0xFFFFFFFF, "Deletion didn't work!");
303 #else /* __WIN32__ */
306 delete_directory_tree_recursive(int dirfd, const char *name)
312 if (!unlinkat(dirfd, name, 0) || errno == ENOENT)
314 ASSERT(errno == EISDIR, "%s: unlink error: %m", name);
316 fd = openat(dirfd, name, O_RDONLY | O_NOFOLLOW | O_DIRECTORY);
317 ASSERT(fd >= 0, "%m");
320 ASSERT(dir != NULL, "%m");
321 while (errno = 0, (ent = readdir(dir)))
322 if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
323 delete_directory_tree_recursive(fd, ent->d_name);
326 ASSERT(!unlinkat(dirfd, name, AT_REMOVEDIR), "%m");
330 delete_directory_tree(const tchar *name)
332 delete_directory_tree_recursive(AT_FDCWD, name);
335 #endif /* !__WIN32__ */
340 static uint64_t state;
342 /* A simple linear congruential generator */
343 state = (state * 25214903917 + 11) & (((uint64_t)1 << 48) - 1);
353 static tchar wimfile[32];
356 get_wimfile(int index)
358 tsprintf(wimfile, T("wim%d"), index);
363 select_random_wimfile_index(void)
365 return in_use_wimfile_indices[rand32() % num_wimfiles_in_use];
369 select_new_wimfile(void)
373 while (wimfile_in_use[index])
376 in_use_wimfile_indices[num_wimfiles_in_use++] = index;
377 wimfile_in_use[index] = true;
379 return get_wimfile(index);
385 const tchar *wimfile = get_wimfile(index);
389 open_flags |= randbool() ? 0 : WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
391 printf("Opening %"TS" with flags 0x%08x\n", wimfile, open_flags);
393 CHECK_RET(wimlib_open_wim(wimfile, open_flags, &wim));
399 open_random_wim(void)
401 return open_wim(select_random_wimfile_index());
405 get_image_count(WIMStruct *wim)
407 struct wimlib_wim_info info;
409 CHECK_RET(wimlib_get_wim_info(wim, &info));
411 return info.image_count;
416 is_wimboot_capable(WIMStruct *wim)
418 struct wimlib_wim_info info;
420 CHECK_RET(wimlib_get_wim_info(wim, &info));
422 return info.wim_version == 0x10D00 &&
423 ((info.compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS &&
424 (info.chunk_size == 4096 || info.chunk_size == 8192 ||
425 info.chunk_size == 16384 || info.chunk_size == 32768)) ||
426 (info.compression_type == WIMLIB_COMPRESSION_TYPE_LZX &&
427 info.chunk_size == 32768));
429 #endif /* __WIN32__ */
432 overwrite_wim(WIMStruct *wim)
435 struct wimlib_wim_info info;
437 CHECK_RET(wimlib_get_wim_info(wim, &info));
439 switch (rand32() % 4) {
441 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
444 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
448 switch (rand32() % 8) {
450 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
453 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
457 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_RECOMPRESS;
458 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_FSYNC;
459 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_REBUILD;
460 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SOFT_DELETE;
461 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_IGNORE_READONLY_FLAG;
462 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_RETAIN_GUID;
463 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES;
464 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
466 if (rand32() % 8 == 0 &&
467 !(write_flags & WIMLIB_WRITE_FLAG_PIPABLE) &&
468 (!info.pipable || (write_flags & WIMLIB_WRITE_FLAG_NOT_PIPABLE)))
469 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
471 if (randbool() && !info.pipable &&
472 !(write_flags & (WIMLIB_WRITE_FLAG_RECOMPRESS |
473 WIMLIB_WRITE_FLAG_PIPABLE)))
474 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
476 printf("overwrite with flags: 0x%08x\n", write_flags);
478 CHECK_RET(wimlib_overwrite(wim, write_flags, 0));
482 get_random_write_flags(void)
486 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
487 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_SEND_DONE_WITH_FILE_MESSAGES;
488 write_flags |= randbool() ? 0 : WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
489 switch (rand32() % 8) {
491 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
494 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
502 get_random_chunk_size(int min_order, int max_order)
504 return 1 << (min_order + (rand32() % (max_order - min_order + 1)));
508 op__create_new_wim(void)
510 printf(":::op__create_new_wim\n");
512 const tchar *wimfile;
513 enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_NONE;
514 uint32_t chunk_size = 0;
515 uint32_t solid_chunk_size = 0;
519 if (num_wimfiles_in_use == MAX_NUM_WIMS)
522 wimfile = select_new_wimfile();
524 /* Select a random compression type and chunk size. */
525 switch (rand32() % 4) {
529 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
530 chunk_size = get_random_chunk_size(12, 16);
533 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
535 chunk_size = 1 << 15;
537 chunk_size = get_random_chunk_size(15, 21);
540 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
541 chunk_size = get_random_chunk_size(15, 28);
543 solid_chunk_size = get_random_chunk_size(15, 26);
545 solid_chunk_size = get_random_chunk_size(26, 28);
549 /* Select random write flags. */
550 write_flags = get_random_write_flags();
552 printf("Creating %"TS" with write flags 0x%08x, compression_type=%"TS", chunk_size=%u, solid_chunk_size=%u\n",
553 wimfile, write_flags,
554 wimlib_get_compression_type_string(ctype),
555 chunk_size, solid_chunk_size);
557 CHECK_RET(wimlib_create_new_wim(ctype, &wim));
559 CHECK_RET(wimlib_set_output_chunk_size(wim, chunk_size));
560 if (solid_chunk_size != 0)
561 CHECK_RET(wimlib_set_output_pack_chunk_size(wim, solid_chunk_size));
563 CHECK_RET(wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES, write_flags, 0));
569 op__add_empty_image_to_random_wim(void)
571 printf(":::op__add_empty_image_to_random_wim\n");
576 if (num_wimfiles_in_use < 1)
579 wim = open_random_wim();
580 CHECK_RET(wimlib_add_empty_image(wim, NULL, &new_idx));
581 printf("Adding empty image to %"TS" at index %d\n", wimfile, new_idx);
587 op__delete_random_image_from_random_wim(void)
589 printf(":::op__delete_random_image_from_random_wim\n");
595 if (num_wimfiles_in_use == 0)
598 wim = open_random_wim();
599 image_count = get_image_count(wim);
600 if (image_count != 0) {
601 image = 1 + (rand32() % image_count);
602 CHECK_RET(wimlib_delete_image(wim, image));
603 printf("Deleting image %d from %"TS"\n", image, wimfile);
610 op__delete_random_wim(void)
612 printf(":::op__delete_random_wim\n");
614 const tchar *wimfile;
618 if (num_wimfiles_in_use == 0)
621 which = rand32() % num_wimfiles_in_use;
622 index = in_use_wimfile_indices[which];
624 wimfile = get_wimfile(index);
626 ASSERT(!tunlink(wimfile), "failed to unlink %"TS": %m", wimfile);
628 printf("Deleted %"TS"\n", wimfile);
630 for (int i = which; i < num_wimfiles_in_use - 1; i++)
631 in_use_wimfile_indices[i] = in_use_wimfile_indices[i + 1];
632 num_wimfiles_in_use--;
633 wimfile_in_use[index] = false;
637 op__verify_random_wim(void)
639 printf(":::op__verify_random_wim\n");
643 if (num_wimfiles_in_use == 0)
646 wim = open_random_wim();
647 CHECK_RET(wimlib_verify_wim(wim, 0));
648 printf("Verified %"TS"\n", wimfile);
653 op__overwrite_with_no_changes(void)
655 printf(":::op__overwrite_with_no_changes\n");
659 if (num_wimfiles_in_use == 0)
662 wim = open_random_wim();
668 op__export_random_image(void)
670 printf(":::op__export_random_image\n");
672 int src_wimfile_index;
673 int dst_wimfile_index;
681 if (num_wimfiles_in_use < 2)
684 src_wimfile_index = select_random_wimfile_index();
686 dst_wimfile_index = select_random_wimfile_index();
687 } while (dst_wimfile_index == src_wimfile_index);
689 src_wim = open_wim(src_wimfile_index);
690 dst_wim = open_wim(dst_wimfile_index);
692 src_image_count = get_image_count(src_wim);
693 dst_image_count = get_image_count(dst_wim);
695 /* Choose a random source image --- single or all. */
696 src_image = WIMLIB_ALL_IMAGES;
697 if (src_image_count != 0 && randbool())
698 src_image = 1 + (rand32() % src_image_count);
700 printf("Exporting image %d of %d from wim %d into wim %d\n",
701 src_image, src_image_count, src_wimfile_index, dst_wimfile_index);
702 CHECK_RET(wimlib_export_image(src_wim, src_image, dst_wim, NULL, NULL, 0));
704 overwrite_wim(dst_wim);
705 wimlib_free(dst_wim);
707 dst_wim = open_wim(dst_wimfile_index);
709 /* Compare the images. */
710 dst_image = dst_image_count;
711 for (int image = (src_image == WIMLIB_ALL_IMAGES ? 1 : src_image);
712 image <= (src_image == WIMLIB_ALL_IMAGES ? src_image_count : src_image);
715 CHECK_RET(wimlib_compare_images(src_wim, image, dst_wim, ++dst_image, 0));
718 wimlib_free(src_wim);
719 wimlib_free(dst_wim);
723 op__apply_and_capture_test(void)
725 printf(":::op__apply_and_capture_test\n");
730 int extract_flags = 0;
734 if (num_wimfiles_in_use == 0)
737 /* Generate a random image. */
738 index = select_random_wimfile_index();
739 wim = open_wim(index);
741 CHECK_RET(wimlib_add_image(wim, (void *)rand32, NULL, NULL,
742 WIMLIB_ADD_FLAG_GENERATE_TEST_DATA |
743 WIMLIB_ADD_FLAG_NORPFIX));
745 image = get_image_count(wim);
747 printf("generated wim%d image %d\n", index, image);
751 * Compare the in-memory version of the generated image with a
752 * version written to disk
756 CHECK_RET(wimlib_write(wim, T("tmp.wim"), image, 0, 0));
757 CHECK_RET(wimlib_open_wim(T("tmp.wim"), 0, &tmp_wim));
758 CHECK_RET(wimlib_compare_images(wim, image, tmp_wim, 1, 0));
759 wimlib_free(tmp_wim);
765 /* Apply the generated image. */
766 wim = open_wim(index);
767 delete_directory_tree(TMP_TARGET_NAME);
770 printf("applying in NTFS mode\n");
771 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
772 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
773 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES;
774 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS;
775 add_flags |= WIMLIB_ADD_FLAG_NTFS;
776 cmp_flags |= WIMLIB_CMP_FLAG_NTFS_3G_MODE;
777 create_ntfs_volume(TMP_TARGET_NAME);
782 printf("applying in Windows mode\n");
783 cmp_flags |= WIMLIB_CMP_FLAG_WINDOWS_MODE;
784 #else /* __WIN32__ */
785 printf("applying in UNIX mode\n");
786 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
787 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
788 cmp_flags |= WIMLIB_CMP_FLAG_UNIX_MODE;
789 #endif /* !__WIN32__ */
791 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
792 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
795 /* Sometimes extract twice so that we test overwriting existing files.
797 if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) && randbool()) {
798 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
802 /* Capture the applied image. */
803 CHECK_RET(wimlib_add_image(wim, TMP_TARGET_NAME, NULL, NULL, add_flags));
807 /* Compare the generated image with the captured image. */
808 wim = open_wim(index);
809 CHECK_RET(wimlib_compare_images(wim, image, wim, image + 1, cmp_flags));
815 /* Enumerate and unregister all backing WIMs from the specified volume */
817 unregister_all_backing_wims(const tchar drive_letter)
822 DWORD bytes_returned;
823 const WIM_PROVIDER_OVERLAY_ENTRY *entry;
825 WOF_EXTERNAL_INFO wof_info;
826 WIM_PROVIDER_REMOVE_OVERLAY_INPUT wim;
829 wsprintf(volume, L"\\\\.\\%lc:", drive_letter);
831 h = CreateFile(volume, GENERIC_READ | GENERIC_WRITE,
832 FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
833 FILE_FLAG_BACKUP_SEMANTICS, NULL);
834 ASSERT(h != INVALID_HANDLE_VALUE,
835 "Failed to open %ls; error=%u", volume, (unsigned)GetLastError());
837 overlay_list = malloc(32768);
838 ASSERT(overlay_list != NULL, "out of memory");
840 in.wof_info.Version = WOF_CURRENT_VERSION;
841 in.wof_info.Provider = WOF_PROVIDER_WIM;
843 if (!DeviceIoControl(h, FSCTL_ENUM_OVERLAY,
844 &in, sizeof(WOF_EXTERNAL_INFO),
845 overlay_list, 32768, &bytes_returned, NULL))
847 ASSERT(GetLastError() == ERROR_INVALID_FUNCTION ||
848 GetLastError() == ERROR_INVALID_PARAMETER ||
849 GetLastError() == ERROR_FILE_NOT_FOUND,
850 "FSCTL_ENUM_OVERLAY failed; error=%u", GetLastError());
854 entry = overlay_list;
856 printf("Unregistering data source ID %"PRIu64"\n",
857 entry->DataSourceId.QuadPart);
858 in.wim.DataSourceId = entry->DataSourceId;
859 ASSERT(DeviceIoControl(h, FSCTL_REMOVE_OVERLAY, &in, sizeof(in),
860 NULL, 0, &bytes_returned, NULL),
861 "FSCTL_REMOVE_OVERLAY failed; error=%u",
862 (unsigned )GetLastError());
863 if (entry->NextEntryOffset == 0)
865 entry = (const WIM_PROVIDER_OVERLAY_ENTRY *)
866 ((const uint8_t *)entry + entry->NextEntryOffset);
873 op__wimboot_test(void)
882 if (num_wimfiles_in_use == 0)
885 index = select_random_wimfile_index();
887 unregister_all_backing_wims(L'E');
888 copy_file(get_wimfile(index), L"wimboot.wim");
890 CHECK_RET(wimlib_open_wim(L"wimboot.wim", 0, &wim));
892 image_count = get_image_count(wim);
893 if (image_count == 0 || !is_wimboot_capable(wim)) {
899 image = 1 + (rand32() % image_count);
901 printf("WIMBOOT test; wim%d image %d\n", index, image);
903 delete_directory_tree(TMP_TARGET_NAME);
905 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
906 WIMLIB_EXTRACT_FLAG_WIMBOOT));
909 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
910 WIMLIB_EXTRACT_FLAG_WIMBOOT));
913 index2 = select_random_wimfile_index();
914 wim2 = open_wim(index2);
915 image_count = get_image_count(wim2);
917 CHECK_RET(wimlib_add_image(wim2, TMP_TARGET_NAME, NULL, NULL,
918 WIMLIB_ADD_FLAG_NORPFIX));
923 wim2 = open_wim(index2);
925 printf("comparing wimboot.wim:%d with wim%d:%d\n",
926 image, index2, image_count + 1);
928 CHECK_RET(wimlib_compare_images(wim, image, wim2, image_count + 1,
929 WIMLIB_CMP_FLAG_WINDOWS_MODE));
934 #endif /* __WIN32__ */
937 is_solid_resource(const struct wimlib_resource_entry *resource, void *_ctx)
939 return resource->packed;
943 wim_contains_solid_resources(WIMStruct *wim)
945 return wimlib_iterate_lookup_table(wim, 0, is_solid_resource, NULL);
951 printf(":::op__split_test\n");
955 WIMStruct *joined_wim;
958 const tchar *globs[] = { T("tmp*.swm") };
961 if (num_wimfiles_in_use == 0)
964 /* split, join, and compare */
966 wim = open_random_wim();
968 if (wim_contains_solid_resources(wim)) {
969 /* Can't split a WIM containing solid resources */
974 image_count = get_image_count(wim);
976 part_size = 10000 + (rand32() % 1000000);
977 write_flags = get_random_write_flags();
978 write_flags &= ~WIMLIB_WRITE_FLAG_SOLID;
980 printf("splitting WIM %"TS": part_size=%"PRIu64", write_flags=0x%08x\n",
981 wimfile, part_size, write_flags);
983 CHECK_RET(wimlib_split(wim, T("tmp.swm"), part_size, write_flags));
985 CHECK_RET(wimlib_open_wim(T("tmp.swm"), WIMLIB_OPEN_FLAG_CHECK_INTEGRITY,
988 CHECK_RET(wimlib_reference_resource_files(swm, globs, 1,
989 WIMLIB_REF_FLAG_GLOB_ENABLE |
990 WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH,
991 WIMLIB_OPEN_FLAG_CHECK_INTEGRITY));
993 CHECK_RET(wimlib_verify_wim(swm, 0));
995 CHECK_RET(wimlib_write(swm, T("joined.wim"), WIMLIB_ALL_IMAGES, write_flags, 0));
998 CHECK_RET(wimlib_open_wim(T("joined.wim"), 0, &joined_wim));
999 for (int i = 1; i <= image_count; i++)
1000 CHECK_RET(wimlib_compare_images(wim, 1, joined_wim, 1, 0));
1001 CHECK_RET(wimlib_verify_wim(joined_wim, 0));
1002 wimlib_free(joined_wim);
1005 tunlink(T("tmp.swm"));
1006 for (int i = 2; ; i++) {
1008 tsprintf(name, T("tmp%d.swm"), i);
1015 op__set_compression_level(void)
1017 printf(":::op__set_compression_level\n");
1019 unsigned int level = rand32() % 100;
1020 printf("Changing compression levels to %d\n", level);
1021 wimlib_set_default_compression_level(-1, level);
1024 typedef void (*operation_func)(void);
1026 static const operation_func operation_table[] = {
1028 op__add_empty_image_to_random_wim,
1029 op__delete_random_image_from_random_wim,
1030 op__delete_random_wim,
1031 op__delete_random_wim,
1032 op__verify_random_wim,
1033 op__overwrite_with_no_changes,
1034 op__export_random_image,
1035 op__apply_and_capture_test,
1036 op__apply_and_capture_test,
1037 op__apply_and_capture_test,
1038 op__apply_and_capture_test,
1039 op__apply_and_capture_test,
1041 op__set_compression_level,
1048 extern int wmain(int argc, wchar_t **argv);
1053 main(int argc, tchar **argv)
1055 unsigned long long num_iterations;
1058 num_iterations = ULLONG_MAX;
1059 printf("Starting test runner\n");
1061 num_iterations = tstrtoull(argv[1], NULL, 10);
1062 printf("Starting test runner with %llu iterations\n",
1066 CHECK_RET(wimlib_global_init(0));
1067 wimlib_set_print_errors(true);
1069 change_to_temporary_directory();
1071 for (int i = 0; i < MAX_NUM_WIMS; i++)
1072 ASSERT(!tunlink(get_wimfile(i)) || errno == ENOENT, "unlink: %m");
1074 for (unsigned long long i = 0; i < num_iterations; i++) {
1075 printf("--> iteration %llu\n", i);
1076 (*operation_table[rand32() % ARRAY_LEN(operation_table)])();
1079 wimlib_global_cleanup();