4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012-2017 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 only_short_form);
136 static FILE *imagex_info_file;
138 #define imagex_printf(format, ...) \
139 if (imagex_info_file) \
140 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
142 static void imagex_suppress_output(void)
144 imagex_info_file = NULL;
147 static void imagex_output_to_stderr(void)
149 if (imagex_info_file)
150 imagex_info_file = stderr;
153 static void imagex_flush_output(void)
155 if (imagex_info_file)
156 fflush(imagex_info_file);
160 IMAGEX_ALLOW_OTHER_OPTION,
164 IMAGEX_CHUNK_SIZE_OPTION,
165 IMAGEX_COMMAND_OPTION,
166 IMAGEX_COMMIT_OPTION,
167 IMAGEX_COMPACT_OPTION,
168 IMAGEX_COMPRESS_OPTION,
169 IMAGEX_CONFIG_OPTION,
171 IMAGEX_DELTA_FROM_OPTION,
172 IMAGEX_DEREFERENCE_OPTION,
173 IMAGEX_DEST_DIR_OPTION,
174 IMAGEX_DETAILED_OPTION,
175 IMAGEX_EXTRACT_XML_OPTION,
178 IMAGEX_HEADER_OPTION,
179 IMAGEX_IMAGE_PROPERTY_OPTION,
180 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
182 IMAGEX_METADATA_OPTION,
183 IMAGEX_NEW_IMAGE_OPTION,
184 IMAGEX_NOCHECK_OPTION,
185 IMAGEX_NORPFIX_OPTION,
186 IMAGEX_NOT_PIPABLE_OPTION,
187 IMAGEX_NO_ACLS_OPTION,
188 IMAGEX_NO_ATTRIBUTES_OPTION,
189 IMAGEX_NO_GLOBS_OPTION,
190 IMAGEX_NO_REPLACE_OPTION,
191 IMAGEX_NO_SOLID_SORT_OPTION,
192 IMAGEX_NULLGLOB_OPTION,
193 IMAGEX_ONE_FILE_ONLY_OPTION,
195 IMAGEX_PIPABLE_OPTION,
196 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
197 IMAGEX_REBUILD_OPTION,
198 IMAGEX_RECOMPRESS_OPTION,
199 IMAGEX_RECURSIVE_OPTION,
202 IMAGEX_SNAPSHOT_OPTION,
204 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
205 IMAGEX_SOLID_COMPRESS_OPTION,
207 IMAGEX_SOURCE_LIST_OPTION,
208 IMAGEX_STAGING_DIR_OPTION,
209 IMAGEX_STREAMS_INTERFACE_OPTION,
210 IMAGEX_STRICT_ACLS_OPTION,
211 IMAGEX_THREADS_OPTION,
212 IMAGEX_TO_STDOUT_OPTION,
213 IMAGEX_UNIX_DATA_OPTION,
214 IMAGEX_UNSAFE_COMPACT_OPTION,
215 IMAGEX_UPDATE_OF_OPTION,
216 IMAGEX_VERBOSE_OPTION,
217 IMAGEX_WIMBOOT_CONFIG_OPTION,
218 IMAGEX_WIMBOOT_OPTION,
222 static const struct option apply_options[] = {
223 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
224 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
225 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
226 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
227 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
228 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
229 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
230 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
231 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
232 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
233 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
234 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
235 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
239 static const struct option capture_or_append_options[] = {
240 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
241 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
242 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
243 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
244 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
245 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
246 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
247 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
248 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
249 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
250 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
251 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
252 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
253 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
254 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
255 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
256 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
257 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
258 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
259 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
260 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
261 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
262 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
263 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
264 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
265 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
266 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
267 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
268 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
269 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
270 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
274 static const struct option delete_options[] = {
275 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
276 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
277 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
281 static const struct option dir_options[] = {
282 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
283 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
284 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
285 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
289 static const struct option export_options[] = {
290 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
291 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
292 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
293 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
294 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
295 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
296 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
297 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
298 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
299 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
300 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
301 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
302 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
303 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
304 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
305 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
306 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
307 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
311 static const struct option extract_options[] = {
312 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
313 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
314 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
315 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
316 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
317 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
318 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
319 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
320 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
321 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
322 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
323 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
324 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
325 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
326 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
327 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
328 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
332 static const struct option info_options[] = {
333 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
334 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
335 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
336 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
337 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
338 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
339 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
340 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
341 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
342 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
346 static const struct option join_options[] = {
347 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
351 #if WIM_MOUNTING_SUPPORTED
352 static const struct option mount_options[] = {
353 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
354 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
355 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
356 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
357 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
358 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
359 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
364 static const struct option optimize_options[] = {
365 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
366 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
367 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
368 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
369 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
370 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
371 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
372 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
373 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
374 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
375 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
376 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
377 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
378 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
382 static const struct option split_options[] = {
383 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
387 #if WIM_MOUNTING_SUPPORTED
388 static const struct option unmount_options[] = {
389 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
390 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
391 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
392 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
393 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
394 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
399 static const struct option update_options[] = {
400 /* Careful: some of the options here set the defaults for update
401 * commands, but the flags given to an actual update command (and not to
402 * `imagex update' itself are also handled in
403 * update_command_add_option(). */
404 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
405 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
406 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
407 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
408 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
410 /* Default delete options */
411 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
412 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
414 /* Global add option */
415 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
417 /* Default add options */
418 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
419 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
420 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
421 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
422 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
423 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
424 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
425 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
430 static const struct option verify_options[] = {
431 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
432 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
438 # define _format_attribute(type, format_str, args_start) \
439 __attribute__((format(type, format_str, args_start)))
441 # define _format_attribute(type, format_str, args_start)
444 /* Print formatted error message to stderr. */
445 static void _format_attribute(printf, 1, 2)
446 imagex_error(const tchar *format, ...)
449 va_start(va, format);
450 tfputs(T("ERROR: "), stderr);
451 tvfprintf(stderr, format, va);
452 tputc(T('\n'), stderr);
456 /* Print formatted error message to stderr. */
457 static void _format_attribute(printf, 1, 2)
458 imagex_error_with_errno(const tchar *format, ...)
460 int errno_save = errno;
462 va_start(va, format);
463 tfputs(T("ERROR: "), stderr);
464 tvfprintf(stderr, format, va);
465 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
470 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
472 if (image == WIMLIB_NO_IMAGE) {
473 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
474 " Please specify a 1-based image index or "
475 "image name. To list the images\n"
476 " contained in the WIM archive, run\n"
478 " %"TS" \"%"TS"\"\n"),
479 image_name, wim_name,
480 get_cmd_string(CMD_INFO, false), wim_name);
481 return WIMLIB_ERR_INVALID_IMAGE;
487 verify_image_is_single(int image)
489 if (image == WIMLIB_ALL_IMAGES) {
490 imagex_error(T("Cannot specify all images for this action!"));
491 return WIMLIB_ERR_INVALID_IMAGE;
497 verify_image_exists_and_is_single(int image, const tchar *image_name,
498 const tchar *wim_name)
501 ret = verify_image_exists(image, image_name, wim_name);
503 ret = verify_image_is_single(image);
508 print_available_compression_types(FILE *fp)
510 static const tchar * const s =
512 "Available compression types:\n"
515 " xpress (alias: \"fast\")\n"
516 " lzx (alias: \"maximum\") (default for capture)\n"
517 " lzms (alias: \"recovery\")\n"
523 /* Parse the argument to --compress or --solid-compress */
525 get_compression_type(tchar *optarg, bool solid)
528 unsigned int compression_level = 0;
531 plevel = tstrchr(optarg, T(':'));
537 ultmp = tstrtoul(plevel, &ptmp, 10);
538 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
539 imagex_error(T("Compression level must be a positive integer! "
540 "e.g. --compress=lzx:80"));
541 return WIMLIB_COMPRESSION_TYPE_INVALID;
543 compression_level = ultmp;
546 if (!tstrcasecmp(optarg, T("maximum")) ||
547 !tstrcasecmp(optarg, T("lzx")) ||
548 !tstrcasecmp(optarg, T("max"))) {
549 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
550 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
551 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
552 } else if (!tstrcasecmp(optarg, T("recovery"))) {
556 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
557 " differently from DISM. Instead, you typically want to use '--solid' to\n"
558 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
559 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
560 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
561 " of '--compress=recovery'.\n"));
563 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
564 } else if (!tstrcasecmp(optarg, T("lzms"))) {
565 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
566 } else if (!tstrcasecmp(optarg, T("none"))) {
567 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
569 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
570 print_available_compression_types(stderr);
571 return WIMLIB_COMPRESSION_TYPE_INVALID;
574 if (compression_level != 0)
575 wimlib_set_default_compression_level(ctype, compression_level);
579 /* Parse the argument to --compact */
581 set_compact_mode(const tchar *arg, int *extract_flags)
584 if (!tstrcasecmp(arg, T("xpress4k")))
585 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
586 else if (!tstrcasecmp(arg, T("xpress8k")))
587 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
588 else if (!tstrcasecmp(arg, T("xpress16k")))
589 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
590 else if (!tstrcasecmp(arg, T("lzx")))
591 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
594 *extract_flags |= flag;
599 "\"%"TS"\" is not a recognized System Compression format. The options are:"
601 " --compact=xpress4k\n"
602 " --compact=xpress8k\n"
603 " --compact=xpress16k\n"
612 unsigned num_strings;
613 unsigned num_alloc_strings;
616 #define STRING_LIST_INITIALIZER \
617 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
619 #define STRING_LIST(_strings) \
620 struct string_list _strings = STRING_LIST_INITIALIZER
623 string_list_append(struct string_list *list, tchar *glob)
625 unsigned num_alloc_strings = list->num_alloc_strings;
627 if (list->num_strings == num_alloc_strings) {
630 num_alloc_strings += 4;
631 new_strings = realloc(list->strings,
632 sizeof(list->strings[0]) * num_alloc_strings);
634 imagex_error(T("Out of memory!"));
637 list->strings = new_strings;
638 list->num_alloc_strings = num_alloc_strings;
640 list->strings[list->num_strings++] = glob;
645 string_list_destroy(struct string_list *list)
651 wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags)
653 return wimlib_reference_resource_files(wim, (const tchar **)list->strings,
655 WIMLIB_REF_FLAG_GLOB_ENABLE,
660 append_image_property_argument(struct string_list *image_properties)
662 if (!tstrchr(optarg, '=')) {
663 imagex_error(T("'--image-property' argument "
664 "must be in the form NAME=VALUE"));
667 return string_list_append(image_properties, optarg);
671 apply_image_properties(struct string_list *image_properties,
672 WIMStruct *wim, int image, bool *any_changes_ret)
674 bool any_changes = false;
675 for (unsigned i = 0; i < image_properties->num_strings; i++) {
677 const tchar *current_value;
680 name = image_properties->strings[i];
681 value = tstrchr(name, '=');
684 current_value = wimlib_get_image_property(wim, image, name);
685 if (current_value && !tstrcmp(current_value, value)) {
686 imagex_printf(T("The %"TS" property of image %d "
687 "already has value \"%"TS"\".\n"),
690 imagex_printf(T("Setting the %"TS" property of image "
691 "%d to \"%"TS"\".\n"),
693 ret = wimlib_set_image_property(wim, image, name, value);
700 *any_changes_ret = any_changes;
705 do_resource_not_found_warning(const tchar *wimfile,
706 const struct wimlib_wim_info *info,
707 const struct string_list *refglobs)
709 if (info->total_parts > 1) {
710 if (refglobs->num_strings == 0) {
711 imagex_error(T("\"%"TS"\" is part of a split WIM. "
712 "Use --ref to specify the other parts."),
715 imagex_error(T("Perhaps the '--ref' argument did not "
716 "specify all other parts of the split "
720 imagex_error(T("If this is a delta WIM, use the --ref argument "
721 "to specify the WIM(s) on which it is based."));
726 do_metadata_not_found_warning(const tchar *wimfile,
727 const struct wimlib_wim_info *info)
729 if (info->part_number != 1) {
730 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
731 " You must specify the first part."),
736 /* Returns the size of a file given its name, or -1 if the file does not exist
737 * or its size cannot be determined. */
739 file_get_size(const tchar *filename)
742 if (tstat(filename, &st) == 0)
749 PARSE_STRING_SUCCESS = 0,
750 PARSE_STRING_FAILURE = 1,
751 PARSE_STRING_NONE = 2,
755 * Parses a string token from an array of characters.
757 * Tokens are either whitespace-delimited, or double or single-quoted.
759 * @line_p: Pointer to the pointer to the line of data. Will be updated
760 * to point past the string token iff the return value is
761 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
764 * @len_p: @len_p initially stores the length of the line of data, which may
765 * be 0, and it will be updated to the number of bytes remaining in
766 * the line iff the return value is PARSE_STRING_SUCCESS.
768 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
769 * parsed string token will be returned here.
771 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
772 * PARSE_STRING_FAILURE if the data was invalid due to a missing
773 * closing quote; or PARSE_STRING_NONE if the line ended before the
774 * beginning of a string token was found.
777 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
780 tchar *line = *line_p;
784 /* Skip leading whitespace */
787 return PARSE_STRING_NONE;
788 if (!istspace(*line) && *line != T('\0'))
794 if (quote_char == T('"') || quote_char == T('\'')) {
799 line = tmemchr(line, quote_char, len);
801 imagex_error(T("Missing closing quote: %"TS), fn - 1);
802 return PARSE_STRING_FAILURE;
805 /* Unquoted string. Go until whitespace. Line is terminated
806 * by '\0', so no need to check 'len'. */
810 } while (!istspace(*line) && *line != T('\0'));
817 return PARSE_STRING_SUCCESS;
820 /* Parses a line of data (not an empty line or comment) in the source list file
821 * format. (See the man page for 'wimlib-imagex capture' for details on this
822 * format and the meaning.)
824 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
825 * len == 0. The data in @line will be modified by this function call.
827 * @len: Length of the line of data.
829 * @source: On success, the capture source and target described by the line is
830 * written into this destination. Note that it will contain pointers
831 * to data in the @line array.
833 * Returns true if the line was valid; false otherwise. */
835 parse_source_list_line(tchar *line, size_t len,
836 struct wimlib_capture_source *source)
840 ret = parse_string(&line, &len, &source->fs_source_path);
841 if (ret != PARSE_STRING_SUCCESS)
843 ret = parse_string(&line, &len, &source->wim_target_path);
844 if (ret == PARSE_STRING_NONE)
845 source->wim_target_path = source->fs_source_path;
846 return ret != PARSE_STRING_FAILURE;
849 /* Returns %true if the given line of length @len > 0 is a comment or empty line
850 * in the source list file format. */
852 is_comment_line(const tchar *line, size_t len)
855 if (*line == T('#') || *line == T(';'))
857 if (!istspace(*line) && *line != T('\0'))
867 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
870 tchar *contents = *contents_p;
871 size_t nchars = *nchars_p;
874 for (i = 0; i < nchars; i++)
875 if (contents[i] == T('\n'))
878 /* Handle last line not terminated by a newline */
879 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
880 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
882 imagex_error(T("Out of memory!"));
885 contents[nchars] = T('\n');
886 *contents_p = contents;
894 /* Parses a file in the source list format. (See the man page for
895 * 'wimlib-imagex capture' for details on this format and the meaning.)
897 * @source_list_contents: Contents of the source list file. Note that this
898 * buffer will be modified to save memory allocations,
899 * and cannot be freed until the returned array of
900 * wimlib_capture_source's has also been freed.
902 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
905 * @nsources_ret: On success, the length of the returned array is
908 * Returns: An array of `struct wimlib_capture_source's that can be passed to
909 * the wimlib_add_image_multisource() function to specify how a WIM image is to
911 static struct wimlib_capture_source *
912 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
913 size_t *nsources_ret)
917 struct wimlib_capture_source *sources;
920 nlines = text_file_count_lines(source_list_contents_p,
921 &source_list_nchars);
925 /* Always allocate at least 1 slot, just in case the implementation of
926 * calloc() returns NULL if 0 bytes are requested. */
927 sources = calloc(nlines ?: 1, sizeof(*sources));
929 imagex_error(T("out of memory"));
932 p = *source_list_contents_p;
934 for (i = 0; i < nlines; i++) {
935 /* XXX: Could use rawmemchr() here instead, but it may not be
936 * available on all platforms. */
937 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
938 size_t len = endp - p + 1;
940 if (!is_comment_line(p, len)) {
941 if (!parse_source_list_line(p, len, &sources[j++])) {
953 /* Reads the contents of a file into memory. */
955 file_get_contents(const tchar *filename, size_t *len_ret)
962 if (tstat(filename, &stbuf) != 0) {
963 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
968 fp = tfopen(filename, T("rb"));
970 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
974 buf = malloc(len ? len : 1);
976 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
977 "contents of file \"%"TS"\""), len, filename);
980 if (fread(buf, 1, len, fp) != len) {
981 imagex_error_with_errno(T("Failed to read %zu bytes from the "
982 "file \"%"TS"\""), len, filename);
996 /* Read standard input until EOF and return the full contents in a malloc()ed
997 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1000 stdin_get_contents(size_t *len_ret)
1002 /* stdin can, of course, be a pipe or other non-seekable file, so the
1003 * total length of the data cannot be pre-determined */
1005 size_t newlen = 1024;
1009 char *p = realloc(buf, newlen);
1010 size_t bytes_read, bytes_to_read;
1012 imagex_error(T("out of memory while reading stdin"));
1016 bytes_to_read = newlen - pos;
1017 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1019 if (bytes_read != bytes_to_read) {
1024 imagex_error_with_errno(T("error reading stdin"));
1038 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1041 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1043 *num_tchars_ret = num_bytes;
1045 #else /* !__WIN32__ */
1046 /* On Windows, translate the text to UTF-16LE */
1050 if (num_bytes >= 2 &&
1051 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1052 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1054 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1055 * with something that looks like an ASCII character encoded as
1056 * a UTF-16LE code unit. Assume the file is encoded as
1057 * UTF-16LE. This is not a 100% reliable check. */
1058 num_wchars = num_bytes / 2;
1059 text_wstr = (wchar_t*)text;
1061 /* File does not look like UTF-16LE. Assume it is encoded in
1062 * the current Windows code page. I think these are always
1063 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1064 * should work as expected. */
1065 text_wstr = win32_mbs_to_wcs(text,
1070 *num_tchars_ret = num_wchars;
1072 #endif /* __WIN32__ */
1076 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1081 contents = file_get_contents(filename, &num_bytes);
1084 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1088 stdin_get_text_contents(size_t *num_tchars_ret)
1093 contents = stdin_get_contents(&num_bytes);
1096 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1099 #define TO_PERCENT(numerator, denominator) \
1100 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1102 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1103 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1104 #define KIBIBYTE_MIN_NBYTES 10000ULL
1107 get_unit(uint64_t total_bytes, const tchar **name_ret)
1109 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1110 *name_ret = T("GiB");
1112 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1113 *name_ret = T("MiB");
1115 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1116 *name_ret = T("KiB");
1119 *name_ret = T("bytes");
1124 static struct wimlib_progress_info_scan last_scan_progress;
1127 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1129 uint64_t prev_count, cur_count;
1131 prev_count = last_scan_progress.num_nondirs_scanned +
1132 last_scan_progress.num_dirs_scanned;
1133 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1135 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1136 cur_count % 128 == 0)
1138 unsigned unit_shift;
1139 const tchar *unit_name;
1141 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1142 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1143 "%"PRIu64" directories) "),
1144 scan->num_bytes_scanned >> unit_shift,
1146 scan->num_nondirs_scanned,
1147 scan->num_dirs_scanned);
1148 last_scan_progress = *scan;
1151 /* Progress callback function passed to various wimlib functions. */
1152 static enum wimlib_progress_status
1153 imagex_progress_func(enum wimlib_progress_msg msg,
1154 union wimlib_progress_info *info,
1155 void *_ignored_context)
1157 unsigned percent_done;
1158 unsigned unit_shift;
1159 const tchar *unit_name;
1162 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1164 static bool started;
1166 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1167 imagex_printf(T("Using %"TS" compression "
1168 "with %u thread%"TS"\n"),
1169 wimlib_get_compression_type_string(
1170 info->write_streams.compression_type),
1171 info->write_streams.num_threads,
1172 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1177 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1178 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1179 info->write_streams.total_bytes);
1181 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1182 info->write_streams.completed_bytes >> unit_shift,
1184 info->write_streams.total_bytes >> unit_shift,
1187 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1188 imagex_printf(T("\n"));
1190 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1191 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1192 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1193 imagex_printf(T("\n"));
1195 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1196 info->scan.wim_target_path);
1198 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1200 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1201 switch (info->scan.status) {
1202 case WIMLIB_SCAN_DENTRY_OK:
1203 report_scan_progress(&info->scan, false);
1205 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1206 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1208 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1209 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1210 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1212 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1213 /* Symlink fixups are enabled by default. This is
1214 * mainly intended for Windows, which for some reason
1215 * uses absolute junctions (with drive letters!) in the
1216 * default installation. On UNIX-like systems, warn the
1217 * user when fixing the target of an absolute symbolic
1218 * link, so they know to disable this if they want. */
1220 imagex_printf(T("\nWARNING: Adjusted target of "
1221 "absolute symbolic link \"%"TS"\"\n"
1222 " (Use --norpfix to capture "
1223 "absolute symbolic links as-is)\n"),
1224 info->scan.cur_path);
1231 case WIMLIB_PROGRESS_MSG_SCAN_END:
1232 report_scan_progress(&info->scan, true);
1233 imagex_printf(T("\n"));
1235 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1236 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1237 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1238 info->integrity.total_bytes);
1239 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1240 "of %"PRIu64" %"TS" (%u%%) done"),
1241 info->integrity.filename,
1242 info->integrity.completed_bytes >> unit_shift,
1244 info->integrity.total_bytes >> unit_shift,
1247 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1248 imagex_printf(T("\n"));
1250 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1251 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1252 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1253 info->integrity.total_bytes);
1254 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1255 "of %"PRIu64" %"TS" (%u%%) done"),
1256 info->integrity.completed_bytes >> unit_shift,
1258 info->integrity.total_bytes >> unit_shift,
1261 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1262 imagex_printf(T("\n"));
1264 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1265 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1266 "to %"TS" \"%"TS"\"\n"),
1267 info->extract.image,
1268 info->extract.image_name,
1269 info->extract.wimfile_name,
1270 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1271 T("NTFS volume") : T("directory")),
1272 info->extract.target);
1274 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1275 if (info->extract.end_file_count >= 2000) {
1276 percent_done = TO_PERCENT(info->extract.current_file_count,
1277 info->extract.end_file_count);
1278 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1279 info->extract.current_file_count,
1280 info->extract.end_file_count, percent_done);
1281 if (info->extract.current_file_count == info->extract.end_file_count)
1282 imagex_printf(T("\n"));
1285 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1286 percent_done = TO_PERCENT(info->extract.completed_bytes,
1287 info->extract.total_bytes);
1288 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1289 imagex_printf(T("\rExtracting file data: "
1290 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1291 info->extract.completed_bytes >> unit_shift,
1293 info->extract.total_bytes >> unit_shift,
1296 if (info->extract.completed_bytes >= info->extract.total_bytes)
1297 imagex_printf(T("\n"));
1299 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1300 if (info->extract.end_file_count >= 2000) {
1301 percent_done = TO_PERCENT(info->extract.current_file_count,
1302 info->extract.end_file_count);
1303 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1304 info->extract.current_file_count,
1305 info->extract.end_file_count, percent_done);
1306 if (info->extract.current_file_count == info->extract.end_file_count)
1307 imagex_printf(T("\n"));
1310 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1311 if (info->extract.total_parts != 1) {
1312 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1313 info->extract.part_number,
1314 info->extract.total_parts);
1317 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1318 percent_done = TO_PERCENT(info->split.completed_bytes,
1319 info->split.total_bytes);
1320 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1321 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1322 "%"PRIu64" %"TS" (%u%%) written\n"),
1323 info->split.part_name,
1324 info->split.cur_part_number,
1325 info->split.total_parts,
1326 info->split.completed_bytes >> unit_shift,
1328 info->split.total_bytes >> unit_shift,
1332 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1333 if (info->split.completed_bytes == info->split.total_bytes) {
1334 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1335 info->split.cur_part_number,
1336 info->split.total_parts);
1339 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1340 switch (info->update.command->op) {
1341 case WIMLIB_UPDATE_OP_DELETE:
1342 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1343 info->update.command->delete_.wim_path);
1345 case WIMLIB_UPDATE_OP_RENAME:
1346 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1347 info->update.command->rename.wim_source_path,
1348 info->update.command->rename.wim_target_path);
1350 case WIMLIB_UPDATE_OP_ADD:
1355 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1356 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1357 info->replace.path_in_wim);
1359 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1360 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1361 info->wimboot_exclude.path_in_wim);
1363 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1364 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1365 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1366 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1367 info->unmount.mounted_wim,
1368 info->unmount.mounted_image);
1370 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1371 info->unmount.mounted_wim,
1372 info->unmount.mounted_image);
1373 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1377 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1378 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1379 info->verify_image.current_image,
1380 info->verify_image.total_images);
1382 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1383 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1384 info->verify_streams.total_bytes);
1385 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1386 imagex_printf(T("\rVerifying file data: "
1387 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1388 info->verify_streams.completed_bytes >> unit_shift,
1390 info->verify_streams.total_bytes >> unit_shift,
1393 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1394 imagex_printf(T("\n"));
1399 imagex_flush_output();
1400 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1404 parse_num_threads(const tchar *optarg)
1407 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1408 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1409 imagex_error(T("Number of threads must be a non-negative integer!"));
1417 parse_chunk_size(const tchar *optarg)
1420 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1421 if (chunk_size == 0) {
1422 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1423 " with optional K, M, or G suffix"));
1427 if (*tmp == T('k') || *tmp == T('K')) {
1430 } else if (*tmp == T('m') || *tmp == T('M')) {
1433 } else if (*tmp == T('g') || *tmp == T('G')) {
1437 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1438 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1442 if (chunk_size >= UINT32_MAX) {
1443 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1451 * Parse an option passed to an update command.
1453 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1456 * @option: Text string for the option (beginning with --)
1458 * @cmd: `struct wimlib_update_command' that is being constructed for
1461 * Returns true if the option was recognized; false if not.
1464 update_command_add_option(int op, const tchar *option,
1465 struct wimlib_update_command *cmd)
1467 bool recognized = true;
1469 case WIMLIB_UPDATE_OP_ADD:
1470 if (!tstrcmp(option, T("--verbose")))
1471 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1472 else if (!tstrcmp(option, T("--unix-data")))
1473 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1474 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1475 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1476 else if (!tstrcmp(option, T("--strict-acls")))
1477 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1478 else if (!tstrcmp(option, T("--dereference")))
1479 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1480 else if (!tstrcmp(option, T("--no-replace")))
1481 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1485 case WIMLIB_UPDATE_OP_DELETE:
1486 if (!tstrcmp(option, T("--force")))
1487 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1488 else if (!tstrcmp(option, T("--recursive")))
1489 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1500 /* How many nonoption arguments each `imagex update' command expects */
1501 static const unsigned update_command_num_nonoptions[] = {
1502 [WIMLIB_UPDATE_OP_ADD] = 2,
1503 [WIMLIB_UPDATE_OP_DELETE] = 1,
1504 [WIMLIB_UPDATE_OP_RENAME] = 2,
1508 update_command_add_nonoption(int op, const tchar *nonoption,
1509 struct wimlib_update_command *cmd,
1510 unsigned num_nonoptions)
1513 case WIMLIB_UPDATE_OP_ADD:
1514 if (num_nonoptions == 0)
1515 cmd->add.fs_source_path = (tchar*)nonoption;
1517 cmd->add.wim_target_path = (tchar*)nonoption;
1519 case WIMLIB_UPDATE_OP_DELETE:
1520 cmd->delete_.wim_path = (tchar*)nonoption;
1522 case WIMLIB_UPDATE_OP_RENAME:
1523 if (num_nonoptions == 0)
1524 cmd->rename.wim_source_path = (tchar*)nonoption;
1526 cmd->rename.wim_target_path = (tchar*)nonoption;
1532 * Parse a command passed on stdin to `imagex update'.
1534 * @line: Text of the command.
1535 * @len: Length of the line, including a null terminator
1538 * @command: A `struct wimlib_update_command' to fill in from the parsed
1541 * @line_number: Line number of the command, for diagnostics.
1543 * Returns true on success; returns false on parse error.
1546 parse_update_command(tchar *line, size_t len,
1547 struct wimlib_update_command *command,
1551 tchar *command_name;
1553 size_t num_nonoptions;
1555 /* Get the command name ("add", "delete", "rename") */
1556 ret = parse_string(&line, &len, &command_name);
1557 if (ret != PARSE_STRING_SUCCESS)
1560 if (!tstrcasecmp(command_name, T("add"))) {
1561 op = WIMLIB_UPDATE_OP_ADD;
1562 } else if (!tstrcasecmp(command_name, T("delete"))) {
1563 op = WIMLIB_UPDATE_OP_DELETE;
1564 } else if (!tstrcasecmp(command_name, T("rename"))) {
1565 op = WIMLIB_UPDATE_OP_RENAME;
1567 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1568 command_name, line_number);
1573 /* Parse additional options and non-options as needed */
1578 ret = parse_string(&line, &len, &next_string);
1579 if (ret == PARSE_STRING_NONE) /* End of line */
1581 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1583 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1585 if (!update_command_add_option(op, next_string, command))
1587 imagex_error(T("Unrecognized option \"%"TS"\" to "
1588 "update command \"%"TS"\" on line %zu"),
1589 next_string, command_name, line_number);
1595 if (num_nonoptions == update_command_num_nonoptions[op])
1597 imagex_error(T("Unexpected argument \"%"TS"\" in "
1598 "update command on line %zu\n"
1599 " (The \"%"TS"\" command only "
1600 "takes %zu nonoption arguments!)\n"),
1601 next_string, line_number,
1602 command_name, num_nonoptions);
1605 update_command_add_nonoption(op, next_string,
1606 command, num_nonoptions);
1611 if (num_nonoptions != update_command_num_nonoptions[op]) {
1612 imagex_error(T("Not enough arguments to update command "
1613 "\"%"TS"\" on line %zu"), command_name, line_number);
1619 static struct wimlib_update_command *
1620 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1621 size_t *num_cmds_ret)
1625 struct wimlib_update_command *cmds;
1628 nlines = text_file_count_lines(cmd_file_contents_p,
1633 /* Always allocate at least 1 slot, just in case the implementation of
1634 * calloc() returns NULL if 0 bytes are requested. */
1635 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1637 imagex_error(T("out of memory"));
1640 p = *cmd_file_contents_p;
1642 for (i = 0; i < nlines; i++) {
1643 /* XXX: Could use rawmemchr() here instead, but it may not be
1644 * available on all platforms. */
1645 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1646 size_t len = endp - p + 1;
1648 if (!is_comment_line(p, len)) {
1649 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1660 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1661 * one image from a WIM file to an NTFS volume. */
1663 imagex_apply(int argc, tchar **argv, int cmd)
1667 int image = WIMLIB_NO_IMAGE;
1669 struct wimlib_wim_info info;
1671 const tchar *wimfile;
1672 const tchar *target;
1673 const tchar *image_num_or_name = NULL;
1674 int extract_flags = 0;
1676 STRING_LIST(refglobs);
1678 for_opt(c, apply_options) {
1680 case IMAGEX_CHECK_OPTION:
1681 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1683 case IMAGEX_VERBOSE_OPTION:
1684 /* No longer does anything. */
1686 case IMAGEX_REF_OPTION:
1687 ret = string_list_append(&refglobs, optarg);
1689 goto out_free_refglobs;
1691 case IMAGEX_UNIX_DATA_OPTION:
1692 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1694 case IMAGEX_NO_ACLS_OPTION:
1695 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1697 case IMAGEX_STRICT_ACLS_OPTION:
1698 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1700 case IMAGEX_NO_ATTRIBUTES_OPTION:
1701 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1703 case IMAGEX_NORPFIX_OPTION:
1704 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1706 case IMAGEX_RPFIX_OPTION:
1707 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1709 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1710 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1711 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1713 case IMAGEX_WIMBOOT_OPTION:
1714 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1716 case IMAGEX_COMPACT_OPTION:
1717 ret = set_compact_mode(optarg, &extract_flags);
1719 goto out_free_refglobs;
1727 if (argc != 2 && argc != 3)
1732 if (!tstrcmp(wimfile, T("-"))) {
1733 /* Attempt to apply pipable WIM from standard input. */
1735 image_num_or_name = NULL;
1738 image_num_or_name = argv[1];
1743 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1744 imagex_progress_func, NULL);
1746 goto out_free_refglobs;
1748 wimlib_get_wim_info(wim, &info);
1751 /* Image explicitly specified. */
1752 image_num_or_name = argv[1];
1753 image = wimlib_resolve_image(wim, image_num_or_name);
1754 ret = verify_image_exists(image, image_num_or_name, wimfile);
1756 goto out_wimlib_free;
1759 /* No image specified; default to image 1, but only if the WIM
1760 * contains exactly one image. */
1762 if (info.image_count != 1) {
1763 imagex_error(T("\"%"TS"\" contains %d images; "
1764 "Please select one (or all)."),
1765 wimfile, info.image_count);
1774 if (refglobs.num_strings) {
1776 imagex_error(T("Can't specify --ref when applying from stdin!"));
1778 goto out_wimlib_free;
1780 ret = wim_reference_globs(wim, &refglobs, open_flags);
1782 goto out_wimlib_free;
1787 /* Interpret a regular file or block device target as an NTFS
1791 if (tstat(target, &stbuf)) {
1792 if (errno != ENOENT) {
1793 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1796 goto out_wimlib_free;
1799 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1800 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1806 ret = wimlib_extract_image(wim, image, target, extract_flags);
1808 set_fd_to_binary_mode(STDIN_FILENO);
1809 ret = wimlib_extract_image_from_pipe_with_progress(
1814 imagex_progress_func,
1818 imagex_printf(T("Done applying WIM image.\n"));
1819 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1821 do_resource_not_found_warning(wimfile, &info, &refglobs);
1823 imagex_error(T( "If you are applying an image "
1824 "from a split pipable WIM,\n"
1825 " make sure you have "
1826 "concatenated together all parts."));
1828 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1829 do_metadata_not_found_warning(wimfile, &info);
1834 string_list_destroy(&refglobs);
1838 usage(CMD_APPLY, stderr);
1840 goto out_free_refglobs;
1843 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1844 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1845 * the desired image. 'wimlib-imagex append': add a new image to an existing
1848 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1852 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1853 WIMLIB_ADD_FLAG_WINCONFIG |
1854 WIMLIB_ADD_FLAG_VERBOSE |
1855 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1856 int write_flags = 0;
1857 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1858 uint32_t chunk_size = UINT32_MAX;
1859 uint32_t solid_chunk_size = UINT32_MAX;
1860 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1861 const tchar *wimfile;
1864 STRING_LIST(image_properties);
1867 STRING_LIST(base_wimfiles);
1868 WIMStruct **base_wims;
1870 WIMStruct *template_wim;
1871 const tchar *template_wimfile = NULL;
1872 const tchar *template_image_name_or_num = NULL;
1873 int template_image = WIMLIB_NO_IMAGE;
1876 unsigned num_threads = 0;
1881 tchar *config_file = NULL;
1883 bool source_list = false;
1884 size_t source_list_nchars = 0;
1885 tchar *source_list_contents;
1886 bool capture_sources_malloced;
1887 struct wimlib_capture_source *capture_sources;
1889 bool name_defaulted;
1891 for_opt(c, capture_or_append_options) {
1893 case IMAGEX_BOOT_OPTION:
1894 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1896 case IMAGEX_CHECK_OPTION:
1897 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1898 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1900 case IMAGEX_NOCHECK_OPTION:
1901 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1903 case IMAGEX_CONFIG_OPTION:
1904 config_file = optarg;
1905 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1907 case IMAGEX_COMPRESS_OPTION:
1908 compression_type = get_compression_type(optarg, false);
1909 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1912 case IMAGEX_CHUNK_SIZE_OPTION:
1913 chunk_size = parse_chunk_size(optarg);
1914 if (chunk_size == UINT32_MAX)
1917 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1918 solid_chunk_size = parse_chunk_size(optarg);
1919 if (solid_chunk_size == UINT32_MAX)
1922 case IMAGEX_SOLID_COMPRESS_OPTION:
1923 solid_ctype = get_compression_type(optarg, true);
1924 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1927 case IMAGEX_SOLID_OPTION:
1928 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1930 case IMAGEX_NO_SOLID_SORT_OPTION:
1931 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1933 case IMAGEX_FLAGS_OPTION: {
1934 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1935 tsprintf(p, T("FLAGS=%"TS), optarg);
1936 ret = string_list_append(&image_properties, p);
1941 case IMAGEX_IMAGE_PROPERTY_OPTION:
1942 ret = append_image_property_argument(&image_properties);
1946 case IMAGEX_DEREFERENCE_OPTION:
1947 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1949 case IMAGEX_VERBOSE_OPTION:
1950 /* No longer does anything. */
1952 case IMAGEX_THREADS_OPTION:
1953 num_threads = parse_num_threads(optarg);
1954 if (num_threads == UINT_MAX)
1957 case IMAGEX_REBUILD_OPTION:
1958 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1960 case IMAGEX_UNIX_DATA_OPTION:
1961 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1963 case IMAGEX_SOURCE_LIST_OPTION:
1966 case IMAGEX_NO_ACLS_OPTION:
1967 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1969 case IMAGEX_STRICT_ACLS_OPTION:
1970 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1972 case IMAGEX_RPFIX_OPTION:
1973 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1975 case IMAGEX_NORPFIX_OPTION:
1976 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1978 case IMAGEX_PIPABLE_OPTION:
1979 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1981 case IMAGEX_NOT_PIPABLE_OPTION:
1982 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1984 case IMAGEX_UPDATE_OF_OPTION:
1985 if (template_image_name_or_num) {
1986 imagex_error(T("'--update-of' can only be "
1987 "specified one time!"));
1991 colon = tstrrchr(optarg, T(':'));
1994 template_wimfile = optarg;
1996 template_image_name_or_num = colon + 1;
1998 template_wimfile = NULL;
1999 template_image_name_or_num = optarg;
2003 case IMAGEX_DELTA_FROM_OPTION:
2004 ret = string_list_append(&base_wimfiles, optarg);
2007 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2009 case IMAGEX_WIMBOOT_OPTION:
2010 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2012 case IMAGEX_UNSAFE_COMPACT_OPTION:
2013 if (cmd != CMD_APPEND) {
2014 imagex_error(T("'--unsafe-compact' is only "
2015 "valid for append!"));
2018 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2020 case IMAGEX_SNAPSHOT_OPTION:
2021 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2030 if (argc < 2 || argc > 4)
2036 /* Set default compression type and parameters. */
2039 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2040 /* No compression type specified. Use the default. */
2042 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2043 /* With --wimboot, default to XPRESS compression. */
2044 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2045 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2046 /* With --solid, default to LZMS compression. (However,
2047 * this will not affect solid resources!) */
2048 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2050 /* Otherwise, default to LZX compression. */
2051 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2055 if (!tstrcmp(wimfile, T("-"))) {
2056 /* Writing captured WIM to standard output. */
2058 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2059 imagex_error("Can't write a non-pipable WIM to "
2060 "standard output! Specify --pipable\n"
2061 " if you want to create a pipable WIM "
2062 "(but read the docs first).");
2066 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2068 if (cmd == CMD_APPEND) {
2069 imagex_error(T("Using standard output for append does "
2070 "not make sense."));
2073 wim_fd = STDOUT_FILENO;
2075 imagex_output_to_stderr();
2076 set_fd_to_binary_mode(wim_fd);
2079 /* If template image was specified using --update-of=IMAGE rather
2080 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2081 if (template_image_name_or_num && !template_wimfile) {
2082 if (base_wimfiles.num_strings == 1) {
2083 /* Capturing delta WIM based on single WIM: default to
2085 template_wimfile = base_wimfiles.strings[0];
2086 } else if (cmd == CMD_APPEND) {
2087 /* Appending to WIM: default to WIM being appended to.
2089 template_wimfile = wimfile;
2091 /* Capturing a normal (non-delta) WIM, so the WIM file
2092 * *must* be explicitly specified. */
2093 if (base_wimfiles.num_strings > 1) {
2094 imagex_error(T("For capture of delta WIM "
2095 "based on multiple existing "
2097 " '--update-of' must "
2098 "specify WIMFILE:IMAGE!"));
2100 imagex_error(T("For capture of non-delta WIM, "
2101 "'--update-of' must specify "
2110 name_defaulted = false;
2112 /* Set default name to SOURCE argument, omitting any directory
2113 * prefixes and trailing slashes. This requires making a copy
2114 * of @source. Leave some free characters at the end in case we
2115 * append a number to keep the name unique. */
2116 size_t source_name_len;
2118 source_name_len = tstrlen(source);
2119 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2120 name = tbasename(tstrcpy(source_copy, source));
2121 name_defaulted = true;
2124 /* Image description (if given). */
2126 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2127 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2128 ret = string_list_append(&image_properties, p);
2134 /* Set up capture sources in source list mode */
2135 if (source[0] == T('-') && source[1] == T('\0')) {
2136 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2138 source_list_contents = file_get_text_contents(source,
2139 &source_list_nchars);
2141 if (!source_list_contents)
2144 capture_sources = parse_source_list(&source_list_contents,
2147 if (!capture_sources) {
2149 goto out_free_source_list_contents;
2151 capture_sources_malloced = true;
2153 /* Set up capture source in non-source-list mode. */
2154 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2155 capture_sources[0].fs_source_path = source;
2156 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2157 capture_sources[0].reserved = 0;
2159 capture_sources_malloced = false;
2160 source_list_contents = NULL;
2163 /* Open the existing WIM, or create a new one. */
2164 if (cmd == CMD_APPEND) {
2165 ret = wimlib_open_wim_with_progress(wimfile,
2166 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2168 imagex_progress_func,
2171 goto out_free_capture_sources;
2173 ret = wimlib_create_new_wim(compression_type, &wim);
2175 goto out_free_capture_sources;
2176 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2179 /* Set chunk size if non-default. */
2180 if (chunk_size != UINT32_MAX) {
2181 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2184 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2186 int ctype = compression_type;
2188 if (cmd == CMD_APPEND) {
2189 struct wimlib_wim_info info;
2190 wimlib_get_wim_info(wim, &info);
2191 ctype = info.compression_type;
2194 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2195 ret = wimlib_set_output_chunk_size(wim, 4096);
2200 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2201 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2205 if (solid_chunk_size != UINT32_MAX) {
2206 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2212 /* Detect if source is regular file or block device and set NTFS volume
2217 if (tstat(source, &stbuf) == 0) {
2218 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2219 imagex_printf(T("Capturing WIM image from NTFS "
2220 "filesystem on \"%"TS"\"\n"), source);
2221 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2224 if (errno != ENOENT) {
2225 imagex_error_with_errno(T("Failed to stat "
2226 "\"%"TS"\""), source);
2234 /* If the user did not specify an image name, and the basename of the
2235 * source already exists as an image name in the WIM file, append a
2236 * suffix to make it unique. */
2237 if (cmd == CMD_APPEND && name_defaulted) {
2238 unsigned long conflict_idx;
2239 tchar *name_end = tstrchr(name, T('\0'));
2240 for (conflict_idx = 1;
2241 wimlib_image_name_in_use(wim, name);
2244 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2248 /* If capturing a delta WIM, reference resources from the base WIMs
2249 * before adding the new image. */
2250 if (base_wimfiles.num_strings) {
2251 base_wims = calloc(base_wimfiles.num_strings,
2252 sizeof(base_wims[0]));
2253 if (base_wims == NULL) {
2254 imagex_error(T("Out of memory!"));
2259 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2260 ret = wimlib_open_wim_with_progress(
2261 base_wimfiles.strings[i], open_flags,
2262 &base_wims[i], imagex_progress_func, NULL);
2264 goto out_free_base_wims;
2268 ret = wimlib_reference_resources(wim, base_wims,
2269 base_wimfiles.num_strings, 0);
2271 goto out_free_base_wims;
2273 if (base_wimfiles.num_strings == 1) {
2274 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2275 base_wimfiles.strings[0]);
2277 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2278 base_wimfiles.num_strings);
2285 /* If capturing or appending as an update of an existing (template) image,
2286 * open the WIM if needed and parse the image index. */
2287 if (template_image_name_or_num) {
2290 if (base_wimfiles.num_strings == 1 &&
2291 template_wimfile == base_wimfiles.strings[0]) {
2292 template_wim = base_wims[0];
2293 } else if (template_wimfile == wimfile) {
2296 ret = wimlib_open_wim_with_progress(template_wimfile,
2299 imagex_progress_func,
2302 goto out_free_base_wims;
2305 template_image = wimlib_resolve_image(template_wim,
2306 template_image_name_or_num);
2308 if (template_image_name_or_num[0] == T('-')) {
2311 struct wimlib_wim_info info;
2313 wimlib_get_wim_info(template_wim, &info);
2314 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2315 if (n >= 1 && n <= info.image_count &&
2317 tmp != template_image_name_or_num + 1)
2319 template_image = info.image_count - (n - 1);
2322 ret = verify_image_exists_and_is_single(template_image,
2323 template_image_name_or_num,
2326 goto out_free_template_wim;
2328 template_wim = NULL;
2331 ret = wimlib_add_image_multisource(wim,
2338 goto out_free_template_wim;
2340 if (image_properties.num_strings || template_image_name_or_num) {
2341 /* User asked to set additional image properties, or an image on
2342 * which the added one is to be based has been specified with
2344 struct wimlib_wim_info info;
2346 wimlib_get_wim_info(wim, &info);
2348 ret = apply_image_properties(&image_properties, wim,
2349 info.image_count, NULL);
2351 goto out_free_template_wim;
2353 /* Reference template image if the user provided one. */
2354 if (template_image_name_or_num) {
2355 imagex_printf(T("Using image %d "
2356 "from \"%"TS"\" as template\n"),
2357 template_image, template_wimfile);
2358 ret = wimlib_reference_template_image(wim,
2364 goto out_free_template_wim;
2368 /* Write the new WIM or overwrite the existing WIM with the new image
2370 if (cmd == CMD_APPEND) {
2371 ret = wimlib_overwrite(wim, write_flags, num_threads);
2372 } else if (wimfile) {
2373 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2374 write_flags, num_threads);
2376 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2377 write_flags, num_threads);
2379 out_free_template_wim:
2380 /* template_wim may alias base_wims[0] or wim. */
2381 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2382 template_wim != wim)
2383 wimlib_free(template_wim);
2385 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2386 wimlib_free(base_wims[i]);
2390 out_free_capture_sources:
2391 if (capture_sources_malloced)
2392 free(capture_sources);
2393 out_free_source_list_contents:
2394 free(source_list_contents);
2396 string_list_destroy(&image_properties);
2397 string_list_destroy(&base_wimfiles);
2407 /* Remove image(s) from a WIM. */
2409 imagex_delete(int argc, tchar **argv, int cmd)
2412 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2413 int write_flags = 0;
2414 const tchar *wimfile;
2415 const tchar *image_num_or_name;
2420 for_opt(c, delete_options) {
2422 case IMAGEX_CHECK_OPTION:
2423 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2424 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2426 case IMAGEX_SOFT_OPTION:
2427 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2429 case IMAGEX_UNSAFE_COMPACT_OPTION:
2430 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2441 imagex_error(T("Must specify a WIM file"));
2443 imagex_error(T("Must specify an image"));
2447 image_num_or_name = argv[1];
2449 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2450 imagex_progress_func, NULL);
2454 image = wimlib_resolve_image(wim, image_num_or_name);
2456 ret = verify_image_exists(image, image_num_or_name, wimfile);
2458 goto out_wimlib_free;
2460 ret = wimlib_delete_image(wim, image);
2462 imagex_error(T("Failed to delete image from \"%"TS"\""),
2464 goto out_wimlib_free;
2467 ret = wimlib_overwrite(wim, write_flags, 0);
2469 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2470 "deleted"), wimfile);
2478 usage(CMD_DELETE, stderr);
2483 struct print_dentry_options {
2488 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2490 tprintf(T("%"TS"\n"), dentry->full_path);
2493 static const struct {
2496 } file_attr_flags[] = {
2497 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2498 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2499 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2500 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2501 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2502 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2503 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2504 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2505 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2506 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2507 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2508 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2509 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2510 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2511 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2514 #define TIMESTR_MAX 100
2517 print_time(const tchar *type, const struct wimlib_timespec *wts,
2520 tchar timestr[TIMESTR_MAX];
2524 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2525 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2530 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2531 timestr[TIMESTR_MAX - 1] = '\0';
2533 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2536 static void print_byte_field(const uint8_t field[], size_t len)
2539 tprintf(T("%02hhx"), *field++);
2543 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2545 tchar attr_string[256];
2548 tputs(T("WIM Information:"));
2549 tputs(T("----------------"));
2550 tprintf(T("Path: %"TS"\n"), wimfile);
2551 tprintf(T("GUID: 0x"));
2552 print_byte_field(info->guid, sizeof(info->guid));
2554 tprintf(T("Version: %u\n"), info->wim_version);
2555 tprintf(T("Image Count: %d\n"), info->image_count);
2556 tprintf(T("Compression: %"TS"\n"),
2557 wimlib_get_compression_type_string(info->compression_type));
2558 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2560 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2561 tprintf(T("Boot Index: %d\n"), info->boot_index);
2562 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2564 attr_string[0] = T('\0');
2567 tstrcat(attr_string, T("Pipable, "));
2569 if (info->has_integrity_table)
2570 tstrcat(attr_string, T("Integrity info, "));
2572 if (info->has_rpfix)
2573 tstrcat(attr_string, T("Relative path junction, "));
2575 if (info->resource_only)
2576 tstrcat(attr_string, T("Resource only, "));
2578 if (info->metadata_only)
2579 tstrcat(attr_string, T("Metadata only, "));
2581 if (info->is_marked_readonly)
2582 tstrcat(attr_string, T("Readonly, "));
2584 p = tstrchr(attr_string, T('\0'));
2585 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2588 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2592 print_resource(const struct wimlib_resource_entry *resource,
2595 tprintf(T("Hash = 0x"));
2596 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2599 if (!resource->is_missing) {
2600 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2601 resource->uncompressed_size);
2602 if (resource->packed) {
2603 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2604 "bytes @ offset %"PRIu64"\n"),
2605 resource->raw_resource_uncompressed_size,
2606 resource->raw_resource_compressed_size,
2607 resource->raw_resource_offset_in_wim);
2609 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2612 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2613 resource->compressed_size);
2615 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2619 tprintf(T("Part Number = %u\n"), resource->part_number);
2620 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2622 tprintf(T("Flags = "));
2623 if (resource->is_compressed)
2624 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2625 if (resource->is_metadata)
2626 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2627 if (resource->is_free)
2628 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2629 if (resource->is_spanned)
2630 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2631 if (resource->packed)
2632 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2640 print_blobs(WIMStruct *wim)
2642 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2647 default_print_security_descriptor(const uint8_t *sd, size_t size)
2649 tprintf(T("Security Descriptor = "));
2650 print_byte_field(sd, size);
2656 is_null_guid(const uint8_t *guid)
2658 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2660 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2664 print_guid(const tchar *label, const uint8_t *guid)
2666 if (is_null_guid(guid))
2668 tprintf(T("%-20"TS"= 0x"), label);
2669 print_byte_field(guid, WIMLIB_GUID_LEN);
2674 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2677 "----------------------------------------------------------------------------\n"));
2678 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2679 if (dentry->dos_name)
2680 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2681 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2682 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2683 if (file_attr_flags[i].flag & dentry->attributes)
2684 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2685 file_attr_flags[i].name);
2687 if (dentry->security_descriptor) {
2688 print_security_descriptor(dentry->security_descriptor,
2689 dentry->security_descriptor_size);
2692 print_time(T("Creation Time"),
2693 &dentry->creation_time, dentry->creation_time_high);
2694 print_time(T("Last Write Time"),
2695 &dentry->last_write_time, dentry->last_write_time_high);
2696 print_time(T("Last Access Time"),
2697 &dentry->last_access_time, dentry->last_access_time_high);
2700 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2701 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2703 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2704 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2706 if (dentry->unix_mode != 0) {
2707 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2708 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2709 dentry->unix_uid, dentry->unix_gid,
2710 dentry->unix_mode, dentry->unix_rdev);
2713 if (!is_null_guid(dentry->object_id.object_id)) {
2714 print_guid(T("Object ID"), dentry->object_id.object_id);
2715 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2716 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2717 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2720 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2721 if (dentry->streams[i].stream_name) {
2722 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2723 dentry->streams[i].stream_name);
2724 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2725 tprintf(T("\tRaw encrypted data stream:\n"));
2726 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2727 tprintf(T("\tReparse point stream:\n"));
2729 tprintf(T("\tUnnamed data stream:\n"));
2731 print_resource(&dentry->streams[i].resource, NULL);
2736 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2738 const struct print_dentry_options *options = _options;
2739 if (!options->detailed)
2740 print_dentry_full_path(dentry);
2742 print_dentry_detailed(dentry);
2746 /* Print the files contained in an image(s) in a WIM file. */
2748 imagex_dir(int argc, tchar **argv, int cmd)
2750 const tchar *wimfile;
2751 WIMStruct *wim = NULL;
2754 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2756 struct print_dentry_options options = {
2759 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2761 STRING_LIST(refglobs);
2763 for_opt(c, dir_options) {
2765 case IMAGEX_PATH_OPTION:
2768 case IMAGEX_DETAILED_OPTION:
2769 options.detailed = true;
2771 case IMAGEX_ONE_FILE_ONLY_OPTION:
2772 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2774 case IMAGEX_REF_OPTION:
2775 ret = string_list_append(&refglobs, optarg);
2777 goto out_free_refglobs;
2787 imagex_error(T("Must specify a WIM file"));
2791 imagex_error(T("Too many arguments"));
2796 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2797 imagex_progress_func, NULL);
2799 goto out_free_refglobs;
2802 image = wimlib_resolve_image(wim, argv[1]);
2803 ret = verify_image_exists(image, argv[1], wimfile);
2805 goto out_wimlib_free;
2807 /* No image specified; default to image 1, but only if the WIM
2808 * contains exactly one image. */
2810 struct wimlib_wim_info info;
2812 wimlib_get_wim_info(wim, &info);
2813 if (info.image_count != 1) {
2814 imagex_error(T("\"%"TS"\" contains %d images; Please "
2815 "select one (or all)."),
2816 wimfile, info.image_count);
2823 if (refglobs.num_strings) {
2824 ret = wim_reference_globs(wim, &refglobs, 0);
2826 goto out_wimlib_free;
2829 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2830 print_dentry, &options);
2831 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2832 struct wimlib_wim_info info;
2834 wimlib_get_wim_info(wim, &info);
2835 do_metadata_not_found_warning(wimfile, &info);
2840 string_list_destroy(&refglobs);
2844 usage(CMD_DIR, stderr);
2846 goto out_free_refglobs;
2849 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2852 imagex_export(int argc, tchar **argv, int cmd)
2856 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2857 int write_flags = 0;
2858 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2859 const tchar *src_wimfile;
2860 const tchar *src_image_num_or_name;
2861 const tchar *dest_wimfile;
2863 const tchar *dest_name;
2864 const tchar *dest_desc;
2866 struct wimlib_wim_info src_info;
2867 WIMStruct *dest_wim;
2872 STRING_LIST(refglobs);
2873 unsigned num_threads = 0;
2874 uint32_t chunk_size = UINT32_MAX;
2875 uint32_t solid_chunk_size = UINT32_MAX;
2876 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2878 for_opt(c, export_options) {
2880 case IMAGEX_BOOT_OPTION:
2881 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2883 case IMAGEX_CHECK_OPTION:
2884 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2885 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2887 case IMAGEX_NOCHECK_OPTION:
2888 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2890 case IMAGEX_COMPRESS_OPTION:
2891 compression_type = get_compression_type(optarg, false);
2892 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2895 case IMAGEX_RECOMPRESS_OPTION:
2896 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2898 case IMAGEX_SOLID_OPTION:
2899 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2901 case IMAGEX_NO_SOLID_SORT_OPTION:
2902 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2904 case IMAGEX_CHUNK_SIZE_OPTION:
2905 chunk_size = parse_chunk_size(optarg);
2906 if (chunk_size == UINT32_MAX)
2909 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2910 solid_chunk_size = parse_chunk_size(optarg);
2911 if (solid_chunk_size == UINT32_MAX)
2914 case IMAGEX_SOLID_COMPRESS_OPTION:
2915 solid_ctype = get_compression_type(optarg, true);
2916 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2919 case IMAGEX_REF_OPTION:
2920 ret = string_list_append(&refglobs, optarg);
2922 goto out_free_refglobs;
2924 case IMAGEX_THREADS_OPTION:
2925 num_threads = parse_num_threads(optarg);
2926 if (num_threads == UINT_MAX)
2929 case IMAGEX_REBUILD_OPTION:
2930 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2932 case IMAGEX_PIPABLE_OPTION:
2933 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2935 case IMAGEX_NOT_PIPABLE_OPTION:
2936 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2938 case IMAGEX_WIMBOOT_OPTION:
2939 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2941 case IMAGEX_UNSAFE_COMPACT_OPTION:
2942 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2950 if (argc < 3 || argc > 5)
2952 src_wimfile = argv[0];
2953 src_image_num_or_name = argv[1];
2954 dest_wimfile = argv[2];
2955 dest_name = (argc >= 4) ? argv[3] : NULL;
2956 dest_desc = (argc >= 5) ? argv[4] : NULL;
2957 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2958 imagex_progress_func, NULL);
2960 goto out_free_refglobs;
2962 wimlib_get_wim_info(src_wim, &src_info);
2964 /* Determine if the destination is an existing file or not. If so, we
2965 * try to append the exported image(s) to it; otherwise, we create a new
2966 * WIM containing the exported image(s). Furthermore, determine if we
2967 * need to write a pipable WIM directly to standard output. */
2969 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2971 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2972 imagex_error("Can't write a non-pipable WIM to "
2973 "standard output! Specify --pipable\n"
2974 " if you want to create a pipable WIM "
2975 "(but read the docs first).");
2977 goto out_free_src_wim;
2980 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2982 dest_wimfile = NULL;
2983 dest_wim_fd = STDOUT_FILENO;
2984 imagex_output_to_stderr();
2985 set_fd_to_binary_mode(dest_wim_fd);
2988 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2990 /* Destination file exists. */
2992 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
2993 imagex_error(T("\"%"TS"\" is not a regular file "
2994 "or block device"), dest_wimfile);
2996 goto out_free_src_wim;
2998 ret = wimlib_open_wim_with_progress(dest_wimfile,
3000 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3002 imagex_progress_func,
3005 goto out_free_src_wim;
3007 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3008 /* The user specified a compression type, but we're
3009 * exporting to an existing WIM. Make sure the
3010 * specified compression type is the same as the
3011 * compression type of the existing destination WIM. */
3012 struct wimlib_wim_info dest_info;
3014 wimlib_get_wim_info(dest_wim, &dest_info);
3015 if (compression_type != dest_info.compression_type) {
3016 imagex_error(T("Cannot specify a compression type that is "
3017 "not the same as that used in the "
3018 "destination WIM"));
3020 goto out_free_dest_wim;
3026 if (errno != ENOENT) {
3027 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3030 goto out_free_src_wim;
3033 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3034 imagex_error(T("'--unsafe-compact' is only valid when "
3035 "exporting to an existing WIM file!"));
3037 goto out_free_src_wim;
3040 /* dest_wimfile is not an existing file, so create a new WIM. */
3042 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3043 /* The user did not specify a compression type; default
3044 * to that of the source WIM, unless --solid or
3045 * --wimboot was specified. */
3047 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3048 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3049 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3050 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3052 compression_type = src_info.compression_type;
3054 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3056 goto out_free_src_wim;
3058 wimlib_register_progress_function(dest_wim,
3059 imagex_progress_func, NULL);
3061 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3062 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3064 /* For --wimboot export, use small XPRESS chunks. */
3065 wimlib_set_output_chunk_size(dest_wim, 4096);
3066 } else if (compression_type == src_info.compression_type &&
3067 chunk_size == UINT32_MAX)
3069 /* Use same chunk size if compression type is the same. */
3070 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3074 if (chunk_size != UINT32_MAX) {
3075 /* Set destination chunk size. */
3076 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3078 goto out_free_dest_wim;
3080 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3081 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3083 goto out_free_dest_wim;
3085 if (solid_chunk_size != UINT32_MAX) {
3086 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3088 goto out_free_dest_wim;
3091 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3092 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3094 goto out_free_dest_wim;
3096 if (refglobs.num_strings) {
3097 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3099 goto out_free_dest_wim;
3102 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3103 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3105 imagex_error(T("--boot specified for all-images export, but source WIM "
3106 "has no bootable image."));
3108 goto out_free_dest_wim;
3111 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3112 dest_desc, export_flags);
3114 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3115 do_resource_not_found_warning(src_wimfile,
3116 &src_info, &refglobs);
3117 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3118 do_metadata_not_found_warning(src_wimfile, &src_info);
3120 goto out_free_dest_wim;
3124 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3125 else if (dest_wimfile)
3126 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3127 write_flags, num_threads);
3129 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3130 WIMLIB_ALL_IMAGES, write_flags,
3133 wimlib_free(dest_wim);
3135 wimlib_free(src_wim);
3137 string_list_destroy(&refglobs);
3141 usage(CMD_EXPORT, stderr);
3144 goto out_free_refglobs;
3147 /* Extract files or directories from a WIM image */
3149 imagex_extract(int argc, tchar **argv, int cmd)
3156 const tchar *wimfile;
3157 const tchar *image_num_or_name;
3158 tchar *dest_dir = T(".");
3159 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3160 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3161 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3162 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3164 STRING_LIST(refglobs);
3166 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3168 for_opt(c, extract_options) {
3170 case IMAGEX_CHECK_OPTION:
3171 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3173 case IMAGEX_VERBOSE_OPTION:
3174 /* No longer does anything. */
3176 case IMAGEX_REF_OPTION:
3177 ret = string_list_append(&refglobs, optarg);
3179 goto out_free_refglobs;
3181 case IMAGEX_UNIX_DATA_OPTION:
3182 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3184 case IMAGEX_NO_ACLS_OPTION:
3185 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3187 case IMAGEX_STRICT_ACLS_OPTION:
3188 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3190 case IMAGEX_NO_ATTRIBUTES_OPTION:
3191 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3193 case IMAGEX_DEST_DIR_OPTION:
3196 case IMAGEX_TO_STDOUT_OPTION:
3197 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3198 imagex_suppress_output();
3199 set_fd_to_binary_mode(STDOUT_FILENO);
3201 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3202 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3203 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3205 case IMAGEX_NO_GLOBS_OPTION:
3206 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3208 case IMAGEX_NULLGLOB_OPTION:
3209 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3211 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3212 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3214 case IMAGEX_WIMBOOT_OPTION:
3215 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3217 case IMAGEX_COMPACT_OPTION:
3218 ret = set_compact_mode(optarg, &extract_flags);
3220 goto out_free_refglobs;
3232 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3233 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3235 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3240 image_num_or_name = argv[1];
3245 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3246 imagex_progress_func, NULL);
3248 goto out_free_refglobs;
3250 image = wimlib_resolve_image(wim, image_num_or_name);
3251 ret = verify_image_exists_and_is_single(image,
3255 goto out_wimlib_free;
3257 if (refglobs.num_strings) {
3258 ret = wim_reference_globs(wim, &refglobs, open_flags);
3260 goto out_wimlib_free;
3266 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3269 while (argc != 0 && ret == 0) {
3273 num_paths < argc && argv[num_paths][0] != T('@');
3278 ret = wimlib_extract_paths(wim, image, dest_dir,
3279 (const tchar **)argv,
3281 extract_flags | notlist_extract_flags);
3285 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3294 imagex_printf(T("Done extracting files.\n"));
3295 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3296 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3297 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3298 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3299 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3302 T("Note: You can use the '--nullglob' "
3303 "option to ignore missing files.\n"));
3305 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3306 "files and directories\n"
3307 " are in the WIM image.\n"),
3308 get_cmd_string(CMD_DIR, false));
3309 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3310 struct wimlib_wim_info info;
3312 wimlib_get_wim_info(wim, &info);
3313 do_resource_not_found_warning(wimfile, &info, &refglobs);
3314 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3315 struct wimlib_wim_info info;
3317 wimlib_get_wim_info(wim, &info);
3318 do_metadata_not_found_warning(wimfile, &info);
3323 string_list_destroy(&refglobs);
3327 usage(CMD_EXTRACT, stderr);
3330 goto out_free_refglobs;
3333 /* Prints information about a WIM file; also can mark an image as bootable,
3334 * change the name of an image, or change the description of an image. */
3336 imagex_info(int argc, tchar **argv, int cmd)
3341 bool nocheck = false;
3342 bool header = false;
3345 bool short_header = true;
3346 const tchar *xml_out_file = NULL;
3347 const tchar *wimfile;
3348 const tchar *image_num_or_name;
3349 STRING_LIST(image_properties);
3354 struct wimlib_wim_info info;
3356 for_opt(c, info_options) {
3358 case IMAGEX_BOOT_OPTION:
3361 case IMAGEX_CHECK_OPTION:
3364 case IMAGEX_NOCHECK_OPTION:
3367 case IMAGEX_HEADER_OPTION:
3369 short_header = false;
3371 case IMAGEX_BLOBS_OPTION:
3373 short_header = false;
3375 case IMAGEX_XML_OPTION:
3377 short_header = false;
3379 case IMAGEX_EXTRACT_XML_OPTION:
3380 xml_out_file = optarg;
3381 short_header = false;
3383 case IMAGEX_IMAGE_PROPERTY_OPTION:
3384 ret = append_image_property_argument(&image_properties);
3395 if (argc < 1 || argc > 4)
3399 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3403 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3404 tsprintf(p, T("NAME=%"TS), argv[2]);
3405 ret = string_list_append(&image_properties, p);
3412 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3413 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3414 ret = string_list_append(&image_properties, p);
3419 if (check && nocheck) {
3420 imagex_error(T("Can't specify both --check and --nocheck"));
3425 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3427 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3428 imagex_progress_func, NULL);
3432 wimlib_get_wim_info(wim, &info);
3434 image = wimlib_resolve_image(wim, image_num_or_name);
3435 ret = WIMLIB_ERR_INVALID_IMAGE;
3436 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3437 verify_image_exists(image, image_num_or_name, wimfile);
3439 imagex_error(T("If you would like to set the boot "
3440 "index to 0, specify image \"0\" with "
3441 "the --boot flag."));
3443 goto out_wimlib_free;
3446 if (boot && info.image_count == 0) {
3447 imagex_error(T("--boot is meaningless on a WIM with no images"));
3448 goto out_wimlib_free;
3451 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3453 imagex_error(T("Cannot specify the --boot flag "
3454 "without specifying a specific "
3455 "image in a multi-image WIM"));
3456 goto out_wimlib_free;
3458 if (image_properties.num_strings) {
3459 imagex_error(T("Can't change image properties without "
3460 "specifying a specific image in a "
3461 "multi-image WIM"));
3462 goto out_wimlib_free;
3466 /* Operations that print information are separated from operations that
3467 * recreate the WIM file. */
3468 if (!image_properties.num_strings && !boot) {
3470 /* Read-only operations */
3472 if (image == WIMLIB_NO_IMAGE) {
3473 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3474 image_num_or_name, wimfile);
3475 goto out_wimlib_free;
3478 if (image == WIMLIB_ALL_IMAGES && short_header)
3479 print_wim_information(wimfile, &info);
3482 wimlib_print_header(wim);
3485 if (info.total_parts != 1) {
3486 tfprintf(stderr, T("Warning: Only showing the blobs "
3487 "for part %d of a %d-part WIM.\n"),
3488 info.part_number, info.total_parts);
3494 ret = wimlib_extract_xml_data(wim, stdout);
3496 goto out_wimlib_free;
3502 fp = tfopen(xml_out_file, T("wb"));
3504 imagex_error_with_errno(T("Failed to open the "
3505 "file \"%"TS"\" for "
3509 goto out_wimlib_free;
3511 ret = wimlib_extract_xml_data(wim, fp);
3513 imagex_error(T("Failed to close the file "
3519 goto out_wimlib_free;
3523 wimlib_print_available_images(wim, image);
3527 /* Modification operations */
3528 bool any_property_changes;
3530 if (image == WIMLIB_ALL_IMAGES)
3533 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3534 imagex_error(T("Cannot change image properties "
3535 "when using image 0"));
3537 goto out_wimlib_free;
3541 if (image == info.boot_index) {
3542 imagex_printf(T("Image %d is already marked as "
3543 "bootable.\n"), image);
3546 imagex_printf(T("Marking image %d as bootable.\n"),
3548 info.boot_index = image;
3549 ret = wimlib_set_wim_info(wim, &info,
3550 WIMLIB_CHANGE_BOOT_INDEX);
3552 goto out_wimlib_free;
3556 ret = apply_image_properties(&image_properties, wim, image,
3557 &any_property_changes);
3559 goto out_wimlib_free;
3561 /* Only call wimlib_overwrite() if something actually needs to
3563 if (boot || any_property_changes ||
3564 (check && !info.has_integrity_table) ||
3565 (nocheck && info.has_integrity_table))
3567 int write_flags = 0;
3570 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3572 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3573 ret = wimlib_overwrite(wim, write_flags, 1);
3575 imagex_printf(T("The file \"%"TS"\" was not modified "
3576 "because nothing needed to be done.\n"),
3584 string_list_destroy(&image_properties);
3588 usage(CMD_INFO, stderr);
3594 /* Join split WIMs into one part WIM */
3596 imagex_join(int argc, tchar **argv, int cmd)
3599 int swm_open_flags = 0;
3600 int wim_write_flags = 0;
3601 const tchar *output_path;
3604 for_opt(c, join_options) {
3606 case IMAGEX_CHECK_OPTION:
3607 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3608 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3618 imagex_error(T("Must specify one or more split WIM (.swm) "
3622 output_path = argv[0];
3623 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3628 imagex_progress_func,
3634 usage(CMD_JOIN, stderr);
3639 #if WIM_MOUNTING_SUPPORTED
3641 /* Mounts a WIM image. */
3643 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3646 int mount_flags = 0;
3648 const tchar *staging_dir = NULL;
3649 const tchar *wimfile;
3652 struct wimlib_wim_info info;
3656 STRING_LIST(refglobs);
3658 if (cmd == CMD_MOUNTRW) {
3659 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3660 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3663 for_opt(c, mount_options) {
3665 case IMAGEX_ALLOW_OTHER_OPTION:
3666 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3668 case IMAGEX_CHECK_OPTION:
3669 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3671 case IMAGEX_DEBUG_OPTION:
3672 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3674 case IMAGEX_STREAMS_INTERFACE_OPTION:
3675 if (!tstrcasecmp(optarg, T("none")))
3676 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3677 else if (!tstrcasecmp(optarg, T("xattr")))
3678 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3679 else if (!tstrcasecmp(optarg, T("windows")))
3680 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3682 imagex_error(T("Unknown stream interface \"%"TS"\""),
3687 case IMAGEX_REF_OPTION:
3688 ret = string_list_append(&refglobs, optarg);
3690 goto out_free_refglobs;
3692 case IMAGEX_STAGING_DIR_OPTION:
3693 staging_dir = optarg;
3695 case IMAGEX_UNIX_DATA_OPTION:
3696 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3704 if (argc != 2 && argc != 3)
3709 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3710 imagex_progress_func, NULL);
3712 goto out_free_refglobs;
3714 wimlib_get_wim_info(wim, &info);
3717 /* Image explicitly specified. */
3718 image = wimlib_resolve_image(wim, argv[1]);
3720 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3724 /* No image specified; default to image 1, but only if the WIM
3725 * contains exactly one image. */
3727 if (info.image_count != 1) {
3728 imagex_error(T("\"%"TS"\" contains %d images; Please "
3729 "select one."), wimfile, info.image_count);
3737 if (refglobs.num_strings) {
3738 ret = wim_reference_globs(wim, &refglobs, open_flags);
3743 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3745 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3746 do_metadata_not_found_warning(wimfile, &info);
3748 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3750 image, wimfile, dir);
3756 string_list_destroy(&refglobs);
3762 goto out_free_refglobs;
3764 #endif /* WIM_MOUNTING_SUPPORTED */
3766 /* Rebuild a WIM file */
3768 imagex_optimize(int argc, tchar **argv, int cmd)
3771 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3772 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3773 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3774 uint32_t chunk_size = UINT32_MAX;
3775 uint32_t solid_chunk_size = UINT32_MAX;
3776 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3779 const tchar *wimfile;
3782 unsigned num_threads = 0;
3784 for_opt(c, optimize_options) {
3786 case IMAGEX_CHECK_OPTION:
3787 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3788 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3790 case IMAGEX_NOCHECK_OPTION:
3791 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3793 case IMAGEX_COMPRESS_OPTION:
3794 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3795 compression_type = get_compression_type(optarg, false);
3796 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3799 case IMAGEX_RECOMPRESS_OPTION:
3800 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3802 case IMAGEX_CHUNK_SIZE_OPTION:
3803 chunk_size = parse_chunk_size(optarg);
3804 if (chunk_size == UINT32_MAX)
3807 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3808 solid_chunk_size = parse_chunk_size(optarg);
3809 if (solid_chunk_size == UINT32_MAX)
3812 case IMAGEX_SOLID_COMPRESS_OPTION:
3813 solid_ctype = get_compression_type(optarg, true);
3814 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3817 case IMAGEX_SOLID_OPTION:
3818 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3819 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3821 case IMAGEX_NO_SOLID_SORT_OPTION:
3822 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3824 case IMAGEX_THREADS_OPTION:
3825 num_threads = parse_num_threads(optarg);
3826 if (num_threads == UINT_MAX)
3829 case IMAGEX_PIPABLE_OPTION:
3830 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3832 case IMAGEX_NOT_PIPABLE_OPTION:
3833 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3835 case IMAGEX_UNSAFE_COMPACT_OPTION:
3836 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3850 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3851 imagex_progress_func, NULL);
3855 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3856 /* Change compression type. */
3857 ret = wimlib_set_output_compression_type(wim, compression_type);
3859 goto out_wimlib_free;
3862 if (chunk_size != UINT32_MAX) {
3863 /* Change chunk size. */
3864 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3866 goto out_wimlib_free;
3868 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3869 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3871 goto out_wimlib_free;
3873 if (solid_chunk_size != UINT32_MAX) {
3874 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3876 goto out_wimlib_free;
3879 old_size = file_get_size(wimfile);
3880 tprintf(T("\"%"TS"\" original size: "), wimfile);
3882 tputs(T("Unknown"));
3884 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3886 ret = wimlib_overwrite(wim, write_flags, num_threads);
3888 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3889 goto out_wimlib_free;
3892 new_size = file_get_size(wimfile);
3893 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3895 tputs(T("Unknown"));
3897 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3899 tfputs(T("Space saved: "), stdout);
3900 if (new_size != -1 && old_size != -1) {
3901 tprintf(T("%lld KiB\n"),
3902 ((long long)old_size - (long long)new_size) >> 10);
3904 tputs(T("Unknown"));
3913 usage(CMD_OPTIMIZE, stderr);
3919 /* Split a WIM into a spanned set */
3921 imagex_split(int argc, tchar **argv, int cmd)
3925 int write_flags = 0;
3926 unsigned long part_size;
3931 for_opt(c, split_options) {
3933 case IMAGEX_CHECK_OPTION:
3934 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3935 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3947 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3948 if (tmp == argv[2] || *tmp) {
3949 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3950 imagex_error(T("The part size must be an integer or "
3951 "floating-point number of megabytes."));
3954 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3955 imagex_progress_func, NULL);
3959 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3965 usage(CMD_SPLIT, stderr);
3971 #if WIM_MOUNTING_SUPPORTED
3972 /* Unmounts a mounted WIM image. */
3974 imagex_unmount(int argc, tchar **argv, int cmd)
3977 int unmount_flags = 0;
3980 for_opt(c, unmount_options) {
3982 case IMAGEX_COMMIT_OPTION:
3983 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3985 case IMAGEX_CHECK_OPTION:
3986 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3988 case IMAGEX_REBUILD_OPTION:
3989 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3991 case IMAGEX_LAZY_OPTION:
3992 case IMAGEX_FORCE_OPTION:
3993 /* Now, unmount is lazy by default. However, committing
3994 * the image will fail with
3995 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3996 * file descriptors on the WIM image. The
3997 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3998 * descriptors to be closed. */
3999 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4001 case IMAGEX_NEW_IMAGE_OPTION:
4002 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4013 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4014 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4015 imagex_error(T("--new-image is meaningless "
4016 "without --commit also specified!"));
4021 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4022 imagex_progress_func, NULL);
4024 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4025 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4027 "\tNote: Use --commit --force to force changes "
4028 "to be committed, regardless\n"
4029 "\t of open files.\n"));
4036 usage(CMD_UNMOUNT, stderr);
4041 #endif /* WIM_MOUNTING_SUPPORTED */
4044 * Add, delete, or rename files in a WIM image.
4047 imagex_update(int argc, tchar **argv, int cmd)
4049 const tchar *wimfile;
4053 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4054 int write_flags = 0;
4055 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4056 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4057 WIMLIB_ADD_FLAG_VERBOSE |
4058 WIMLIB_ADD_FLAG_WINCONFIG;
4059 int default_delete_flags = 0;
4060 unsigned num_threads = 0;
4062 tchar *cmd_file_contents;
4063 size_t cmd_file_nchars;
4064 struct wimlib_update_command *cmds;
4066 tchar *command_str = NULL;
4067 tchar *config_file = NULL;
4068 tchar *wimboot_config = NULL;
4070 for_opt(c, update_options) {
4072 /* Generic or write options */
4073 case IMAGEX_THREADS_OPTION:
4074 num_threads = parse_num_threads(optarg);
4075 if (num_threads == UINT_MAX)
4078 case IMAGEX_CHECK_OPTION:
4079 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4080 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4082 case IMAGEX_REBUILD_OPTION:
4083 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4085 case IMAGEX_COMMAND_OPTION:
4087 imagex_error(T("--command may only be specified "
4088 "one time. Please provide\n"
4089 " the update commands "
4090 "on standard input instead."));
4093 command_str = tstrdup(optarg);
4095 imagex_error(T("Out of memory!"));
4099 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4100 wimboot_config = optarg;
4102 /* Default delete options */
4103 case IMAGEX_FORCE_OPTION:
4104 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4106 case IMAGEX_RECURSIVE_OPTION:
4107 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4110 /* Global add option */
4111 case IMAGEX_CONFIG_OPTION:
4112 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4113 config_file = optarg;
4116 /* Default add options */
4117 case IMAGEX_VERBOSE_OPTION:
4118 /* No longer does anything. */
4120 case IMAGEX_DEREFERENCE_OPTION:
4121 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4123 case IMAGEX_UNIX_DATA_OPTION:
4124 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4126 case IMAGEX_NO_ACLS_OPTION:
4127 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4129 case IMAGEX_STRICT_ACLS_OPTION:
4130 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4132 case IMAGEX_NO_REPLACE_OPTION:
4133 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4135 case IMAGEX_UNSAFE_COMPACT_OPTION:
4136 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4145 if (argc != 1 && argc != 2)
4149 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4150 imagex_progress_func, NULL);
4152 goto out_free_command_str;
4155 /* Image explicitly specified. */
4156 image = wimlib_resolve_image(wim, argv[1]);
4157 ret = verify_image_exists_and_is_single(image, argv[1],
4160 goto out_wimlib_free;
4162 /* No image specified; default to image 1, but only if the WIM
4163 * contains exactly one image. */
4164 struct wimlib_wim_info info;
4166 wimlib_get_wim_info(wim, &info);
4167 if (info.image_count != 1) {
4168 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4169 wimfile, info.image_count);
4176 /* Read update commands from standard input, or the command string if
4179 cmd_file_contents = NULL;
4180 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4184 goto out_free_cmd_file_contents;
4186 } else if (!wimboot_config) {
4187 if (isatty(STDIN_FILENO)) {
4188 tputs(T("Reading update commands from standard input..."));
4189 recommend_man_page(CMD_UPDATE, stdout);
4191 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4192 if (!cmd_file_contents) {
4194 goto out_wimlib_free;
4197 /* Parse the update commands */
4198 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4202 goto out_free_cmd_file_contents;
4205 cmd_file_contents = NULL;
4210 /* Set default flags and capture config on the update commands */
4211 for (size_t i = 0; i < num_cmds; i++) {
4212 switch (cmds[i].op) {
4213 case WIMLIB_UPDATE_OP_ADD:
4214 cmds[i].add.add_flags |= default_add_flags;
4215 cmds[i].add.config_file = config_file;
4217 case WIMLIB_UPDATE_OP_DELETE:
4218 cmds[i].delete_.delete_flags |= default_delete_flags;
4225 /* Execute the update commands */
4226 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4230 if (wimboot_config) {
4231 /* --wimboot-config=FILE is short for an
4232 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4234 struct wimlib_update_command cmd;
4236 cmd.op = WIMLIB_UPDATE_OP_ADD;
4237 cmd.add.fs_source_path = wimboot_config;
4238 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4239 cmd.add.config_file = NULL;
4240 cmd.add.add_flags = 0;
4242 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4247 /* Overwrite the updated WIM */
4248 ret = wimlib_overwrite(wim, write_flags, num_threads);
4251 out_free_cmd_file_contents:
4252 free(cmd_file_contents);
4255 out_free_command_str:
4260 usage(CMD_UPDATE, stderr);
4263 goto out_free_command_str;
4266 /* Verify a WIM file. */
4268 imagex_verify(int argc, tchar **argv, int cmd)
4271 const tchar *wimfile;
4273 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4274 int verify_flags = 0;
4275 STRING_LIST(refglobs);
4278 for_opt(c, verify_options) {
4280 case IMAGEX_REF_OPTION:
4281 ret = string_list_append(&refglobs, optarg);
4283 goto out_free_refglobs;
4285 case IMAGEX_NOCHECK_OPTION:
4286 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4298 imagex_error(T("Must specify a WIM file!"));
4300 imagex_error(T("At most one WIM file can be specified!"));
4306 ret = wimlib_open_wim_with_progress(wimfile,
4309 imagex_progress_func,
4312 goto out_free_refglobs;
4314 ret = wim_reference_globs(wim, &refglobs, open_flags);
4316 goto out_wimlib_free;
4318 ret = wimlib_verify_wim(wim, verify_flags);
4320 tputc(T('\n'), stderr);
4321 imagex_error(T("\"%"TS"\" failed verification!"),
4323 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4324 refglobs.num_strings == 0)
4326 imagex_printf(T("Note: if this WIM file is not standalone, "
4327 "use the --ref option to specify the other parts.\n"));
4330 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4337 string_list_destroy(&refglobs);
4341 usage(CMD_VERIFY, stderr);
4343 goto out_free_refglobs;
4346 struct imagex_command {
4348 int (*func)(int argc, tchar **argv, int cmd);
4351 static const struct imagex_command imagex_commands[] = {
4352 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4353 [CMD_APPLY] = {T("apply"), imagex_apply},
4354 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4355 [CMD_DELETE] = {T("delete"), imagex_delete},
4356 [CMD_DIR ] = {T("dir"), imagex_dir},
4357 [CMD_EXPORT] = {T("export"), imagex_export},
4358 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4359 [CMD_INFO] = {T("info"), imagex_info},
4360 [CMD_JOIN] = {T("join"), imagex_join},
4361 #if WIM_MOUNTING_SUPPORTED
4362 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4363 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4365 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4366 [CMD_SPLIT] = {T("split"), imagex_split},
4367 #if WIM_MOUNTING_SUPPORTED
4368 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4370 [CMD_UPDATE] = {T("update"), imagex_update},
4371 [CMD_VERIFY] = {T("verify"), imagex_verify},
4376 /* Can be a directory or source list file. But source list file is probably
4377 * a rare use case, so just say directory. */
4378 # define SOURCE_STR T("DIRECTORY")
4380 /* Can only be a directory */
4381 # define TARGET_STR T("DIRECTORY")
4384 /* Can be a directory, NTFS volume, or source list file. */
4385 # define SOURCE_STR T("SOURCE")
4387 /* Can be a directory or NTFS volume. */
4388 # define TARGET_STR T("TARGET")
4392 static const tchar * const usage_strings[] = {
4395 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4396 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4397 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4398 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4399 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4400 " [--dereference] [--snapshot]\n"
4404 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4405 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4406 " [--no-attributes] [--rpfix] [--norpfix]\n"
4407 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4408 " [--compact=FORMAT]\n"
4412 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4413 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4414 " [--config=FILE] [--threads=NUM_THREADS]\n"
4415 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4416 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4417 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4422 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4426 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4430 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4431 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4432 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4433 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4434 " [--wimboot] [--solid]\n"
4438 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4439 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4440 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4441 " [--no-attributes] [--include-invalid-names]\n"
4442 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4446 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4447 " [--boot] [--check] [--nocheck] [--xml]\n"
4448 " [--extract-xml FILE] [--header] [--blobs]\n"
4449 " [--image-property NAME=VALUE]\n"
4453 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4455 #if WIM_MOUNTING_SUPPORTED
4458 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4459 " [--check] [--streams-interface=INTERFACE]\n"
4460 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4464 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4465 " [--check] [--streams-interface=INTERFACE]\n"
4466 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4472 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4473 " [--check] [--nocheck] [--solid]\n"
4478 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4480 #if WIM_MOUNTING_SUPPORTED
4483 " %"TS" DIRECTORY\n"
4484 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4489 " %"TS" WIMFILE [IMAGE]\n"
4490 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4491 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4492 " [--command=STRING] [--wimboot-config=FILE]\n"
4497 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4501 static const tchar *invocation_name;
4502 static int invocation_cmd = CMD_NONE;
4504 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4506 static tchar buf[50];
4508 if (cmd == CMD_NONE)
4509 return T("wimlib-imagex");
4511 if (only_short_form || invocation_cmd != CMD_NONE) {
4512 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4514 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4515 imagex_commands[cmd].name);
4523 static const tchar * const s =
4525 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4526 "Copyright (C) 2012-2017 Eric Biggers\n"
4527 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4528 "This is free software: you are free to change and redistribute it.\n"
4529 "There is NO WARRANTY, to the extent permitted by law.\n"
4531 "Report bugs to "PACKAGE_BUGREPORT".\n"
4538 do_common_options(int *argc_p, tchar **argv, int cmd)
4544 for (i = 1; i < argc; i++) {
4546 if (p[0] == T('-') && p[1] == T('-')) {
4548 if (!tstrcmp(p, T("help"))) {
4549 if (cmd == CMD_NONE)
4554 } else if (!tstrcmp(p, T("version"))) {
4557 } else if (!tstrcmp(p, T("quiet"))) {
4558 imagex_suppress_output();
4559 memmove(&argv[i], &argv[i + 1],
4560 (argc - i) * sizeof(argv[i]));
4563 } else if (!*p) /* reached "--", no more options */
4572 print_usage_string(int cmd, FILE *fp)
4574 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4578 recommend_man_page(int cmd, FILE *fp)
4580 const tchar *format_str;
4582 format_str = T("Some uncommon options are not listed;\n"
4583 "See %"TS".pdf in the doc directory for more details.\n");
4585 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4587 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4591 usage(int cmd, FILE *fp)
4593 tfprintf(fp, T("Usage:\n"));
4594 print_usage_string(cmd, fp);
4595 tfprintf(fp, T("\n"));
4596 recommend_man_page(cmd, fp);
4602 tfprintf(fp, T("Usage:\n"));
4603 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4604 print_usage_string(cmd, fp);
4605 tfprintf(fp, T("\n"));
4607 static const tchar * const extra =
4610 " %"TS" --version\n"
4613 tfprintf(fp, extra, invocation_name, invocation_name);
4615 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4616 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4617 "For some commands IMAGE may be \"all\".\n"
4619 recommend_man_page(CMD_NONE, fp);
4623 extern int wmain(int argc, wchar_t **argv);
4627 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4628 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4629 * something else), while on Windows the command arguments will be UTF-16LE
4630 * encoded 'wchar_t' strings. */
4632 main(int argc, tchar **argv)
4638 imagex_info_file = stdout;
4639 invocation_name = tbasename(argv[0]);
4642 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4643 if (igcase != NULL) {
4644 if (!tstrcmp(igcase, T("no")) ||
4645 !tstrcmp(igcase, T("0")))
4646 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4647 else if (!tstrcmp(igcase, T("yes")) ||
4648 !tstrcmp(igcase, T("1")))
4649 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4652 "WARNING: Ignoring unknown setting of "
4653 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4658 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4660 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4661 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4662 for (int i = 0; i < CMD_MAX; i++) {
4663 if (!tstrcmp(invocation_name + 3,
4664 imagex_commands[i].name))
4673 /* Unless already known from the invocation name, determine which
4674 * command was specified. */
4675 if (cmd == CMD_NONE) {
4677 imagex_error(T("No command specified!\n"));
4681 for (int i = 0; i < CMD_MAX; i++) {
4682 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4687 if (cmd != CMD_NONE) {
4693 /* Handle common options. May exit early (for --help or --version). */
4694 do_common_options(&argc, argv, cmd);
4696 /* Bail if a valid command was not specified. */
4697 if (cmd == CMD_NONE) {
4698 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4703 /* Enable warning and error messages in wimlib to be more user-friendly.
4705 wimlib_set_print_errors(true);
4707 /* Initialize wimlib. */
4708 ret = wimlib_global_init(init_flags);
4710 goto out_check_status;
4712 /* Call the command handler function. */
4713 ret = imagex_commands[cmd].func(argc, argv, cmd);
4715 /* Check for error writing to standard output, especially since for some
4716 * commands, writing to standard output is part of the program's actual
4717 * behavior and not just for informational purposes. */
4718 if (ferror(stdout) || fclose(stdout)) {
4719 imagex_error_with_errno(T("error writing to standard output"));
4724 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4725 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4726 * error code from which an error message can be printed. */
4728 imagex_error(T("Exiting with error code %d:\n"
4730 wimlib_get_error_string(ret));
4731 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4732 imagex_error_with_errno(T("errno"));
4734 /* Make wimlib free any resources it's holding (although this is not
4735 * strictly necessary because the process is ending anyway). */
4736 wimlib_global_cleanup();