2 * wlfuzz.c - Randomized tests for wimlib
6 * Copyright (C) 2015-2016 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 op__create_new_wim(void)
504 printf(":::op__create_new_wim\n");
506 const tchar *wimfile;
510 if (num_wimfiles_in_use == MAX_NUM_WIMS)
513 wimfile = select_new_wimfile();
515 CHECK_RET(wimlib_create_new_wim(WIMLIB_COMPRESSION_TYPE_NONE, &wim));
517 /* Select a random compression type and chunk size. */
518 switch (rand32() % 8) {
520 CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_NONE));
523 CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_XPRESS));
524 CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << (12 + rand32() % 5)));
527 CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_LZX));
529 CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << 15));
531 CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << (15 + rand32() % 7)));
534 CHECK_RET(wimlib_set_output_compression_type(wim, WIMLIB_COMPRESSION_TYPE_LZMS));
535 CHECK_RET(wimlib_set_output_chunk_size(wim, 1 << (15 + rand32() % 12)));
539 /* Select random write flags. */
540 write_flags = get_random_write_flags();
542 printf("Creating %"TS" with write flags 0x%08x\n", wimfile, write_flags);
544 CHECK_RET(wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES, write_flags, 0));
550 op__add_empty_image_to_random_wim(void)
552 printf(":::op__add_empty_image_to_random_wim\n");
557 if (num_wimfiles_in_use < 1)
560 wim = open_random_wim();
561 CHECK_RET(wimlib_add_empty_image(wim, NULL, &new_idx));
562 printf("Adding empty image to %"TS" at index %d\n", wimfile, new_idx);
568 op__delete_random_image_from_random_wim(void)
570 printf(":::op__delete_random_image_from_random_wim\n");
576 if (num_wimfiles_in_use == 0)
579 wim = open_random_wim();
580 image_count = get_image_count(wim);
581 if (image_count != 0) {
582 image = 1 + (rand32() % image_count);
583 CHECK_RET(wimlib_delete_image(wim, image));
584 printf("Deleting image %d from %"TS"\n", image, wimfile);
591 op__delete_random_wim(void)
593 printf(":::op__delete_random_wim\n");
595 const tchar *wimfile;
599 if (num_wimfiles_in_use == 0)
602 which = rand32() % num_wimfiles_in_use;
603 index = in_use_wimfile_indices[which];
605 wimfile = get_wimfile(index);
607 ASSERT(!tunlink(wimfile), "failed to unlink %"TS": %m", wimfile);
609 printf("Deleted %"TS"\n", wimfile);
611 for (int i = which; i < num_wimfiles_in_use - 1; i++)
612 in_use_wimfile_indices[i] = in_use_wimfile_indices[i + 1];
613 num_wimfiles_in_use--;
614 wimfile_in_use[index] = false;
618 op__verify_random_wim(void)
620 printf(":::op__verify_random_wim\n");
624 if (num_wimfiles_in_use == 0)
627 wim = open_random_wim();
628 CHECK_RET(wimlib_verify_wim(wim, 0));
629 printf("Verified %"TS"\n", wimfile);
634 op__overwrite_with_no_changes(void)
636 printf(":::op__overwrite_with_no_changes\n");
640 if (num_wimfiles_in_use == 0)
643 wim = open_random_wim();
649 op__export_random_image(void)
651 printf(":::op__export_random_image\n");
653 int src_wimfile_index;
654 int dst_wimfile_index;
662 if (num_wimfiles_in_use < 2)
665 src_wimfile_index = select_random_wimfile_index();
667 dst_wimfile_index = select_random_wimfile_index();
668 } while (dst_wimfile_index == src_wimfile_index);
670 src_wim = open_wim(src_wimfile_index);
671 dst_wim = open_wim(dst_wimfile_index);
673 src_image_count = get_image_count(src_wim);
674 dst_image_count = get_image_count(dst_wim);
676 /* Choose a random source image --- single or all. */
677 src_image = WIMLIB_ALL_IMAGES;
678 if (src_image_count != 0 && randbool())
679 src_image = 1 + (rand32() % src_image_count);
681 printf("Exporting image %d of %d from wim %d into wim %d\n",
682 src_image, src_image_count, src_wimfile_index, dst_wimfile_index);
683 CHECK_RET(wimlib_export_image(src_wim, src_image, dst_wim, NULL, NULL, 0));
685 overwrite_wim(dst_wim);
686 wimlib_free(dst_wim);
688 dst_wim = open_wim(dst_wimfile_index);
690 /* Compare the images. */
691 dst_image = dst_image_count;
692 for (int image = (src_image == WIMLIB_ALL_IMAGES ? 1 : src_image);
693 image <= (src_image == WIMLIB_ALL_IMAGES ? src_image_count : src_image);
696 CHECK_RET(wimlib_compare_images(src_wim, image, dst_wim, ++dst_image, 0));
699 wimlib_free(src_wim);
700 wimlib_free(dst_wim);
704 op__apply_and_capture_test(void)
706 printf(":::op__apply_and_capture_test\n");
711 int extract_flags = 0;
715 if (num_wimfiles_in_use == 0)
718 /* Generate a random image. */
719 index = select_random_wimfile_index();
720 wim = open_wim(index);
722 CHECK_RET(wimlib_add_image(wim, (void *)rand32, NULL, NULL,
723 WIMLIB_ADD_FLAG_GENERATE_TEST_DATA |
724 WIMLIB_ADD_FLAG_NORPFIX));
726 image = get_image_count(wim);
728 printf("generated wim%d image %d\n", index, image);
732 * Compare the in-memory version of the generated image with a
733 * version written to disk
737 CHECK_RET(wimlib_write(wim, T("tmp.wim"), image, 0, 0));
738 CHECK_RET(wimlib_open_wim(T("tmp.wim"), 0, &tmp_wim));
739 CHECK_RET(wimlib_compare_images(wim, image, tmp_wim, 1, 0));
740 wimlib_free(tmp_wim);
746 /* Apply the generated image. */
747 wim = open_wim(index);
748 delete_directory_tree(TMP_TARGET_NAME);
751 printf("applying in NTFS mode\n");
752 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
753 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
754 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_SHORT_NAMES;
755 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS;
756 add_flags |= WIMLIB_ADD_FLAG_NTFS;
757 cmp_flags |= WIMLIB_CMP_FLAG_NTFS_3G_MODE;
758 create_ntfs_volume(TMP_TARGET_NAME);
763 printf("applying in Windows mode\n");
764 cmp_flags |= WIMLIB_CMP_FLAG_WINDOWS_MODE;
765 #else /* __WIN32__ */
766 printf("applying in UNIX mode\n");
767 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
768 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
769 cmp_flags |= WIMLIB_CMP_FLAG_UNIX_MODE;
770 #endif /* !__WIN32__ */
772 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
773 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
776 /* Sometimes extract twice so that we test overwriting existing files.
778 if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) && randbool()) {
779 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
783 /* Capture the applied image. */
784 CHECK_RET(wimlib_add_image(wim, TMP_TARGET_NAME, NULL, NULL, add_flags));
788 /* Compare the generated image with the captured image. */
789 wim = open_wim(index);
790 CHECK_RET(wimlib_compare_images(wim, image, wim, image + 1, cmp_flags));
796 /* Enumerate and unregister all backing WIMs from the specified volume */
798 unregister_all_backing_wims(const tchar drive_letter)
803 DWORD bytes_returned;
804 const struct wim_provider_overlay_entry *entry;
806 struct wof_external_info wof_info;
807 struct wim_provider_remove_overlay_input wim;
810 wsprintf(volume, L"\\\\.\\%lc:", drive_letter);
812 h = CreateFile(volume, GENERIC_READ | GENERIC_WRITE,
813 FILE_SHARE_VALID_FLAGS, NULL, OPEN_EXISTING,
814 FILE_FLAG_BACKUP_SEMANTICS, NULL);
815 ASSERT(h != INVALID_HANDLE_VALUE,
816 "Failed to open %ls; error=%u", volume, (unsigned)GetLastError());
818 overlay_list = malloc(32768);
819 ASSERT(overlay_list != NULL, "out of memory");
821 in.wof_info.version = WOF_CURRENT_VERSION;
822 in.wof_info.provider = WOF_PROVIDER_WIM;
824 if (!DeviceIoControl(h, FSCTL_ENUM_OVERLAY,
825 &in, sizeof(struct wof_external_info),
826 overlay_list, 32768, &bytes_returned, NULL))
828 ASSERT(GetLastError() == ERROR_INVALID_FUNCTION ||
829 GetLastError() == ERROR_INVALID_PARAMETER ||
830 GetLastError() == ERROR_FILE_NOT_FOUND,
831 "FSCTL_ENUM_OVERLAY failed; error=%u", GetLastError());
835 entry = overlay_list;
837 printf("Unregistering data source ID %"PRIu64"\n",
838 entry->data_source_id);
839 in.wim.data_source_id = entry->data_source_id;
840 ASSERT(DeviceIoControl(h, FSCTL_REMOVE_OVERLAY, &in, sizeof(in),
841 NULL, 0, &bytes_returned, NULL),
842 "FSCTL_REMOVE_OVERLAY failed; error=%u",
843 (unsigned )GetLastError());
844 if (entry->next_entry_offset == 0)
846 entry = (const struct wim_provider_overlay_entry *)
847 ((const uint8_t *)entry + entry->next_entry_offset);
854 op__wimboot_test(void)
863 if (num_wimfiles_in_use == 0)
866 index = select_random_wimfile_index();
868 unregister_all_backing_wims(L'E');
869 copy_file(get_wimfile(index), L"wimboot.wim");
871 CHECK_RET(wimlib_open_wim(L"wimboot.wim", 0, &wim));
873 image_count = get_image_count(wim);
874 if (image_count == 0 || !is_wimboot_capable(wim)) {
880 image = 1 + (rand32() % image_count);
882 printf("WIMBOOT test; wim%d image %d\n", index, image);
884 delete_directory_tree(TMP_TARGET_NAME);
886 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
887 WIMLIB_EXTRACT_FLAG_WIMBOOT));
890 CHECK_RET(wimlib_extract_image(wim, image, TMP_TARGET_NAME,
891 WIMLIB_EXTRACT_FLAG_WIMBOOT));
894 index2 = select_random_wimfile_index();
895 wim2 = open_wim(index2);
896 image_count = get_image_count(wim2);
898 CHECK_RET(wimlib_add_image(wim2, TMP_TARGET_NAME, NULL, NULL,
899 WIMLIB_ADD_FLAG_NORPFIX));
904 wim2 = open_wim(index2);
906 printf("comparing wimboot.wim:%d with wim%d:%d\n",
907 image, index2, image_count + 1);
909 CHECK_RET(wimlib_compare_images(wim, image, wim2, image_count + 1,
910 WIMLIB_CMP_FLAG_WINDOWS_MODE));
915 #endif /* __WIN32__ */
918 is_solid_resource(const struct wimlib_resource_entry *resource, void *_ctx)
920 return resource->packed;
924 wim_contains_solid_resources(WIMStruct *wim)
926 return wimlib_iterate_lookup_table(wim, 0, is_solid_resource, NULL);
932 printf(":::op__split_test\n");
936 WIMStruct *joined_wim;
939 const tchar *globs[] = { T("tmp*.swm") };
942 if (num_wimfiles_in_use == 0)
945 /* split, join, and compare */
947 wim = open_random_wim();
949 if (wim_contains_solid_resources(wim)) {
950 /* Can't split a WIM containing solid resources */
955 image_count = get_image_count(wim);
957 part_size = 10000 + (rand32() % 1000000);
958 write_flags = get_random_write_flags();
959 write_flags &= ~WIMLIB_WRITE_FLAG_SOLID;
961 printf("splitting WIM %"TS": part_size=%"PRIu64", write_flags=0x%08x\n",
962 wimfile, part_size, write_flags);
964 CHECK_RET(wimlib_split(wim, T("tmp.swm"), part_size, write_flags));
966 CHECK_RET(wimlib_open_wim(T("tmp.swm"), WIMLIB_OPEN_FLAG_CHECK_INTEGRITY,
969 CHECK_RET(wimlib_reference_resource_files(swm, globs, 1,
970 WIMLIB_REF_FLAG_GLOB_ENABLE |
971 WIMLIB_REF_FLAG_GLOB_ERR_ON_NOMATCH,
972 WIMLIB_OPEN_FLAG_CHECK_INTEGRITY));
974 CHECK_RET(wimlib_verify_wim(swm, 0));
976 CHECK_RET(wimlib_write(swm, T("joined.wim"), WIMLIB_ALL_IMAGES, write_flags, 0));
979 CHECK_RET(wimlib_open_wim(T("joined.wim"), 0, &joined_wim));
980 for (int i = 1; i <= image_count; i++)
981 CHECK_RET(wimlib_compare_images(wim, 1, joined_wim, 1, 0));
982 CHECK_RET(wimlib_verify_wim(joined_wim, 0));
983 wimlib_free(joined_wim);
986 tunlink(T("tmp.swm"));
987 for (int i = 2; ; i++) {
989 tsprintf(name, T("tmp%d.swm"), i);
996 op__set_compression_level(void)
998 printf(":::op__set_compression_level\n");
1000 unsigned int level = rand32() % 100;
1001 printf("Changing compression levels to %d\n", level);
1002 wimlib_set_default_compression_level(-1, level);
1005 typedef void (*operation_func)(void);
1007 static const operation_func operation_table[] = {
1009 op__add_empty_image_to_random_wim,
1010 op__delete_random_image_from_random_wim,
1011 op__delete_random_wim,
1012 op__delete_random_wim,
1013 op__verify_random_wim,
1014 op__overwrite_with_no_changes,
1015 op__export_random_image,
1016 op__apply_and_capture_test,
1017 op__apply_and_capture_test,
1018 op__apply_and_capture_test,
1019 op__apply_and_capture_test,
1020 op__apply_and_capture_test,
1022 op__set_compression_level,
1029 extern int wmain(int argc, wchar_t **argv);
1034 main(int argc, tchar **argv)
1036 unsigned long long num_iterations;
1039 num_iterations = ULLONG_MAX;
1040 printf("Starting test runner\n");
1042 num_iterations = tstrtoull(argv[1], NULL, 10);
1043 printf("Starting test runner with %llu iterations\n",
1047 CHECK_RET(wimlib_global_init(0));
1048 wimlib_set_print_errors(true);
1050 change_to_temporary_directory();
1052 for (int i = 0; i < MAX_NUM_WIMS; i++)
1053 ASSERT(!tunlink(get_wimfile(i)) || errno == ENOENT, "unlink: %m");
1055 for (unsigned long long i = 0; i < num_iterations; i++) {
1056 printf("--> iteration %llu\n", i);
1057 (*operation_table[rand32() % ARRAY_LEN(operation_table)])();
1060 wimlib_global_cleanup();