4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012-2016 Eric Biggers
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # include "config.h" /* Need for PACKAGE_VERSION, etc. */
30 #include "wimlib_tchar.h"
49 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
52 # include "imagex-win32.h"
53 # define print_security_descriptor win32_print_security_descriptor
56 # include <langinfo.h>
57 # define print_security_descriptor default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
63 /* Don't confuse the user by presenting the mounting commands on Windows when
64 * they will never work. However on UNIX-like systems we always present them,
65 * even if WITH_FUSE is not defined at this point, as to not tie the build of
66 * wimlib-imagex to a specific build of wimlib. */
68 # define WIM_MOUNTING_SUPPORTED 0
70 # define WIM_MOUNTING_SUPPORTED 1
73 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
76 is_any_path_separator(tchar c)
78 return c == T('/') || c == T('\\');
81 /* Like basename(), but handles both forward and backwards slashes. */
83 tbasename(tchar *path)
85 tchar *p = tstrchr(path, T('\0'));
90 if (!is_any_path_separator(*--p))
98 if (is_any_path_separator(*--p))
103 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
117 #if WIM_MOUNTING_SUPPORTED
123 #if WIM_MOUNTING_SUPPORTED
131 static void usage(int cmd, FILE *fp);
132 static void usage_all(FILE *fp);
133 static void recommend_man_page(int cmd, FILE *fp);
134 static const tchar *get_cmd_string(int cmd, bool nospace);
136 static bool imagex_be_quiet = false;
137 static FILE *imagex_info_file;
139 #define imagex_printf(format, ...) \
140 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
143 IMAGEX_ALLOW_OTHER_OPTION,
147 IMAGEX_CHUNK_SIZE_OPTION,
148 IMAGEX_COMMAND_OPTION,
149 IMAGEX_COMMIT_OPTION,
150 IMAGEX_COMPACT_OPTION,
151 IMAGEX_COMPRESS_OPTION,
152 IMAGEX_CONFIG_OPTION,
154 IMAGEX_DELTA_FROM_OPTION,
155 IMAGEX_DEREFERENCE_OPTION,
156 IMAGEX_DEST_DIR_OPTION,
157 IMAGEX_DETAILED_OPTION,
158 IMAGEX_EXTRACT_XML_OPTION,
161 IMAGEX_HEADER_OPTION,
162 IMAGEX_IMAGE_PROPERTY_OPTION,
163 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
165 IMAGEX_METADATA_OPTION,
166 IMAGEX_NEW_IMAGE_OPTION,
167 IMAGEX_NOCHECK_OPTION,
168 IMAGEX_NORPFIX_OPTION,
169 IMAGEX_NOT_PIPABLE_OPTION,
170 IMAGEX_NO_ACLS_OPTION,
171 IMAGEX_NO_ATTRIBUTES_OPTION,
172 IMAGEX_NO_GLOBS_OPTION,
173 IMAGEX_NO_REPLACE_OPTION,
174 IMAGEX_NO_SOLID_SORT_OPTION,
175 IMAGEX_NULLGLOB_OPTION,
176 IMAGEX_ONE_FILE_ONLY_OPTION,
178 IMAGEX_PIPABLE_OPTION,
179 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
180 IMAGEX_REBUILD_OPTION,
181 IMAGEX_RECOMPRESS_OPTION,
182 IMAGEX_RECURSIVE_OPTION,
185 IMAGEX_SNAPSHOT_OPTION,
187 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
188 IMAGEX_SOLID_COMPRESS_OPTION,
190 IMAGEX_SOURCE_LIST_OPTION,
191 IMAGEX_STAGING_DIR_OPTION,
192 IMAGEX_STREAMS_INTERFACE_OPTION,
193 IMAGEX_STRICT_ACLS_OPTION,
194 IMAGEX_THREADS_OPTION,
195 IMAGEX_TO_STDOUT_OPTION,
196 IMAGEX_UNIX_DATA_OPTION,
197 IMAGEX_UNSAFE_COMPACT_OPTION,
198 IMAGEX_UPDATE_OF_OPTION,
199 IMAGEX_VERBOSE_OPTION,
200 IMAGEX_WIMBOOT_CONFIG_OPTION,
201 IMAGEX_WIMBOOT_OPTION,
205 static const struct option apply_options[] = {
206 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
207 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
208 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
209 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
210 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
211 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
212 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
213 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
214 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
215 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
216 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
217 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
218 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
222 static const struct option capture_or_append_options[] = {
223 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
224 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
225 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
226 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
227 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
228 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
229 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
230 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
231 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
232 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
233 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
234 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
235 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
236 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
237 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
238 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
239 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
240 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
241 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
242 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
243 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
244 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
245 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
246 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
247 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
248 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
249 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
250 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
251 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
252 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
253 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
257 static const struct option delete_options[] = {
258 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
259 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
260 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
264 static const struct option dir_options[] = {
265 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
266 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
267 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
268 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
272 static const struct option export_options[] = {
273 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
274 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
275 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
276 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
277 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
278 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
279 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
280 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
281 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
282 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
283 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
284 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
285 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
286 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
287 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
288 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
289 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
290 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
294 static const struct option extract_options[] = {
295 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
296 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
297 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
298 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
299 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
300 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
301 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
302 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
303 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
304 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
305 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
306 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
307 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
308 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
309 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
310 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
311 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
315 static const struct option info_options[] = {
316 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
317 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
318 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
319 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
320 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
321 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
322 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
323 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
324 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
325 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
329 static const struct option join_options[] = {
330 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
334 static const struct option mount_options[] = {
335 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
336 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
337 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
338 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
339 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
340 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
341 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
345 static const struct option optimize_options[] = {
346 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
347 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
348 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
349 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
350 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
351 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
352 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
353 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
354 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
355 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
356 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
357 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
358 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
359 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
363 static const struct option split_options[] = {
364 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
368 static const struct option unmount_options[] = {
369 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
370 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
371 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
372 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
373 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
374 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
378 static const struct option update_options[] = {
379 /* Careful: some of the options here set the defaults for update
380 * commands, but the flags given to an actual update command (and not to
381 * `imagex update' itself are also handled in
382 * update_command_add_option(). */
383 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
384 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
385 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
386 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
387 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
389 /* Default delete options */
390 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
391 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
393 /* Global add option */
394 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
396 /* Default add options */
397 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
398 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
399 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
400 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
401 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
402 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
403 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
404 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
409 static const struct option verify_options[] = {
410 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
411 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
417 # define _format_attribute(type, format_str, args_start) \
418 __attribute__((format(type, format_str, args_start)))
420 # define _format_attribute(type, format_str, args_start)
423 /* Print formatted error message to stderr. */
424 static void _format_attribute(printf, 1, 2)
425 imagex_error(const tchar *format, ...)
428 va_start(va, format);
429 tfputs(T("ERROR: "), stderr);
430 tvfprintf(stderr, format, va);
431 tputc(T('\n'), stderr);
435 /* Print formatted error message to stderr. */
436 static void _format_attribute(printf, 1, 2)
437 imagex_error_with_errno(const tchar *format, ...)
439 int errno_save = errno;
441 va_start(va, format);
442 tfputs(T("ERROR: "), stderr);
443 tvfprintf(stderr, format, va);
444 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
449 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
451 if (image == WIMLIB_NO_IMAGE) {
452 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
453 " Please specify a 1-based image index or "
454 "image name. To list the images\n"
455 " contained in the WIM archive, run\n"
457 " %"TS" \"%"TS"\"\n"),
458 image_name, wim_name,
459 get_cmd_string(CMD_INFO, false), wim_name);
460 return WIMLIB_ERR_INVALID_IMAGE;
466 verify_image_is_single(int image)
468 if (image == WIMLIB_ALL_IMAGES) {
469 imagex_error(T("Cannot specify all images for this action!"));
470 return WIMLIB_ERR_INVALID_IMAGE;
476 verify_image_exists_and_is_single(int image, const tchar *image_name,
477 const tchar *wim_name)
480 ret = verify_image_exists(image, image_name, wim_name);
482 ret = verify_image_is_single(image);
487 print_available_compression_types(FILE *fp)
489 static const tchar *s =
491 "Available compression types:\n"
494 " xpress (alias: \"fast\")\n"
495 " lzx (alias: \"maximum\") (default for capture)\n"
496 " lzms (alias: \"recovery\")\n"
502 /* Parse the argument to --compress or --solid-compress */
504 get_compression_type(tchar *optarg, bool solid)
507 unsigned int compression_level = 0;
510 plevel = tstrchr(optarg, T(':'));
516 ultmp = tstrtoul(plevel, &ptmp, 10);
517 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
518 imagex_error(T("Compression level must be a positive integer! "
519 "e.g. --compress=lzx:80"));
520 return WIMLIB_COMPRESSION_TYPE_INVALID;
522 compression_level = ultmp;
525 if (!tstrcasecmp(optarg, T("maximum")) ||
526 !tstrcasecmp(optarg, T("lzx")) ||
527 !tstrcasecmp(optarg, T("max"))) {
528 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
529 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
530 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
531 } else if (!tstrcasecmp(optarg, T("recovery"))) {
535 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
536 " differently from DISM. Instead, you typically want to use '--solid' to\n"
537 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
538 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
539 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
540 " of '--compress=recovery'.\n"));
542 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
543 } else if (!tstrcasecmp(optarg, T("lzms"))) {
544 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
545 } else if (!tstrcasecmp(optarg, T("none"))) {
546 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
548 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
549 print_available_compression_types(stderr);
550 return WIMLIB_COMPRESSION_TYPE_INVALID;
553 if (compression_level != 0)
554 wimlib_set_default_compression_level(ctype, compression_level);
558 /* Parse the argument to --compact */
560 set_compact_mode(const tchar *arg, int *extract_flags)
563 if (!tstrcasecmp(arg, T("xpress4k")))
564 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
565 else if (!tstrcasecmp(arg, T("xpress8k")))
566 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
567 else if (!tstrcasecmp(arg, T("xpress16k")))
568 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
569 else if (!tstrcasecmp(arg, T("lzx")))
570 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
573 *extract_flags |= flag;
578 "\"%"TS"\" is not a recognized System Compression format. The options are:"
580 " --compact=xpress4k\n"
581 " --compact=xpress8k\n"
582 " --compact=xpress16k\n"
591 unsigned num_strings;
592 unsigned num_alloc_strings;
595 #define STRING_LIST_INITIALIZER \
596 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
598 #define STRING_LIST(_strings) \
599 struct string_list _strings = STRING_LIST_INITIALIZER
602 string_list_append(struct string_list *list, tchar *glob)
604 unsigned num_alloc_strings = list->num_alloc_strings;
606 if (list->num_strings == num_alloc_strings) {
609 num_alloc_strings += 4;
610 new_strings = realloc(list->strings,
611 sizeof(list->strings[0]) * num_alloc_strings);
613 imagex_error(T("Out of memory!"));
616 list->strings = new_strings;
617 list->num_alloc_strings = num_alloc_strings;
619 list->strings[list->num_strings++] = glob;
624 string_list_destroy(struct string_list *list)
630 wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags)
632 return wimlib_reference_resource_files(wim, (const tchar **)list->strings,
634 WIMLIB_REF_FLAG_GLOB_ENABLE,
639 append_image_property_argument(struct string_list *image_properties)
641 if (!tstrchr(optarg, '=')) {
642 imagex_error(T("'--image-property' argument "
643 "must be in the form NAME=VALUE"));
646 return string_list_append(image_properties, optarg);
650 apply_image_properties(struct string_list *image_properties,
651 WIMStruct *wim, int image, bool *any_changes_ret)
653 bool any_changes = false;
654 for (unsigned i = 0; i < image_properties->num_strings; i++) {
656 const tchar *current_value;
659 name = image_properties->strings[i];
660 value = tstrchr(name, '=');
663 current_value = wimlib_get_image_property(wim, image, name);
664 if (current_value && !tstrcmp(current_value, value)) {
665 imagex_printf(T("The %"TS" property of image %d "
666 "already has value \"%"TS"\".\n"),
669 imagex_printf(T("Setting the %"TS" property of image "
670 "%d to \"%"TS"\".\n"),
672 ret = wimlib_set_image_property(wim, image, name, value);
679 *any_changes_ret = any_changes;
684 do_resource_not_found_warning(const tchar *wimfile,
685 const struct wimlib_wim_info *info,
686 const struct string_list *refglobs)
688 if (info->total_parts > 1) {
689 if (refglobs->num_strings == 0) {
690 imagex_error(T("\"%"TS"\" is part of a split WIM. "
691 "Use --ref to specify the other parts."),
694 imagex_error(T("Perhaps the '--ref' argument did not "
695 "specify all other parts of the split "
699 imagex_error(T("If this is a delta WIM, use the --ref argument "
700 "to specify the WIM(s) on which it is based."));
705 do_metadata_not_found_warning(const tchar *wimfile,
706 const struct wimlib_wim_info *info)
708 if (info->part_number != 1) {
709 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
710 " You must specify the first part."),
715 /* Returns the size of a file given its name, or -1 if the file does not exist
716 * or its size cannot be determined. */
718 file_get_size(const tchar *filename)
721 if (tstat(filename, &st) == 0)
728 PARSE_STRING_SUCCESS = 0,
729 PARSE_STRING_FAILURE = 1,
730 PARSE_STRING_NONE = 2,
734 * Parses a string token from an array of characters.
736 * Tokens are either whitespace-delimited, or double or single-quoted.
738 * @line_p: Pointer to the pointer to the line of data. Will be updated
739 * to point past the string token iff the return value is
740 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
743 * @len_p: @len_p initially stores the length of the line of data, which may
744 * be 0, and it will be updated to the number of bytes remaining in
745 * the line iff the return value is PARSE_STRING_SUCCESS.
747 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
748 * parsed string token will be returned here.
750 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
751 * PARSE_STRING_FAILURE if the data was invalid due to a missing
752 * closing quote; or PARSE_STRING_NONE if the line ended before the
753 * beginning of a string token was found.
756 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
759 tchar *line = *line_p;
763 /* Skip leading whitespace */
766 return PARSE_STRING_NONE;
767 if (!istspace(*line) && *line != T('\0'))
773 if (quote_char == T('"') || quote_char == T('\'')) {
778 line = tmemchr(line, quote_char, len);
780 imagex_error(T("Missing closing quote: %"TS), fn - 1);
781 return PARSE_STRING_FAILURE;
784 /* Unquoted string. Go until whitespace. Line is terminated
785 * by '\0', so no need to check 'len'. */
789 } while (!istspace(*line) && *line != T('\0'));
796 return PARSE_STRING_SUCCESS;
799 /* Parses a line of data (not an empty line or comment) in the source list file
800 * format. (See the man page for 'wimlib-imagex capture' for details on this
801 * format and the meaning.)
803 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
804 * len == 0. The data in @line will be modified by this function call.
806 * @len: Length of the line of data.
808 * @source: On success, the capture source and target described by the line is
809 * written into this destination. Note that it will contain pointers
810 * to data in the @line array.
812 * Returns true if the line was valid; false otherwise. */
814 parse_source_list_line(tchar *line, size_t len,
815 struct wimlib_capture_source *source)
819 ret = parse_string(&line, &len, &source->fs_source_path);
820 if (ret != PARSE_STRING_SUCCESS)
822 ret = parse_string(&line, &len, &source->wim_target_path);
823 if (ret == PARSE_STRING_NONE)
824 source->wim_target_path = source->fs_source_path;
825 return ret != PARSE_STRING_FAILURE;
828 /* Returns %true if the given line of length @len > 0 is a comment or empty line
829 * in the source list file format. */
831 is_comment_line(const tchar *line, size_t len)
834 if (*line == T('#') || *line == T(';'))
836 if (!istspace(*line) && *line != T('\0'))
846 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
849 tchar *contents = *contents_p;
850 size_t nchars = *nchars_p;
853 for (i = 0; i < nchars; i++)
854 if (contents[i] == T('\n'))
857 /* Handle last line not terminated by a newline */
858 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
859 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
861 imagex_error(T("Out of memory!"));
864 contents[nchars] = T('\n');
865 *contents_p = contents;
873 /* Parses a file in the source list format. (See the man page for
874 * 'wimlib-imagex capture' for details on this format and the meaning.)
876 * @source_list_contents: Contents of the source list file. Note that this
877 * buffer will be modified to save memory allocations,
878 * and cannot be freed until the returned array of
879 * wimlib_capture_source's has also been freed.
881 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
884 * @nsources_ret: On success, the length of the returned array is
887 * Returns: An array of `struct wimlib_capture_source's that can be passed to
888 * the wimlib_add_image_multisource() function to specify how a WIM image is to
890 static struct wimlib_capture_source *
891 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
892 size_t *nsources_ret)
896 struct wimlib_capture_source *sources;
899 nlines = text_file_count_lines(source_list_contents_p,
900 &source_list_nchars);
904 /* Always allocate at least 1 slot, just in case the implementation of
905 * calloc() returns NULL if 0 bytes are requested. */
906 sources = calloc(nlines ?: 1, sizeof(*sources));
908 imagex_error(T("out of memory"));
911 p = *source_list_contents_p;
913 for (i = 0; i < nlines; i++) {
914 /* XXX: Could use rawmemchr() here instead, but it may not be
915 * available on all platforms. */
916 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
917 size_t len = endp - p + 1;
919 if (!is_comment_line(p, len)) {
920 if (!parse_source_list_line(p, len, &sources[j++])) {
932 /* Reads the contents of a file into memory. */
934 file_get_contents(const tchar *filename, size_t *len_ret)
941 if (tstat(filename, &stbuf) != 0) {
942 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
947 fp = tfopen(filename, T("rb"));
949 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
953 buf = malloc(len ? len : 1);
955 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
956 "contents of file \"%"TS"\""), len, filename);
959 if (fread(buf, 1, len, fp) != len) {
960 imagex_error_with_errno(T("Failed to read %zu bytes from the "
961 "file \"%"TS"\""), len, filename);
975 /* Read standard input until EOF and return the full contents in a malloc()ed
976 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
979 stdin_get_contents(size_t *len_ret)
981 /* stdin can, of course, be a pipe or other non-seekable file, so the
982 * total length of the data cannot be pre-determined */
984 size_t newlen = 1024;
988 char *p = realloc(buf, newlen);
989 size_t bytes_read, bytes_to_read;
991 imagex_error(T("out of memory while reading stdin"));
995 bytes_to_read = newlen - pos;
996 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
998 if (bytes_read != bytes_to_read) {
1003 imagex_error_with_errno(T("error reading stdin"));
1017 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1020 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1022 *num_tchars_ret = num_bytes;
1024 #else /* !__WIN32__ */
1025 /* On Windows, translate the text to UTF-16LE */
1029 if (num_bytes >= 2 &&
1030 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1031 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1033 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1034 * with something that looks like an ASCII character encoded as
1035 * a UTF-16LE code unit. Assume the file is encoded as
1036 * UTF-16LE. This is not a 100% reliable check. */
1037 num_wchars = num_bytes / 2;
1038 text_wstr = (wchar_t*)text;
1040 /* File does not look like UTF-16LE. Assume it is encoded in
1041 * the current Windows code page. I think these are always
1042 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1043 * should work as expected. */
1044 text_wstr = win32_mbs_to_wcs(text,
1049 *num_tchars_ret = num_wchars;
1051 #endif /* __WIN32__ */
1055 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1060 contents = file_get_contents(filename, &num_bytes);
1063 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1067 stdin_get_text_contents(size_t *num_tchars_ret)
1072 contents = stdin_get_contents(&num_bytes);
1075 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1078 #define TO_PERCENT(numerator, denominator) \
1079 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1081 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1082 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1083 #define KIBIBYTE_MIN_NBYTES 10000ULL
1086 get_unit(uint64_t total_bytes, const tchar **name_ret)
1088 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1089 *name_ret = T("GiB");
1091 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1092 *name_ret = T("MiB");
1094 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1095 *name_ret = T("KiB");
1098 *name_ret = T("bytes");
1103 static struct wimlib_progress_info_scan last_scan_progress;
1106 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1108 uint64_t prev_count, cur_count;
1110 prev_count = last_scan_progress.num_nondirs_scanned +
1111 last_scan_progress.num_dirs_scanned;
1112 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1114 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1115 cur_count % 128 == 0)
1117 unsigned unit_shift;
1118 const tchar *unit_name;
1120 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1121 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1122 "%"PRIu64" directories) "),
1123 scan->num_bytes_scanned >> unit_shift,
1125 scan->num_nondirs_scanned,
1126 scan->num_dirs_scanned);
1127 last_scan_progress = *scan;
1130 /* Progress callback function passed to various wimlib functions. */
1131 static enum wimlib_progress_status
1132 imagex_progress_func(enum wimlib_progress_msg msg,
1133 union wimlib_progress_info *info,
1134 void *_ignored_context)
1136 unsigned percent_done;
1137 unsigned unit_shift;
1138 const tchar *unit_name;
1140 if (imagex_be_quiet)
1141 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1143 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1145 static bool started;
1147 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1148 imagex_printf(T("Using %"TS" compression "
1149 "with %u thread%"TS"\n"),
1150 wimlib_get_compression_type_string(
1151 info->write_streams.compression_type),
1152 info->write_streams.num_threads,
1153 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1158 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1159 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1160 info->write_streams.total_bytes);
1162 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1163 info->write_streams.completed_bytes >> unit_shift,
1165 info->write_streams.total_bytes >> unit_shift,
1168 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1169 imagex_printf(T("\n"));
1171 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1172 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1173 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1174 imagex_printf(T("\n"));
1176 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1177 info->scan.wim_target_path);
1179 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1181 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1182 switch (info->scan.status) {
1183 case WIMLIB_SCAN_DENTRY_OK:
1184 report_scan_progress(&info->scan, false);
1186 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1187 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1189 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1190 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1191 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1193 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1194 /* Symlink fixups are enabled by default. This is
1195 * mainly intended for Windows, which for some reason
1196 * uses absolute junctions (with drive letters!) in the
1197 * default installation. On UNIX-like systems, warn the
1198 * user when fixing the target of an absolute symbolic
1199 * link, so they know to disable this if they want. */
1201 imagex_printf(T("\nWARNING: Adjusted target of "
1202 "absolute symbolic link \"%"TS"\"\n"
1203 " (Use --norpfix to capture "
1204 "absolute symbolic links as-is)\n"),
1205 info->scan.cur_path);
1212 case WIMLIB_PROGRESS_MSG_SCAN_END:
1213 report_scan_progress(&info->scan, true);
1214 imagex_printf(T("\n"));
1216 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1217 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1218 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1219 info->integrity.total_bytes);
1220 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1221 "of %"PRIu64" %"TS" (%u%%) done"),
1222 info->integrity.filename,
1223 info->integrity.completed_bytes >> unit_shift,
1225 info->integrity.total_bytes >> unit_shift,
1228 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1229 imagex_printf(T("\n"));
1231 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1232 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1233 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1234 info->integrity.total_bytes);
1235 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1236 "of %"PRIu64" %"TS" (%u%%) done"),
1237 info->integrity.completed_bytes >> unit_shift,
1239 info->integrity.total_bytes >> unit_shift,
1242 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1243 imagex_printf(T("\n"));
1245 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1246 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1247 "to %"TS" \"%"TS"\"\n"),
1248 info->extract.image,
1249 info->extract.image_name,
1250 info->extract.wimfile_name,
1251 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1252 T("NTFS volume") : T("directory")),
1253 info->extract.target);
1255 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1256 if (info->extract.end_file_count >= 2000) {
1257 percent_done = TO_PERCENT(info->extract.current_file_count,
1258 info->extract.end_file_count);
1259 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1260 info->extract.current_file_count,
1261 info->extract.end_file_count, percent_done);
1262 if (info->extract.current_file_count == info->extract.end_file_count)
1263 imagex_printf(T("\n"));
1266 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1267 percent_done = TO_PERCENT(info->extract.completed_bytes,
1268 info->extract.total_bytes);
1269 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1270 imagex_printf(T("\rExtracting file data: "
1271 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1272 info->extract.completed_bytes >> unit_shift,
1274 info->extract.total_bytes >> unit_shift,
1277 if (info->extract.completed_bytes >= info->extract.total_bytes)
1278 imagex_printf(T("\n"));
1280 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1281 if (info->extract.end_file_count >= 2000) {
1282 percent_done = TO_PERCENT(info->extract.current_file_count,
1283 info->extract.end_file_count);
1284 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1285 info->extract.current_file_count,
1286 info->extract.end_file_count, percent_done);
1287 if (info->extract.current_file_count == info->extract.end_file_count)
1288 imagex_printf(T("\n"));
1291 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1292 if (info->extract.total_parts != 1) {
1293 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1294 info->extract.part_number,
1295 info->extract.total_parts);
1298 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1299 percent_done = TO_PERCENT(info->split.completed_bytes,
1300 info->split.total_bytes);
1301 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1302 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1303 "%"PRIu64" %"TS" (%u%%) written\n"),
1304 info->split.part_name,
1305 info->split.cur_part_number,
1306 info->split.total_parts,
1307 info->split.completed_bytes >> unit_shift,
1309 info->split.total_bytes >> unit_shift,
1313 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1314 if (info->split.completed_bytes == info->split.total_bytes) {
1315 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1316 info->split.cur_part_number,
1317 info->split.total_parts);
1320 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1321 switch (info->update.command->op) {
1322 case WIMLIB_UPDATE_OP_DELETE:
1323 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1324 info->update.command->delete_.wim_path);
1326 case WIMLIB_UPDATE_OP_RENAME:
1327 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1328 info->update.command->rename.wim_source_path,
1329 info->update.command->rename.wim_target_path);
1331 case WIMLIB_UPDATE_OP_ADD:
1336 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1337 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1338 info->replace.path_in_wim);
1340 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1341 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1342 info->wimboot_exclude.path_in_wim);
1344 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1345 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1346 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1347 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1348 info->unmount.mounted_wim,
1349 info->unmount.mounted_image);
1351 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1352 info->unmount.mounted_wim,
1353 info->unmount.mounted_image);
1354 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1358 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1359 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1360 info->verify_image.current_image,
1361 info->verify_image.total_images);
1363 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1364 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1365 info->verify_streams.total_bytes);
1366 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1367 imagex_printf(T("\rVerifying file data: "
1368 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1369 info->verify_streams.completed_bytes >> unit_shift,
1371 info->verify_streams.total_bytes >> unit_shift,
1374 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1375 imagex_printf(T("\n"));
1380 fflush(imagex_info_file);
1381 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1385 parse_num_threads(const tchar *optarg)
1388 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1389 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1390 imagex_error(T("Number of threads must be a non-negative integer!"));
1398 parse_chunk_size(const tchar *optarg)
1401 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1402 if (chunk_size == 0) {
1403 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1404 " with optional K, M, or G suffix"));
1408 if (*tmp == T('k') || *tmp == T('K')) {
1411 } else if (*tmp == T('m') || *tmp == T('M')) {
1414 } else if (*tmp == T('g') || *tmp == T('G')) {
1418 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1419 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1423 if (chunk_size >= UINT32_MAX) {
1424 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1432 * Parse an option passed to an update command.
1434 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1437 * @option: Text string for the option (beginning with --)
1439 * @cmd: `struct wimlib_update_command' that is being constructed for
1442 * Returns true if the option was recognized; false if not.
1445 update_command_add_option(int op, const tchar *option,
1446 struct wimlib_update_command *cmd)
1448 bool recognized = true;
1450 case WIMLIB_UPDATE_OP_ADD:
1451 if (!tstrcmp(option, T("--verbose")))
1452 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1453 else if (!tstrcmp(option, T("--unix-data")))
1454 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1455 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1456 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1457 else if (!tstrcmp(option, T("--strict-acls")))
1458 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1459 else if (!tstrcmp(option, T("--dereference")))
1460 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1461 else if (!tstrcmp(option, T("--no-replace")))
1462 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1466 case WIMLIB_UPDATE_OP_DELETE:
1467 if (!tstrcmp(option, T("--force")))
1468 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1469 else if (!tstrcmp(option, T("--recursive")))
1470 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1481 /* How many nonoption arguments each `imagex update' command expects */
1482 static const unsigned update_command_num_nonoptions[] = {
1483 [WIMLIB_UPDATE_OP_ADD] = 2,
1484 [WIMLIB_UPDATE_OP_DELETE] = 1,
1485 [WIMLIB_UPDATE_OP_RENAME] = 2,
1489 update_command_add_nonoption(int op, const tchar *nonoption,
1490 struct wimlib_update_command *cmd,
1491 unsigned num_nonoptions)
1494 case WIMLIB_UPDATE_OP_ADD:
1495 if (num_nonoptions == 0)
1496 cmd->add.fs_source_path = (tchar*)nonoption;
1498 cmd->add.wim_target_path = (tchar*)nonoption;
1500 case WIMLIB_UPDATE_OP_DELETE:
1501 cmd->delete_.wim_path = (tchar*)nonoption;
1503 case WIMLIB_UPDATE_OP_RENAME:
1504 if (num_nonoptions == 0)
1505 cmd->rename.wim_source_path = (tchar*)nonoption;
1507 cmd->rename.wim_target_path = (tchar*)nonoption;
1513 * Parse a command passed on stdin to `imagex update'.
1515 * @line: Text of the command.
1516 * @len: Length of the line, including a null terminator
1519 * @command: A `struct wimlib_update_command' to fill in from the parsed
1522 * @line_number: Line number of the command, for diagnostics.
1524 * Returns true on success; returns false on parse error.
1527 parse_update_command(tchar *line, size_t len,
1528 struct wimlib_update_command *command,
1532 tchar *command_name;
1534 size_t num_nonoptions;
1536 /* Get the command name ("add", "delete", "rename") */
1537 ret = parse_string(&line, &len, &command_name);
1538 if (ret != PARSE_STRING_SUCCESS)
1541 if (!tstrcasecmp(command_name, T("add"))) {
1542 op = WIMLIB_UPDATE_OP_ADD;
1543 } else if (!tstrcasecmp(command_name, T("delete"))) {
1544 op = WIMLIB_UPDATE_OP_DELETE;
1545 } else if (!tstrcasecmp(command_name, T("rename"))) {
1546 op = WIMLIB_UPDATE_OP_RENAME;
1548 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1549 command_name, line_number);
1554 /* Parse additional options and non-options as needed */
1559 ret = parse_string(&line, &len, &next_string);
1560 if (ret == PARSE_STRING_NONE) /* End of line */
1562 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1564 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1566 if (!update_command_add_option(op, next_string, command))
1568 imagex_error(T("Unrecognized option \"%"TS"\" to "
1569 "update command \"%"TS"\" on line %zu"),
1570 next_string, command_name, line_number);
1576 if (num_nonoptions == update_command_num_nonoptions[op])
1578 imagex_error(T("Unexpected argument \"%"TS"\" in "
1579 "update command on line %zu\n"
1580 " (The \"%"TS"\" command only "
1581 "takes %zu nonoption arguments!)\n"),
1582 next_string, line_number,
1583 command_name, num_nonoptions);
1586 update_command_add_nonoption(op, next_string,
1587 command, num_nonoptions);
1592 if (num_nonoptions != update_command_num_nonoptions[op]) {
1593 imagex_error(T("Not enough arguments to update command "
1594 "\"%"TS"\" on line %zu"), command_name, line_number);
1600 static struct wimlib_update_command *
1601 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1602 size_t *num_cmds_ret)
1606 struct wimlib_update_command *cmds;
1609 nlines = text_file_count_lines(cmd_file_contents_p,
1614 /* Always allocate at least 1 slot, just in case the implementation of
1615 * calloc() returns NULL if 0 bytes are requested. */
1616 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1618 imagex_error(T("out of memory"));
1621 p = *cmd_file_contents_p;
1623 for (i = 0; i < nlines; i++) {
1624 /* XXX: Could use rawmemchr() here instead, but it may not be
1625 * available on all platforms. */
1626 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1627 size_t len = endp - p + 1;
1629 if (!is_comment_line(p, len)) {
1630 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1641 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1642 * one image from a WIM file to an NTFS volume. */
1644 imagex_apply(int argc, tchar **argv, int cmd)
1648 int image = WIMLIB_NO_IMAGE;
1650 struct wimlib_wim_info info;
1652 const tchar *wimfile;
1653 const tchar *target;
1654 const tchar *image_num_or_name = NULL;
1655 int extract_flags = 0;
1657 STRING_LIST(refglobs);
1659 for_opt(c, apply_options) {
1661 case IMAGEX_CHECK_OPTION:
1662 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1664 case IMAGEX_VERBOSE_OPTION:
1665 /* No longer does anything. */
1667 case IMAGEX_REF_OPTION:
1668 ret = string_list_append(&refglobs, optarg);
1670 goto out_free_refglobs;
1672 case IMAGEX_UNIX_DATA_OPTION:
1673 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1675 case IMAGEX_NO_ACLS_OPTION:
1676 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1678 case IMAGEX_STRICT_ACLS_OPTION:
1679 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1681 case IMAGEX_NO_ATTRIBUTES_OPTION:
1682 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1684 case IMAGEX_NORPFIX_OPTION:
1685 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1687 case IMAGEX_RPFIX_OPTION:
1688 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1690 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1691 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1692 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1694 case IMAGEX_WIMBOOT_OPTION:
1695 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1697 case IMAGEX_COMPACT_OPTION:
1698 ret = set_compact_mode(optarg, &extract_flags);
1700 goto out_free_refglobs;
1708 if (argc != 2 && argc != 3)
1713 if (!tstrcmp(wimfile, T("-"))) {
1714 /* Attempt to apply pipable WIM from standard input. */
1716 image_num_or_name = NULL;
1719 image_num_or_name = argv[1];
1724 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1725 imagex_progress_func, NULL);
1727 goto out_free_refglobs;
1729 wimlib_get_wim_info(wim, &info);
1732 /* Image explicitly specified. */
1733 image_num_or_name = argv[1];
1734 image = wimlib_resolve_image(wim, image_num_or_name);
1735 ret = verify_image_exists(image, image_num_or_name, wimfile);
1737 goto out_wimlib_free;
1740 /* No image specified; default to image 1, but only if the WIM
1741 * contains exactly one image. */
1743 if (info.image_count != 1) {
1744 imagex_error(T("\"%"TS"\" contains %d images; "
1745 "Please select one (or all)."),
1746 wimfile, info.image_count);
1755 if (refglobs.num_strings) {
1757 imagex_error(T("Can't specify --ref when applying from stdin!"));
1759 goto out_wimlib_free;
1761 ret = wim_reference_globs(wim, &refglobs, open_flags);
1763 goto out_wimlib_free;
1768 /* Interpret a regular file or block device target as an NTFS
1772 if (tstat(target, &stbuf)) {
1773 if (errno != ENOENT) {
1774 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1777 goto out_wimlib_free;
1780 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1781 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1787 ret = wimlib_extract_image(wim, image, target, extract_flags);
1789 set_fd_to_binary_mode(STDIN_FILENO);
1790 ret = wimlib_extract_image_from_pipe_with_progress(
1795 imagex_progress_func,
1799 imagex_printf(T("Done applying WIM image.\n"));
1800 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1802 do_resource_not_found_warning(wimfile, &info, &refglobs);
1804 imagex_error(T( "If you are applying an image "
1805 "from a split pipable WIM,\n"
1806 " make sure you have "
1807 "concatenated together all parts."));
1809 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1810 do_metadata_not_found_warning(wimfile, &info);
1815 string_list_destroy(&refglobs);
1819 usage(CMD_APPLY, stderr);
1821 goto out_free_refglobs;
1824 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1825 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1826 * the desired image. 'wimlib-imagex append': add a new image to an existing
1829 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1833 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1834 WIMLIB_ADD_FLAG_WINCONFIG |
1835 WIMLIB_ADD_FLAG_VERBOSE |
1836 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1837 int write_flags = 0;
1838 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1839 uint32_t chunk_size = UINT32_MAX;
1840 uint32_t solid_chunk_size = UINT32_MAX;
1841 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1842 const tchar *wimfile;
1845 STRING_LIST(image_properties);
1848 STRING_LIST(base_wimfiles);
1849 WIMStruct **base_wims;
1851 WIMStruct *template_wim;
1852 const tchar *template_wimfile = NULL;
1853 const tchar *template_image_name_or_num = NULL;
1854 int template_image = WIMLIB_NO_IMAGE;
1857 unsigned num_threads = 0;
1862 tchar *config_file = NULL;
1864 bool source_list = false;
1865 size_t source_list_nchars = 0;
1866 tchar *source_list_contents;
1867 bool capture_sources_malloced;
1868 struct wimlib_capture_source *capture_sources;
1870 bool name_defaulted;
1872 for_opt(c, capture_or_append_options) {
1874 case IMAGEX_BOOT_OPTION:
1875 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1877 case IMAGEX_CHECK_OPTION:
1878 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1879 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1881 case IMAGEX_NOCHECK_OPTION:
1882 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1884 case IMAGEX_CONFIG_OPTION:
1885 config_file = optarg;
1886 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1888 case IMAGEX_COMPRESS_OPTION:
1889 compression_type = get_compression_type(optarg, false);
1890 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1893 case IMAGEX_CHUNK_SIZE_OPTION:
1894 chunk_size = parse_chunk_size(optarg);
1895 if (chunk_size == UINT32_MAX)
1898 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1899 solid_chunk_size = parse_chunk_size(optarg);
1900 if (solid_chunk_size == UINT32_MAX)
1903 case IMAGEX_SOLID_COMPRESS_OPTION:
1904 solid_ctype = get_compression_type(optarg, true);
1905 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1908 case IMAGEX_SOLID_OPTION:
1909 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1911 case IMAGEX_NO_SOLID_SORT_OPTION:
1912 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1914 case IMAGEX_FLAGS_OPTION: {
1915 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1916 tsprintf(p, T("FLAGS=%"TS), optarg);
1917 ret = string_list_append(&image_properties, p);
1922 case IMAGEX_IMAGE_PROPERTY_OPTION:
1923 ret = append_image_property_argument(&image_properties);
1927 case IMAGEX_DEREFERENCE_OPTION:
1928 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1930 case IMAGEX_VERBOSE_OPTION:
1931 /* No longer does anything. */
1933 case IMAGEX_THREADS_OPTION:
1934 num_threads = parse_num_threads(optarg);
1935 if (num_threads == UINT_MAX)
1938 case IMAGEX_REBUILD_OPTION:
1939 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1941 case IMAGEX_UNIX_DATA_OPTION:
1942 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1944 case IMAGEX_SOURCE_LIST_OPTION:
1947 case IMAGEX_NO_ACLS_OPTION:
1948 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1950 case IMAGEX_STRICT_ACLS_OPTION:
1951 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1953 case IMAGEX_RPFIX_OPTION:
1954 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1956 case IMAGEX_NORPFIX_OPTION:
1957 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1959 case IMAGEX_PIPABLE_OPTION:
1960 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1962 case IMAGEX_NOT_PIPABLE_OPTION:
1963 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1965 case IMAGEX_UPDATE_OF_OPTION:
1966 if (template_image_name_or_num) {
1967 imagex_error(T("'--update-of' can only be "
1968 "specified one time!"));
1972 colon = tstrrchr(optarg, T(':'));
1975 template_wimfile = optarg;
1977 template_image_name_or_num = colon + 1;
1979 template_wimfile = NULL;
1980 template_image_name_or_num = optarg;
1984 case IMAGEX_DELTA_FROM_OPTION:
1985 if (cmd != CMD_CAPTURE) {
1986 imagex_error(T("'--delta-from' is only "
1987 "valid for capture!"));
1990 ret = string_list_append(&base_wimfiles, optarg);
1993 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
1995 case IMAGEX_WIMBOOT_OPTION:
1996 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
1998 case IMAGEX_UNSAFE_COMPACT_OPTION:
1999 if (cmd != CMD_APPEND) {
2000 imagex_error(T("'--unsafe-compact' is only "
2001 "valid for append!"));
2004 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2006 case IMAGEX_SNAPSHOT_OPTION:
2007 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2016 if (argc < 2 || argc > 4)
2022 /* Set default compression type and parameters. */
2025 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2026 /* No compression type specified. Use the default. */
2028 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2029 /* With --wimboot, default to XPRESS compression. */
2030 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2031 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2032 /* With --solid, default to LZMS compression. (However,
2033 * this will not affect solid resources!) */
2034 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2036 /* Otherwise, default to LZX compression. */
2037 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2041 if (!tstrcmp(wimfile, T("-"))) {
2042 /* Writing captured WIM to standard output. */
2044 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2045 imagex_error("Can't write a non-pipable WIM to "
2046 "standard output! Specify --pipable\n"
2047 " if you want to create a pipable WIM "
2048 "(but read the docs first).");
2052 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2054 if (cmd == CMD_APPEND) {
2055 imagex_error(T("Using standard output for append does "
2056 "not make sense."));
2059 wim_fd = STDOUT_FILENO;
2061 imagex_info_file = stderr;
2062 set_fd_to_binary_mode(wim_fd);
2065 /* If template image was specified using --update-of=IMAGE rather
2066 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2067 if (template_image_name_or_num && !template_wimfile) {
2068 if (base_wimfiles.num_strings == 1) {
2069 /* Capturing delta WIM based on single WIM: default to
2071 template_wimfile = base_wimfiles.strings[0];
2072 } else if (cmd == CMD_APPEND) {
2073 /* Appending to WIM: default to WIM being appended to.
2075 template_wimfile = wimfile;
2077 /* Capturing a normal (non-delta) WIM, so the WIM file
2078 * *must* be explicitly specified. */
2079 if (base_wimfiles.num_strings > 1) {
2080 imagex_error(T("For capture of delta WIM "
2081 "based on multiple existing "
2083 " '--update-of' must "
2084 "specify WIMFILE:IMAGE!"));
2086 imagex_error(T("For capture of non-delta WIM, "
2087 "'--update-of' must specify "
2096 name_defaulted = false;
2098 /* Set default name to SOURCE argument, omitting any directory
2099 * prefixes and trailing slashes. This requires making a copy
2100 * of @source. Leave some free characters at the end in case we
2101 * append a number to keep the name unique. */
2102 size_t source_name_len;
2104 source_name_len = tstrlen(source);
2105 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2106 name = tbasename(tstrcpy(source_copy, source));
2107 name_defaulted = true;
2110 /* Image description (if given). */
2112 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2113 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2114 ret = string_list_append(&image_properties, p);
2120 /* Set up capture sources in source list mode */
2121 if (source[0] == T('-') && source[1] == T('\0')) {
2122 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2124 source_list_contents = file_get_text_contents(source,
2125 &source_list_nchars);
2127 if (!source_list_contents)
2130 capture_sources = parse_source_list(&source_list_contents,
2133 if (!capture_sources) {
2135 goto out_free_source_list_contents;
2137 capture_sources_malloced = true;
2139 /* Set up capture source in non-source-list mode. */
2140 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2141 capture_sources[0].fs_source_path = source;
2142 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2143 capture_sources[0].reserved = 0;
2145 capture_sources_malloced = false;
2146 source_list_contents = NULL;
2149 /* Open the existing WIM, or create a new one. */
2150 if (cmd == CMD_APPEND) {
2151 ret = wimlib_open_wim_with_progress(wimfile,
2152 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2154 imagex_progress_func,
2157 goto out_free_capture_sources;
2159 ret = wimlib_create_new_wim(compression_type, &wim);
2161 goto out_free_capture_sources;
2162 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2165 /* Set chunk size if non-default. */
2166 if (chunk_size != UINT32_MAX) {
2167 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2170 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2172 int ctype = compression_type;
2174 if (cmd == CMD_APPEND) {
2175 struct wimlib_wim_info info;
2176 wimlib_get_wim_info(wim, &info);
2177 ctype = info.compression_type;
2180 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2181 ret = wimlib_set_output_chunk_size(wim, 4096);
2186 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2187 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2191 if (solid_chunk_size != UINT32_MAX) {
2192 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2198 /* Detect if source is regular file or block device and set NTFS volume
2203 if (tstat(source, &stbuf) == 0) {
2204 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2205 imagex_printf(T("Capturing WIM image from NTFS "
2206 "filesystem on \"%"TS"\"\n"), source);
2207 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2210 if (errno != ENOENT) {
2211 imagex_error_with_errno(T("Failed to stat "
2212 "\"%"TS"\""), source);
2220 /* If the user did not specify an image name, and the basename of the
2221 * source already exists as an image name in the WIM file, append a
2222 * suffix to make it unique. */
2223 if (cmd == CMD_APPEND && name_defaulted) {
2224 unsigned long conflict_idx;
2225 tchar *name_end = tstrchr(name, T('\0'));
2226 for (conflict_idx = 1;
2227 wimlib_image_name_in_use(wim, name);
2230 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2234 /* If capturing a delta WIM, reference resources from the base WIMs
2235 * before adding the new image. */
2236 if (base_wimfiles.num_strings) {
2237 base_wims = calloc(base_wimfiles.num_strings,
2238 sizeof(base_wims[0]));
2239 if (base_wims == NULL) {
2240 imagex_error(T("Out of memory!"));
2245 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2246 ret = wimlib_open_wim_with_progress(
2247 base_wimfiles.strings[i], open_flags,
2248 &base_wims[i], imagex_progress_func, NULL);
2250 goto out_free_base_wims;
2254 ret = wimlib_reference_resources(wim, base_wims,
2255 base_wimfiles.num_strings, 0);
2257 goto out_free_base_wims;
2259 if (base_wimfiles.num_strings == 1) {
2260 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2261 base_wimfiles.strings[0]);
2263 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2264 base_wimfiles.num_strings);
2271 /* If capturing or appending as an update of an existing (template) image,
2272 * open the WIM if needed and parse the image index. */
2273 if (template_image_name_or_num) {
2276 if (base_wimfiles.num_strings == 1 &&
2277 template_wimfile == base_wimfiles.strings[0]) {
2278 template_wim = base_wims[0];
2279 } else if (template_wimfile == wimfile) {
2282 ret = wimlib_open_wim_with_progress(template_wimfile,
2285 imagex_progress_func,
2288 goto out_free_base_wims;
2291 template_image = wimlib_resolve_image(template_wim,
2292 template_image_name_or_num);
2294 if (template_image_name_or_num[0] == T('-')) {
2297 struct wimlib_wim_info info;
2299 wimlib_get_wim_info(template_wim, &info);
2300 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2301 if (n >= 1 && n <= info.image_count &&
2303 tmp != template_image_name_or_num + 1)
2305 template_image = info.image_count - (n - 1);
2308 ret = verify_image_exists_and_is_single(template_image,
2309 template_image_name_or_num,
2312 goto out_free_template_wim;
2314 template_wim = NULL;
2317 ret = wimlib_add_image_multisource(wim,
2324 goto out_free_template_wim;
2326 if (image_properties.num_strings || template_image_name_or_num) {
2327 /* User asked to set additional image properties, or an image on
2328 * which the added one is to be based has been specified with
2330 struct wimlib_wim_info info;
2332 wimlib_get_wim_info(wim, &info);
2334 ret = apply_image_properties(&image_properties, wim,
2335 info.image_count, NULL);
2337 goto out_free_template_wim;
2339 /* Reference template image if the user provided one. */
2340 if (template_image_name_or_num) {
2341 imagex_printf(T("Using image %d "
2342 "from \"%"TS"\" as template\n"),
2343 template_image, template_wimfile);
2344 ret = wimlib_reference_template_image(wim,
2350 goto out_free_template_wim;
2354 /* Write the new WIM or overwrite the existing WIM with the new image
2356 if (cmd == CMD_APPEND) {
2357 ret = wimlib_overwrite(wim, write_flags, num_threads);
2358 } else if (wimfile) {
2359 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2360 write_flags, num_threads);
2362 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2363 write_flags, num_threads);
2365 out_free_template_wim:
2366 /* template_wim may alias base_wims[0] or wim. */
2367 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2368 template_wim != wim)
2369 wimlib_free(template_wim);
2371 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2372 wimlib_free(base_wims[i]);
2376 out_free_capture_sources:
2377 if (capture_sources_malloced)
2378 free(capture_sources);
2379 out_free_source_list_contents:
2380 free(source_list_contents);
2382 string_list_destroy(&image_properties);
2383 string_list_destroy(&base_wimfiles);
2393 /* Remove image(s) from a WIM. */
2395 imagex_delete(int argc, tchar **argv, int cmd)
2398 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2399 int write_flags = 0;
2400 const tchar *wimfile;
2401 const tchar *image_num_or_name;
2406 for_opt(c, delete_options) {
2408 case IMAGEX_CHECK_OPTION:
2409 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2410 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2412 case IMAGEX_SOFT_OPTION:
2413 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2415 case IMAGEX_UNSAFE_COMPACT_OPTION:
2416 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2427 imagex_error(T("Must specify a WIM file"));
2429 imagex_error(T("Must specify an image"));
2433 image_num_or_name = argv[1];
2435 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2436 imagex_progress_func, NULL);
2440 image = wimlib_resolve_image(wim, image_num_or_name);
2442 ret = verify_image_exists(image, image_num_or_name, wimfile);
2444 goto out_wimlib_free;
2446 ret = wimlib_delete_image(wim, image);
2448 imagex_error(T("Failed to delete image from \"%"TS"\""),
2450 goto out_wimlib_free;
2453 ret = wimlib_overwrite(wim, write_flags, 0);
2455 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2456 "deleted"), wimfile);
2464 usage(CMD_DELETE, stderr);
2469 struct print_dentry_options {
2474 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2476 tprintf(T("%"TS"\n"), dentry->full_path);
2479 static const struct {
2482 } file_attr_flags[] = {
2483 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2484 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2485 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2486 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2487 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2488 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2489 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2490 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2491 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2492 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2493 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2494 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2495 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2496 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2497 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2500 #define TIMESTR_MAX 100
2503 timespec_to_string(const struct timespec *spec, tchar *buf)
2505 time_t t = spec->tv_sec;
2508 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2509 buf[TIMESTR_MAX - 1] = '\0';
2513 print_time(const tchar *type, const struct timespec *spec)
2515 tchar timestr[TIMESTR_MAX];
2517 timespec_to_string(spec, timestr);
2519 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2522 static void print_byte_field(const uint8_t field[], size_t len)
2525 tprintf(T("%02hhx"), *field++);
2529 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2531 tchar attr_string[256];
2534 tputs(T("WIM Information:"));
2535 tputs(T("----------------"));
2536 tprintf(T("Path: %"TS"\n"), wimfile);
2537 tprintf(T("GUID: 0x"));
2538 print_byte_field(info->guid, sizeof(info->guid));
2540 tprintf(T("Version: %u\n"), info->wim_version);
2541 tprintf(T("Image Count: %d\n"), info->image_count);
2542 tprintf(T("Compression: %"TS"\n"),
2543 wimlib_get_compression_type_string(info->compression_type));
2544 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2546 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2547 tprintf(T("Boot Index: %d\n"), info->boot_index);
2548 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2550 attr_string[0] = T('\0');
2553 tstrcat(attr_string, T("Pipable, "));
2555 if (info->has_integrity_table)
2556 tstrcat(attr_string, T("Integrity info, "));
2558 if (info->has_rpfix)
2559 tstrcat(attr_string, T("Relative path junction, "));
2561 if (info->resource_only)
2562 tstrcat(attr_string, T("Resource only, "));
2564 if (info->metadata_only)
2565 tstrcat(attr_string, T("Metadata only, "));
2567 if (info->is_marked_readonly)
2568 tstrcat(attr_string, T("Readonly, "));
2570 p = tstrchr(attr_string, T('\0'));
2571 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2574 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2578 print_resource(const struct wimlib_resource_entry *resource,
2581 tprintf(T("Hash = 0x"));
2582 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2585 if (!resource->is_missing) {
2586 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2587 resource->uncompressed_size);
2588 if (resource->packed) {
2589 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2590 "bytes @ offset %"PRIu64"\n"),
2591 resource->raw_resource_uncompressed_size,
2592 resource->raw_resource_compressed_size,
2593 resource->raw_resource_offset_in_wim);
2595 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2598 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2599 resource->compressed_size);
2601 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2605 tprintf(T("Part Number = %u\n"), resource->part_number);
2606 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2608 tprintf(T("Flags = "));
2609 if (resource->is_compressed)
2610 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2611 if (resource->is_metadata)
2612 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2613 if (resource->is_free)
2614 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2615 if (resource->is_spanned)
2616 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2617 if (resource->packed)
2618 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2626 print_blobs(WIMStruct *wim)
2628 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2633 default_print_security_descriptor(const uint8_t *sd, size_t size)
2635 tprintf(T("Security Descriptor = "));
2636 print_byte_field(sd, size);
2642 is_null_guid(const uint8_t *guid)
2644 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2646 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2650 print_guid(const tchar *label, const uint8_t *guid)
2652 if (is_null_guid(guid))
2654 tprintf(T("%-20"TS"= 0x"), label);
2655 print_byte_field(guid, WIMLIB_GUID_LEN);
2660 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2663 "----------------------------------------------------------------------------\n"));
2664 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2665 if (dentry->dos_name)
2666 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2667 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2668 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2669 if (file_attr_flags[i].flag & dentry->attributes)
2670 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2671 file_attr_flags[i].name);
2673 if (dentry->security_descriptor) {
2674 print_security_descriptor(dentry->security_descriptor,
2675 dentry->security_descriptor_size);
2678 print_time(T("Creation Time"), &dentry->creation_time);
2679 print_time(T("Last Write Time"), &dentry->last_write_time);
2680 print_time(T("Last Access Time"), &dentry->last_access_time);
2683 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2684 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2686 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2687 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2689 if (dentry->unix_mode != 0) {
2690 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2691 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2692 dentry->unix_uid, dentry->unix_gid,
2693 dentry->unix_mode, dentry->unix_rdev);
2696 if (!is_null_guid(dentry->object_id.object_id)) {
2697 print_guid(T("Object ID"), dentry->object_id.object_id);
2698 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2699 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2700 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2703 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2704 if (dentry->streams[i].stream_name) {
2705 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2706 dentry->streams[i].stream_name);
2707 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2708 tprintf(T("\tRaw encrypted data stream:\n"));
2709 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2710 tprintf(T("\tReparse point stream:\n"));
2712 tprintf(T("\tUnnamed data stream:\n"));
2714 print_resource(&dentry->streams[i].resource, NULL);
2719 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2721 const struct print_dentry_options *options = _options;
2722 if (!options->detailed)
2723 print_dentry_full_path(dentry);
2725 print_dentry_detailed(dentry);
2729 /* Print the files contained in an image(s) in a WIM file. */
2731 imagex_dir(int argc, tchar **argv, int cmd)
2733 const tchar *wimfile;
2734 WIMStruct *wim = NULL;
2737 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2739 struct print_dentry_options options = {
2742 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2744 STRING_LIST(refglobs);
2746 for_opt(c, dir_options) {
2748 case IMAGEX_PATH_OPTION:
2751 case IMAGEX_DETAILED_OPTION:
2752 options.detailed = true;
2754 case IMAGEX_ONE_FILE_ONLY_OPTION:
2755 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2757 case IMAGEX_REF_OPTION:
2758 ret = string_list_append(&refglobs, optarg);
2760 goto out_free_refglobs;
2770 imagex_error(T("Must specify a WIM file"));
2774 imagex_error(T("Too many arguments"));
2779 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2780 imagex_progress_func, NULL);
2782 goto out_free_refglobs;
2785 image = wimlib_resolve_image(wim, argv[1]);
2786 ret = verify_image_exists(image, argv[1], wimfile);
2788 goto out_wimlib_free;
2790 /* No image specified; default to image 1, but only if the WIM
2791 * contains exactly one image. */
2793 struct wimlib_wim_info info;
2795 wimlib_get_wim_info(wim, &info);
2796 if (info.image_count != 1) {
2797 imagex_error(T("\"%"TS"\" contains %d images; Please "
2798 "select one (or all)."),
2799 wimfile, info.image_count);
2806 if (refglobs.num_strings) {
2807 ret = wim_reference_globs(wim, &refglobs, 0);
2809 goto out_wimlib_free;
2812 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2813 print_dentry, &options);
2814 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2815 struct wimlib_wim_info info;
2817 wimlib_get_wim_info(wim, &info);
2818 do_metadata_not_found_warning(wimfile, &info);
2823 string_list_destroy(&refglobs);
2827 usage(CMD_DIR, stderr);
2829 goto out_free_refglobs;
2832 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2835 imagex_export(int argc, tchar **argv, int cmd)
2839 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2840 int write_flags = 0;
2841 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2842 const tchar *src_wimfile;
2843 const tchar *src_image_num_or_name;
2844 const tchar *dest_wimfile;
2846 const tchar *dest_name;
2847 const tchar *dest_desc;
2849 struct wimlib_wim_info src_info;
2850 WIMStruct *dest_wim;
2855 STRING_LIST(refglobs);
2856 unsigned num_threads = 0;
2857 uint32_t chunk_size = UINT32_MAX;
2858 uint32_t solid_chunk_size = UINT32_MAX;
2859 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2861 for_opt(c, export_options) {
2863 case IMAGEX_BOOT_OPTION:
2864 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2866 case IMAGEX_CHECK_OPTION:
2867 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2868 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2870 case IMAGEX_NOCHECK_OPTION:
2871 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2873 case IMAGEX_COMPRESS_OPTION:
2874 compression_type = get_compression_type(optarg, false);
2875 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2878 case IMAGEX_RECOMPRESS_OPTION:
2879 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2881 case IMAGEX_SOLID_OPTION:
2882 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2884 case IMAGEX_NO_SOLID_SORT_OPTION:
2885 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2887 case IMAGEX_CHUNK_SIZE_OPTION:
2888 chunk_size = parse_chunk_size(optarg);
2889 if (chunk_size == UINT32_MAX)
2892 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2893 solid_chunk_size = parse_chunk_size(optarg);
2894 if (solid_chunk_size == UINT32_MAX)
2897 case IMAGEX_SOLID_COMPRESS_OPTION:
2898 solid_ctype = get_compression_type(optarg, true);
2899 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2902 case IMAGEX_REF_OPTION:
2903 ret = string_list_append(&refglobs, optarg);
2905 goto out_free_refglobs;
2907 case IMAGEX_THREADS_OPTION:
2908 num_threads = parse_num_threads(optarg);
2909 if (num_threads == UINT_MAX)
2912 case IMAGEX_REBUILD_OPTION:
2913 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2915 case IMAGEX_PIPABLE_OPTION:
2916 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2918 case IMAGEX_NOT_PIPABLE_OPTION:
2919 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2921 case IMAGEX_WIMBOOT_OPTION:
2922 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2924 case IMAGEX_UNSAFE_COMPACT_OPTION:
2925 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2933 if (argc < 3 || argc > 5)
2935 src_wimfile = argv[0];
2936 src_image_num_or_name = argv[1];
2937 dest_wimfile = argv[2];
2938 dest_name = (argc >= 4) ? argv[3] : NULL;
2939 dest_desc = (argc >= 5) ? argv[4] : NULL;
2940 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2941 imagex_progress_func, NULL);
2943 goto out_free_refglobs;
2945 wimlib_get_wim_info(src_wim, &src_info);
2947 /* Determine if the destination is an existing file or not. If so, we
2948 * try to append the exported image(s) to it; otherwise, we create a new
2949 * WIM containing the exported image(s). Furthermore, determine if we
2950 * need to write a pipable WIM directly to standard output. */
2952 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2954 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2955 imagex_error("Can't write a non-pipable WIM to "
2956 "standard output! Specify --pipable\n"
2957 " if you want to create a pipable WIM "
2958 "(but read the docs first).");
2960 goto out_free_src_wim;
2963 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2965 dest_wimfile = NULL;
2966 dest_wim_fd = STDOUT_FILENO;
2967 imagex_info_file = stderr;
2968 set_fd_to_binary_mode(dest_wim_fd);
2971 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2973 /* Destination file exists. */
2975 if (!S_ISREG(stbuf.st_mode)) {
2976 imagex_error(T("\"%"TS"\" is not a regular file"),
2979 goto out_free_src_wim;
2981 ret = wimlib_open_wim_with_progress(dest_wimfile,
2983 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2985 imagex_progress_func,
2988 goto out_free_src_wim;
2990 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2991 /* The user specified a compression type, but we're
2992 * exporting to an existing WIM. Make sure the
2993 * specified compression type is the same as the
2994 * compression type of the existing destination WIM. */
2995 struct wimlib_wim_info dest_info;
2997 wimlib_get_wim_info(dest_wim, &dest_info);
2998 if (compression_type != dest_info.compression_type) {
2999 imagex_error(T("Cannot specify a compression type that is "
3000 "not the same as that used in the "
3001 "destination WIM"));
3003 goto out_free_dest_wim;
3009 if (errno != ENOENT) {
3010 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3013 goto out_free_src_wim;
3016 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3017 imagex_error(T("'--unsafe-compact' is only valid when "
3018 "exporting to an existing WIM file!"));
3020 goto out_free_src_wim;
3023 /* dest_wimfile is not an existing file, so create a new WIM. */
3025 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3026 /* The user did not specify a compression type; default
3027 * to that of the source WIM, unless --solid or
3028 * --wimboot was specified. */
3030 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3031 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3032 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3033 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3035 compression_type = src_info.compression_type;
3037 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3039 goto out_free_src_wim;
3041 wimlib_register_progress_function(dest_wim,
3042 imagex_progress_func, NULL);
3044 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3045 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3047 /* For --wimboot export, use small XPRESS chunks. */
3048 wimlib_set_output_chunk_size(dest_wim, 4096);
3049 } else if (compression_type == src_info.compression_type &&
3050 chunk_size == UINT32_MAX)
3052 /* Use same chunk size if compression type is the same. */
3053 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3057 if (chunk_size != UINT32_MAX) {
3058 /* Set destination chunk size. */
3059 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3061 goto out_free_dest_wim;
3063 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3064 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3066 goto out_free_dest_wim;
3068 if (solid_chunk_size != UINT32_MAX) {
3069 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3071 goto out_free_dest_wim;
3074 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3075 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3077 goto out_free_dest_wim;
3079 if (refglobs.num_strings) {
3080 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3082 goto out_free_dest_wim;
3085 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3086 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3088 imagex_error(T("--boot specified for all-images export, but source WIM "
3089 "has no bootable image."));
3091 goto out_free_dest_wim;
3094 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3095 dest_desc, export_flags);
3097 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3098 do_resource_not_found_warning(src_wimfile,
3099 &src_info, &refglobs);
3100 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3101 do_metadata_not_found_warning(src_wimfile, &src_info);
3103 goto out_free_dest_wim;
3107 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3108 else if (dest_wimfile)
3109 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3110 write_flags, num_threads);
3112 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3113 WIMLIB_ALL_IMAGES, write_flags,
3116 wimlib_free(dest_wim);
3118 wimlib_free(src_wim);
3120 string_list_destroy(&refglobs);
3124 usage(CMD_EXPORT, stderr);
3127 goto out_free_refglobs;
3130 /* Extract files or directories from a WIM image */
3132 imagex_extract(int argc, tchar **argv, int cmd)
3139 const tchar *wimfile;
3140 const tchar *image_num_or_name;
3141 tchar *dest_dir = T(".");
3142 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3143 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3144 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3145 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3147 STRING_LIST(refglobs);
3149 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3151 for_opt(c, extract_options) {
3153 case IMAGEX_CHECK_OPTION:
3154 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3156 case IMAGEX_VERBOSE_OPTION:
3157 /* No longer does anything. */
3159 case IMAGEX_REF_OPTION:
3160 ret = string_list_append(&refglobs, optarg);
3162 goto out_free_refglobs;
3164 case IMAGEX_UNIX_DATA_OPTION:
3165 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3167 case IMAGEX_NO_ACLS_OPTION:
3168 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3170 case IMAGEX_STRICT_ACLS_OPTION:
3171 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3173 case IMAGEX_NO_ATTRIBUTES_OPTION:
3174 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3176 case IMAGEX_DEST_DIR_OPTION:
3179 case IMAGEX_TO_STDOUT_OPTION:
3180 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3181 imagex_info_file = stderr;
3182 imagex_be_quiet = true;
3183 set_fd_to_binary_mode(STDOUT_FILENO);
3185 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3186 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3187 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3189 case IMAGEX_NO_GLOBS_OPTION:
3190 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3192 case IMAGEX_NULLGLOB_OPTION:
3193 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3195 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3196 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3198 case IMAGEX_WIMBOOT_OPTION:
3199 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3201 case IMAGEX_COMPACT_OPTION:
3202 ret = set_compact_mode(optarg, &extract_flags);
3204 goto out_free_refglobs;
3216 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3217 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3219 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3224 image_num_or_name = argv[1];
3229 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3230 imagex_progress_func, NULL);
3232 goto out_free_refglobs;
3234 image = wimlib_resolve_image(wim, image_num_or_name);
3235 ret = verify_image_exists_and_is_single(image,
3239 goto out_wimlib_free;
3241 if (refglobs.num_strings) {
3242 ret = wim_reference_globs(wim, &refglobs, open_flags);
3244 goto out_wimlib_free;
3250 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3253 while (argc != 0 && ret == 0) {
3257 num_paths < argc && argv[num_paths][0] != T('@');
3262 ret = wimlib_extract_paths(wim, image, dest_dir,
3263 (const tchar **)argv,
3265 extract_flags | notlist_extract_flags);
3269 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3278 if (!imagex_be_quiet)
3279 imagex_printf(T("Done extracting files.\n"));
3280 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3281 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3282 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3283 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3284 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3287 T("Note: You can use the '--nullglob' "
3288 "option to ignore missing files.\n"));
3290 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3291 "files and directories\n"
3292 " are in the WIM image.\n"),
3293 get_cmd_string(CMD_DIR, false));
3294 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3295 struct wimlib_wim_info info;
3297 wimlib_get_wim_info(wim, &info);
3298 do_resource_not_found_warning(wimfile, &info, &refglobs);
3299 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3300 struct wimlib_wim_info info;
3302 wimlib_get_wim_info(wim, &info);
3303 do_metadata_not_found_warning(wimfile, &info);
3308 string_list_destroy(&refglobs);
3312 usage(CMD_EXTRACT, stderr);
3315 goto out_free_refglobs;
3318 /* Prints information about a WIM file; also can mark an image as bootable,
3319 * change the name of an image, or change the description of an image. */
3321 imagex_info(int argc, tchar **argv, int cmd)
3326 bool nocheck = false;
3327 bool header = false;
3330 bool short_header = true;
3331 const tchar *xml_out_file = NULL;
3332 const tchar *wimfile;
3333 const tchar *image_num_or_name;
3334 STRING_LIST(image_properties);
3339 struct wimlib_wim_info info;
3341 for_opt(c, info_options) {
3343 case IMAGEX_BOOT_OPTION:
3346 case IMAGEX_CHECK_OPTION:
3349 case IMAGEX_NOCHECK_OPTION:
3352 case IMAGEX_HEADER_OPTION:
3354 short_header = false;
3356 case IMAGEX_BLOBS_OPTION:
3358 short_header = false;
3360 case IMAGEX_XML_OPTION:
3362 short_header = false;
3364 case IMAGEX_EXTRACT_XML_OPTION:
3365 xml_out_file = optarg;
3366 short_header = false;
3368 case IMAGEX_IMAGE_PROPERTY_OPTION:
3369 ret = append_image_property_argument(&image_properties);
3380 if (argc < 1 || argc > 4)
3384 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3388 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3389 tsprintf(p, T("NAME=%"TS), argv[2]);
3390 ret = string_list_append(&image_properties, p);
3397 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3398 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3399 ret = string_list_append(&image_properties, p);
3404 if (check && nocheck) {
3405 imagex_error(T("Can't specify both --check and --nocheck"));
3410 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3412 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3413 imagex_progress_func, NULL);
3417 wimlib_get_wim_info(wim, &info);
3419 image = wimlib_resolve_image(wim, image_num_or_name);
3420 ret = WIMLIB_ERR_INVALID_IMAGE;
3421 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3422 verify_image_exists(image, image_num_or_name, wimfile);
3424 imagex_error(T("If you would like to set the boot "
3425 "index to 0, specify image \"0\" with "
3426 "the --boot flag."));
3428 goto out_wimlib_free;
3431 if (boot && info.image_count == 0) {
3432 imagex_error(T("--boot is meaningless on a WIM with no images"));
3433 goto out_wimlib_free;
3436 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3438 imagex_error(T("Cannot specify the --boot flag "
3439 "without specifying a specific "
3440 "image in a multi-image WIM"));
3441 goto out_wimlib_free;
3443 if (image_properties.num_strings) {
3444 imagex_error(T("Can't change image properties without "
3445 "specifying a specific image in a "
3446 "multi-image WIM"));
3447 goto out_wimlib_free;
3451 /* Operations that print information are separated from operations that
3452 * recreate the WIM file. */
3453 if (!image_properties.num_strings && !boot) {
3455 /* Read-only operations */
3457 if (image == WIMLIB_NO_IMAGE) {
3458 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3459 image_num_or_name, wimfile);
3460 goto out_wimlib_free;
3463 if (image == WIMLIB_ALL_IMAGES && short_header)
3464 print_wim_information(wimfile, &info);
3467 wimlib_print_header(wim);
3470 if (info.total_parts != 1) {
3471 tfprintf(stderr, T("Warning: Only showing the blobs "
3472 "for part %d of a %d-part WIM.\n"),
3473 info.part_number, info.total_parts);
3479 ret = wimlib_extract_xml_data(wim, stdout);
3481 goto out_wimlib_free;
3487 fp = tfopen(xml_out_file, T("wb"));
3489 imagex_error_with_errno(T("Failed to open the "
3490 "file \"%"TS"\" for "
3494 goto out_wimlib_free;
3496 ret = wimlib_extract_xml_data(wim, fp);
3498 imagex_error(T("Failed to close the file "
3504 goto out_wimlib_free;
3508 wimlib_print_available_images(wim, image);
3512 /* Modification operations */
3513 bool any_property_changes;
3515 if (image == WIMLIB_ALL_IMAGES)
3518 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3519 imagex_error(T("Cannot change image properties "
3520 "when using image 0"));
3522 goto out_wimlib_free;
3526 if (image == info.boot_index) {
3527 imagex_printf(T("Image %d is already marked as "
3528 "bootable.\n"), image);
3531 imagex_printf(T("Marking image %d as bootable.\n"),
3533 info.boot_index = image;
3534 ret = wimlib_set_wim_info(wim, &info,
3535 WIMLIB_CHANGE_BOOT_INDEX);
3537 goto out_wimlib_free;
3541 ret = apply_image_properties(&image_properties, wim, image,
3542 &any_property_changes);
3544 goto out_wimlib_free;
3546 /* Only call wimlib_overwrite() if something actually needs to
3548 if (boot || any_property_changes ||
3549 (check && !info.has_integrity_table) ||
3550 (nocheck && info.has_integrity_table))
3552 int write_flags = 0;
3555 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3557 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3558 ret = wimlib_overwrite(wim, write_flags, 1);
3560 imagex_printf(T("The file \"%"TS"\" was not modified "
3561 "because nothing needed to be done.\n"),
3569 string_list_destroy(&image_properties);
3573 usage(CMD_INFO, stderr);
3579 /* Join split WIMs into one part WIM */
3581 imagex_join(int argc, tchar **argv, int cmd)
3584 int swm_open_flags = 0;
3585 int wim_write_flags = 0;
3586 const tchar *output_path;
3589 for_opt(c, join_options) {
3591 case IMAGEX_CHECK_OPTION:
3592 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3593 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3603 imagex_error(T("Must specify one or more split WIM (.swm) "
3607 output_path = argv[0];
3608 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3613 imagex_progress_func,
3619 usage(CMD_JOIN, stderr);
3624 #if WIM_MOUNTING_SUPPORTED
3626 /* Mounts a WIM image. */
3628 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3631 int mount_flags = 0;
3633 const tchar *staging_dir = NULL;
3634 const tchar *wimfile;
3637 struct wimlib_wim_info info;
3641 STRING_LIST(refglobs);
3643 if (cmd == CMD_MOUNTRW) {
3644 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3645 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3648 for_opt(c, mount_options) {
3650 case IMAGEX_ALLOW_OTHER_OPTION:
3651 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3653 case IMAGEX_CHECK_OPTION:
3654 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3656 case IMAGEX_DEBUG_OPTION:
3657 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3659 case IMAGEX_STREAMS_INTERFACE_OPTION:
3660 if (!tstrcasecmp(optarg, T("none")))
3661 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3662 else if (!tstrcasecmp(optarg, T("xattr")))
3663 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3664 else if (!tstrcasecmp(optarg, T("windows")))
3665 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3667 imagex_error(T("Unknown stream interface \"%"TS"\""),
3672 case IMAGEX_REF_OPTION:
3673 ret = string_list_append(&refglobs, optarg);
3675 goto out_free_refglobs;
3677 case IMAGEX_STAGING_DIR_OPTION:
3678 staging_dir = optarg;
3680 case IMAGEX_UNIX_DATA_OPTION:
3681 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3689 if (argc != 2 && argc != 3)
3694 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3695 imagex_progress_func, NULL);
3697 goto out_free_refglobs;
3699 wimlib_get_wim_info(wim, &info);
3702 /* Image explicitly specified. */
3703 image = wimlib_resolve_image(wim, argv[1]);
3705 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3709 /* No image specified; default to image 1, but only if the WIM
3710 * contains exactly one image. */
3712 if (info.image_count != 1) {
3713 imagex_error(T("\"%"TS"\" contains %d images; Please "
3714 "select one."), wimfile, info.image_count);
3722 if (refglobs.num_strings) {
3723 ret = wim_reference_globs(wim, &refglobs, open_flags);
3728 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3730 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3731 do_metadata_not_found_warning(wimfile, &info);
3733 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3735 image, wimfile, dir);
3741 string_list_destroy(&refglobs);
3747 goto out_free_refglobs;
3749 #endif /* WIM_MOUNTING_SUPPORTED */
3751 /* Rebuild a WIM file */
3753 imagex_optimize(int argc, tchar **argv, int cmd)
3756 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3757 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3758 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3759 uint32_t chunk_size = UINT32_MAX;
3760 uint32_t solid_chunk_size = UINT32_MAX;
3761 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3764 const tchar *wimfile;
3767 unsigned num_threads = 0;
3769 for_opt(c, optimize_options) {
3771 case IMAGEX_CHECK_OPTION:
3772 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3773 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3775 case IMAGEX_NOCHECK_OPTION:
3776 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3778 case IMAGEX_COMPRESS_OPTION:
3779 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3780 compression_type = get_compression_type(optarg, false);
3781 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3784 case IMAGEX_RECOMPRESS_OPTION:
3785 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3787 case IMAGEX_CHUNK_SIZE_OPTION:
3788 chunk_size = parse_chunk_size(optarg);
3789 if (chunk_size == UINT32_MAX)
3792 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3793 solid_chunk_size = parse_chunk_size(optarg);
3794 if (solid_chunk_size == UINT32_MAX)
3797 case IMAGEX_SOLID_COMPRESS_OPTION:
3798 solid_ctype = get_compression_type(optarg, true);
3799 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3802 case IMAGEX_SOLID_OPTION:
3803 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3804 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3806 case IMAGEX_NO_SOLID_SORT_OPTION:
3807 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3809 case IMAGEX_THREADS_OPTION:
3810 num_threads = parse_num_threads(optarg);
3811 if (num_threads == UINT_MAX)
3814 case IMAGEX_PIPABLE_OPTION:
3815 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3817 case IMAGEX_NOT_PIPABLE_OPTION:
3818 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3820 case IMAGEX_UNSAFE_COMPACT_OPTION:
3821 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3835 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3836 imagex_progress_func, NULL);
3840 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3841 /* Change compression type. */
3842 ret = wimlib_set_output_compression_type(wim, compression_type);
3844 goto out_wimlib_free;
3847 if (chunk_size != UINT32_MAX) {
3848 /* Change chunk size. */
3849 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3851 goto out_wimlib_free;
3853 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3854 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3856 goto out_wimlib_free;
3858 if (solid_chunk_size != UINT32_MAX) {
3859 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3861 goto out_wimlib_free;
3864 old_size = file_get_size(wimfile);
3865 tprintf(T("\"%"TS"\" original size: "), wimfile);
3867 tputs(T("Unknown"));
3869 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3871 ret = wimlib_overwrite(wim, write_flags, num_threads);
3873 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3874 goto out_wimlib_free;
3877 new_size = file_get_size(wimfile);
3878 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3880 tputs(T("Unknown"));
3882 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3884 tfputs(T("Space saved: "), stdout);
3885 if (new_size != -1 && old_size != -1) {
3886 tprintf(T("%lld KiB\n"),
3887 ((long long)old_size - (long long)new_size) >> 10);
3889 tputs(T("Unknown"));
3898 usage(CMD_OPTIMIZE, stderr);
3904 /* Split a WIM into a spanned set */
3906 imagex_split(int argc, tchar **argv, int cmd)
3910 int write_flags = 0;
3911 unsigned long part_size;
3916 for_opt(c, split_options) {
3918 case IMAGEX_CHECK_OPTION:
3919 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3920 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3932 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3933 if (tmp == argv[2] || *tmp) {
3934 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3935 imagex_error(T("The part size must be an integer or "
3936 "floating-point number of megabytes."));
3939 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3940 imagex_progress_func, NULL);
3944 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3950 usage(CMD_SPLIT, stderr);
3956 #if WIM_MOUNTING_SUPPORTED
3957 /* Unmounts a mounted WIM image. */
3959 imagex_unmount(int argc, tchar **argv, int cmd)
3962 int unmount_flags = 0;
3965 for_opt(c, unmount_options) {
3967 case IMAGEX_COMMIT_OPTION:
3968 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3970 case IMAGEX_CHECK_OPTION:
3971 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3973 case IMAGEX_REBUILD_OPTION:
3974 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3976 case IMAGEX_LAZY_OPTION:
3977 case IMAGEX_FORCE_OPTION:
3978 /* Now, unmount is lazy by default. However, committing
3979 * the image will fail with
3980 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3981 * file descriptors on the WIM image. The
3982 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3983 * descriptors to be closed. */
3984 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3986 case IMAGEX_NEW_IMAGE_OPTION:
3987 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
3998 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
3999 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4000 imagex_error(T("--new-image is meaningless "
4001 "without --commit also specified!"));
4006 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4007 imagex_progress_func, NULL);
4009 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4010 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4012 "\tNote: Use --commit --force to force changes "
4013 "to be committed, regardless\n"
4014 "\t of open files.\n"));
4021 usage(CMD_UNMOUNT, stderr);
4026 #endif /* WIM_MOUNTING_SUPPORTED */
4029 * Add, delete, or rename files in a WIM image.
4032 imagex_update(int argc, tchar **argv, int cmd)
4034 const tchar *wimfile;
4038 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4039 int write_flags = 0;
4040 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4041 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4042 WIMLIB_ADD_FLAG_VERBOSE |
4043 WIMLIB_ADD_FLAG_WINCONFIG;
4044 int default_delete_flags = 0;
4045 unsigned num_threads = 0;
4047 tchar *cmd_file_contents;
4048 size_t cmd_file_nchars;
4049 struct wimlib_update_command *cmds;
4051 tchar *command_str = NULL;
4052 tchar *config_file = NULL;
4053 tchar *wimboot_config = NULL;
4055 for_opt(c, update_options) {
4057 /* Generic or write options */
4058 case IMAGEX_THREADS_OPTION:
4059 num_threads = parse_num_threads(optarg);
4060 if (num_threads == UINT_MAX)
4063 case IMAGEX_CHECK_OPTION:
4064 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4065 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4067 case IMAGEX_REBUILD_OPTION:
4068 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4070 case IMAGEX_COMMAND_OPTION:
4072 imagex_error(T("--command may only be specified "
4073 "one time. Please provide\n"
4074 " the update commands "
4075 "on standard input instead."));
4078 command_str = tstrdup(optarg);
4080 imagex_error(T("Out of memory!"));
4084 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4085 wimboot_config = optarg;
4087 /* Default delete options */
4088 case IMAGEX_FORCE_OPTION:
4089 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4091 case IMAGEX_RECURSIVE_OPTION:
4092 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4095 /* Global add option */
4096 case IMAGEX_CONFIG_OPTION:
4097 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4098 config_file = optarg;
4101 /* Default add options */
4102 case IMAGEX_VERBOSE_OPTION:
4103 /* No longer does anything. */
4105 case IMAGEX_DEREFERENCE_OPTION:
4106 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4108 case IMAGEX_UNIX_DATA_OPTION:
4109 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4111 case IMAGEX_NO_ACLS_OPTION:
4112 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4114 case IMAGEX_STRICT_ACLS_OPTION:
4115 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4117 case IMAGEX_NO_REPLACE_OPTION:
4118 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4120 case IMAGEX_UNSAFE_COMPACT_OPTION:
4121 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4130 if (argc != 1 && argc != 2)
4134 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4135 imagex_progress_func, NULL);
4137 goto out_free_command_str;
4140 /* Image explicitly specified. */
4141 image = wimlib_resolve_image(wim, argv[1]);
4142 ret = verify_image_exists_and_is_single(image, argv[1],
4145 goto out_wimlib_free;
4147 /* No image specified; default to image 1, but only if the WIM
4148 * contains exactly one image. */
4149 struct wimlib_wim_info info;
4151 wimlib_get_wim_info(wim, &info);
4152 if (info.image_count != 1) {
4153 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4154 wimfile, info.image_count);
4161 /* Read update commands from standard input, or the command string if
4164 cmd_file_contents = NULL;
4165 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4169 goto out_free_cmd_file_contents;
4171 } else if (!wimboot_config) {
4172 if (isatty(STDIN_FILENO)) {
4173 tputs(T("Reading update commands from standard input..."));
4174 recommend_man_page(CMD_UPDATE, stdout);
4176 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4177 if (!cmd_file_contents) {
4179 goto out_wimlib_free;
4182 /* Parse the update commands */
4183 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4187 goto out_free_cmd_file_contents;
4190 cmd_file_contents = NULL;
4195 /* Set default flags and capture config on the update commands */
4196 for (size_t i = 0; i < num_cmds; i++) {
4197 switch (cmds[i].op) {
4198 case WIMLIB_UPDATE_OP_ADD:
4199 cmds[i].add.add_flags |= default_add_flags;
4200 cmds[i].add.config_file = config_file;
4202 case WIMLIB_UPDATE_OP_DELETE:
4203 cmds[i].delete_.delete_flags |= default_delete_flags;
4210 /* Execute the update commands */
4211 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4215 if (wimboot_config) {
4216 /* --wimboot-config=FILE is short for an
4217 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4219 struct wimlib_update_command cmd;
4221 cmd.op = WIMLIB_UPDATE_OP_ADD;
4222 cmd.add.fs_source_path = wimboot_config;
4223 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4224 cmd.add.config_file = NULL;
4225 cmd.add.add_flags = 0;
4227 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4232 /* Overwrite the updated WIM */
4233 ret = wimlib_overwrite(wim, write_flags, num_threads);
4236 out_free_cmd_file_contents:
4237 free(cmd_file_contents);
4240 out_free_command_str:
4245 usage(CMD_UPDATE, stderr);
4248 goto out_free_command_str;
4251 /* Verify a WIM file. */
4253 imagex_verify(int argc, tchar **argv, int cmd)
4256 const tchar *wimfile;
4258 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4259 int verify_flags = 0;
4260 STRING_LIST(refglobs);
4263 for_opt(c, verify_options) {
4265 case IMAGEX_REF_OPTION:
4266 ret = string_list_append(&refglobs, optarg);
4268 goto out_free_refglobs;
4270 case IMAGEX_NOCHECK_OPTION:
4271 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4283 imagex_error(T("Must specify a WIM file!"));
4285 imagex_error(T("At most one WIM file can be specified!"));
4291 ret = wimlib_open_wim_with_progress(wimfile,
4294 imagex_progress_func,
4297 goto out_free_refglobs;
4299 ret = wim_reference_globs(wim, &refglobs, open_flags);
4301 goto out_wimlib_free;
4303 ret = wimlib_verify_wim(wim, verify_flags);
4305 tputc(T('\n'), stderr);
4306 imagex_error(T("\"%"TS"\" failed verification!"),
4308 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4309 refglobs.num_strings == 0)
4311 imagex_printf(T("Note: if this WIM file is not standalone, "
4312 "use the --ref option to specify the other parts.\n"));
4315 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4322 string_list_destroy(&refglobs);
4326 usage(CMD_VERIFY, stderr);
4328 goto out_free_refglobs;
4331 struct imagex_command {
4333 int (*func)(int argc, tchar **argv, int cmd);
4336 static const struct imagex_command imagex_commands[] = {
4337 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4338 [CMD_APPLY] = {T("apply"), imagex_apply},
4339 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4340 [CMD_DELETE] = {T("delete"), imagex_delete},
4341 [CMD_DIR ] = {T("dir"), imagex_dir},
4342 [CMD_EXPORT] = {T("export"), imagex_export},
4343 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4344 [CMD_INFO] = {T("info"), imagex_info},
4345 [CMD_JOIN] = {T("join"), imagex_join},
4346 #if WIM_MOUNTING_SUPPORTED
4347 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4348 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4350 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4351 [CMD_SPLIT] = {T("split"), imagex_split},
4352 #if WIM_MOUNTING_SUPPORTED
4353 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4355 [CMD_UPDATE] = {T("update"), imagex_update},
4356 [CMD_VERIFY] = {T("verify"), imagex_verify},
4361 /* Can be a directory or source list file. But source list file is probably
4362 * a rare use case, so just say directory. */
4363 # define SOURCE_STR T("DIRECTORY")
4365 /* Can only be a directory */
4366 # define TARGET_STR T("DIRECTORY")
4369 /* Can be a directory, NTFS volume, or source list file. */
4370 # define SOURCE_STR T("SOURCE")
4372 /* Can be a directory or NTFS volume. */
4373 # define TARGET_STR T("TARGET")
4377 static const tchar *usage_strings[] = {
4380 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4381 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4382 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4383 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4384 " [--wimboot] [--unix-data] [--dereference] [--snapshot]\n"
4388 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4389 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4390 " [--no-attributes] [--rpfix] [--norpfix]\n"
4391 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4392 " [--compact=FORMAT]\n"
4396 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4397 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4398 " [--config=FILE] [--threads=NUM_THREADS]\n"
4399 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4400 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4401 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4406 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4410 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4414 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4415 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4416 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4417 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4418 " [--wimboot] [--solid]\n"
4422 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4423 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4424 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4425 " [--no-attributes] [--include-invalid-names]\n"
4426 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4430 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4431 " [--boot] [--check] [--nocheck] [--xml]\n"
4432 " [--extract-xml FILE] [--header] [--blobs]\n"
4433 " [--image-property NAME=VALUE]\n"
4437 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4439 #if WIM_MOUNTING_SUPPORTED
4442 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4443 " [--check] [--streams-interface=INTERFACE]\n"
4444 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4448 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4449 " [--check] [--streams-interface=INTERFACE]\n"
4450 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4456 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4457 " [--check] [--nocheck] [--solid]\n"
4462 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4464 #if WIM_MOUNTING_SUPPORTED
4467 " %"TS" DIRECTORY\n"
4468 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4473 " %"TS" WIMFILE [IMAGE]\n"
4474 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4475 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4476 " [--command=STRING] [--wimboot-config=FILE]\n"
4481 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4485 static const tchar *invocation_name;
4486 static int invocation_cmd = CMD_NONE;
4488 static const tchar *get_cmd_string(int cmd, bool nospace)
4490 static tchar buf[50];
4491 if (cmd == CMD_NONE) {
4492 return T("wimlib-imagex");
4493 } else if (invocation_cmd != CMD_NONE) {
4494 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4496 const tchar *format;
4499 format = T("%"TS"-%"TS"");
4501 format = T("%"TS" %"TS"");
4502 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4510 static const tchar *s =
4512 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4513 "Copyright (C) 2012-2016 Eric Biggers\n"
4514 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4515 "This is free software: you are free to change and redistribute it.\n"
4516 "There is NO WARRANTY, to the extent permitted by law.\n"
4518 "Report bugs to "PACKAGE_BUGREPORT".\n"
4525 help_or_version(int argc, tchar **argv, int cmd)
4530 for (i = 1; i < argc; i++) {
4532 if (p[0] == T('-') && p[1] == T('-')) {
4534 if (!tstrcmp(p, T("help"))) {
4535 if (cmd == CMD_NONE)
4540 } else if (!tstrcmp(p, T("version"))) {
4549 print_usage_string(int cmd, FILE *fp)
4551 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4555 recommend_man_page(int cmd, FILE *fp)
4557 const tchar *format_str;
4559 format_str = T("Some uncommon options are not listed;\n"
4560 "See %"TS".pdf in the doc directory for more details.\n");
4562 format_str = T("Some uncommon options are not listed;\n"
4563 "Try `man %"TS"' for more details.\n");
4565 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4569 usage(int cmd, FILE *fp)
4571 tfprintf(fp, T("Usage:\n"));
4572 print_usage_string(cmd, fp);
4573 tfprintf(fp, T("\n"));
4574 recommend_man_page(cmd, fp);
4580 tfprintf(fp, T("Usage:\n"));
4581 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4582 print_usage_string(cmd, fp);
4583 tfprintf(fp, T("\n"));
4585 static const tchar *extra =
4588 " %"TS" --version\n"
4591 tfprintf(fp, extra, invocation_name, invocation_name);
4593 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4594 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4595 "For some commands IMAGE may be \"all\".\n"
4597 recommend_man_page(CMD_NONE, fp);
4601 extern int wmain(int argc, wchar_t **argv);
4605 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4606 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4607 * something else), while on Windows the command arguments will be UTF-16LE
4608 * encoded 'wchar_t' strings. */
4610 main(int argc, tchar **argv)
4616 imagex_info_file = stdout;
4617 invocation_name = tbasename(argv[0]);
4620 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4621 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4625 setlocale(LC_ALL, "");
4626 codeset = nl_langinfo(CODESET);
4627 if (!strstr(codeset, "UTF-8") &&
4628 !strstr(codeset, "UTF8") &&
4629 !strstr(codeset, "utf-8") &&
4630 !strstr(codeset, "utf8"))
4633 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4634 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4635 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4636 " to any value to force wimlib to use UTF-8.\n",
4642 #endif /* !__WIN32__ */
4645 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4646 if (igcase != NULL) {
4647 if (!tstrcmp(igcase, T("no")) ||
4648 !tstrcmp(igcase, T("0")))
4649 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4650 else if (!tstrcmp(igcase, T("yes")) ||
4651 !tstrcmp(igcase, T("1")))
4652 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4655 "WARNING: Ignoring unknown setting of "
4656 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4661 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4663 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4664 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4665 for (int i = 0; i < CMD_MAX; i++) {
4666 if (!tstrcmp(invocation_name + 3,
4667 imagex_commands[i].name))
4676 /* Unless already known from the invocation name, determine which
4677 * command was specified. */
4678 if (cmd == CMD_NONE) {
4680 imagex_error(T("No command specified!\n"));
4684 for (int i = 0; i < CMD_MAX; i++) {
4685 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4690 if (cmd != CMD_NONE) {
4696 /* Handle --help and --version. --help can be either for the program as
4697 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4698 * CMD_NONE). Note: help_or_version() will not return if a --help or
4699 * --version argument was found. */
4700 help_or_version(argc, argv, cmd);
4702 /* Bail if a valid command was not specified. */
4703 if (cmd == CMD_NONE) {
4704 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4709 /* Enable warning and error messages in wimlib to be more user-friendly.
4711 wimlib_set_print_errors(true);
4713 /* Initialize wimlib. */
4714 ret = wimlib_global_init(init_flags);
4716 goto out_check_status;
4718 /* Call the command handler function. */
4719 ret = imagex_commands[cmd].func(argc, argv, cmd);
4721 /* Check for error writing to standard output, especially since for some
4722 * commands, writing to standard output is part of the program's actual
4723 * behavior and not just for informational purposes. */
4724 if (ferror(stdout) || fclose(stdout)) {
4725 imagex_error_with_errno(T("error writing to standard output"));
4730 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4731 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4732 * error code from which an error message can be printed. */
4734 imagex_error(T("Exiting with error code %d:\n"
4736 wimlib_get_error_string(ret));
4737 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4738 imagex_error_with_errno(T("errno"));
4740 /* Make wimlib free any resources it's holding (although this is not
4741 * strictly necessary because the process is ending anyway). */
4742 wimlib_global_cleanup();