4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012-2018 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 /* NetBSD is missing getopt_long_only() but has getopt_long() */
50 #ifndef HAVE_GETOPT_LONG_ONLY
51 # define getopt_long_only getopt_long
54 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
57 # include "imagex-win32.h"
58 # define print_security_descriptor win32_print_security_descriptor
61 # include <langinfo.h>
62 # define print_security_descriptor default_print_security_descriptor
63 static inline void set_fd_to_binary_mode(int fd)
68 /* Don't confuse the user by presenting the mounting commands on Windows when
69 * they will never work. However on UNIX-like systems we always present them,
70 * even if WITH_FUSE is not defined at this point, as to not tie the build of
71 * wimlib-imagex to a specific build of wimlib. */
73 # define WIM_MOUNTING_SUPPORTED 0
75 # define WIM_MOUNTING_SUPPORTED 1
78 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
81 is_any_path_separator(tchar c)
83 return c == T('/') || c == T('\\');
86 /* Like basename(), but handles both forward and backwards slashes. */
88 tbasename(tchar *path)
90 tchar *p = tstrchr(path, T('\0'));
95 if (!is_any_path_separator(*--p))
103 if (is_any_path_separator(*--p))
108 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
122 #if WIM_MOUNTING_SUPPORTED
128 #if WIM_MOUNTING_SUPPORTED
136 static void usage(int cmd, FILE *fp);
137 static void usage_all(FILE *fp);
138 static void recommend_man_page(int cmd, FILE *fp);
139 static const tchar *get_cmd_string(int cmd, bool only_short_form);
141 static FILE *imagex_info_file;
143 #define imagex_printf(format, ...) \
144 if (imagex_info_file) \
145 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
147 static void imagex_suppress_output(void)
149 imagex_info_file = NULL;
152 static void imagex_output_to_stderr(void)
154 if (imagex_info_file)
155 imagex_info_file = stderr;
158 static void imagex_flush_output(void)
160 if (imagex_info_file)
161 fflush(imagex_info_file);
165 IMAGEX_ALLOW_OTHER_OPTION,
169 IMAGEX_CHUNK_SIZE_OPTION,
170 IMAGEX_COMMAND_OPTION,
171 IMAGEX_COMMIT_OPTION,
172 IMAGEX_COMPACT_OPTION,
173 IMAGEX_COMPRESS_OPTION,
174 IMAGEX_CONFIG_OPTION,
176 IMAGEX_DELTA_FROM_OPTION,
177 IMAGEX_DEREFERENCE_OPTION,
178 IMAGEX_DEST_DIR_OPTION,
179 IMAGEX_DETAILED_OPTION,
180 IMAGEX_EXTRACT_XML_OPTION,
183 IMAGEX_HEADER_OPTION,
184 IMAGEX_IMAGE_PROPERTY_OPTION,
185 IMAGEX_INCLUDE_INTEGRITY_OPTION,
186 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
188 IMAGEX_METADATA_OPTION,
189 IMAGEX_NEW_IMAGE_OPTION,
190 IMAGEX_NOCHECK_OPTION,
191 IMAGEX_NORPFIX_OPTION,
192 IMAGEX_NOT_PIPABLE_OPTION,
193 IMAGEX_NO_ACLS_OPTION,
194 IMAGEX_NO_ATTRIBUTES_OPTION,
195 IMAGEX_NO_GLOBS_OPTION,
196 IMAGEX_NO_REPLACE_OPTION,
197 IMAGEX_NO_SOLID_SORT_OPTION,
198 IMAGEX_NULLGLOB_OPTION,
199 IMAGEX_ONE_FILE_ONLY_OPTION,
201 IMAGEX_PIPABLE_OPTION,
202 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
203 IMAGEX_REBUILD_OPTION,
204 IMAGEX_RECOMPRESS_OPTION,
205 IMAGEX_RECURSIVE_OPTION,
208 IMAGEX_SNAPSHOT_OPTION,
210 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
211 IMAGEX_SOLID_COMPRESS_OPTION,
213 IMAGEX_SOURCE_LIST_OPTION,
214 IMAGEX_STAGING_DIR_OPTION,
215 IMAGEX_STREAMS_INTERFACE_OPTION,
216 IMAGEX_STRICT_ACLS_OPTION,
217 IMAGEX_THREADS_OPTION,
218 IMAGEX_TO_STDOUT_OPTION,
219 IMAGEX_UNIX_DATA_OPTION,
220 IMAGEX_UNSAFE_COMPACT_OPTION,
221 IMAGEX_UPDATE_OF_OPTION,
222 IMAGEX_VERBOSE_OPTION,
223 IMAGEX_WIMBOOT_CONFIG_OPTION,
224 IMAGEX_WIMBOOT_OPTION,
228 static const struct option apply_options[] = {
229 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
230 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
231 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
232 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
233 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
234 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
235 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
236 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
237 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
238 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
239 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
240 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
241 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
245 static const struct option capture_or_append_options[] = {
246 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
247 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
248 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
249 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
250 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
251 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
252 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
253 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
254 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
255 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
256 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
257 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
258 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
259 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
260 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
261 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
262 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
263 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
264 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
265 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
266 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
267 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
268 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
269 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
270 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
271 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
272 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
273 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
274 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
275 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
276 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
277 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
281 static const struct option delete_options[] = {
282 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
283 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
284 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
285 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
289 static const struct option dir_options[] = {
290 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
291 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
292 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
293 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
297 static const struct option export_options[] = {
298 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
299 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
300 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
301 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
302 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
303 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
304 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
305 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
306 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
307 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
308 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
309 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
310 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
311 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
312 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
313 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
314 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
315 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
316 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
320 static const struct option extract_options[] = {
321 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
322 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
323 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
324 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
325 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
326 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
327 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
328 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
329 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
330 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
331 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
332 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
333 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
334 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
335 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
336 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
337 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
341 static const struct option info_options[] = {
342 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
343 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
344 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
345 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
346 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
347 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
348 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
349 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
350 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
351 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
352 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
356 static const struct option join_options[] = {
357 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
358 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
362 #if WIM_MOUNTING_SUPPORTED
363 static const struct option mount_options[] = {
364 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
365 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
366 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
367 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
368 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
369 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
370 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
375 static const struct option optimize_options[] = {
376 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
377 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
378 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
379 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
380 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
381 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
382 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
383 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
384 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
385 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
386 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
387 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
388 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
389 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
390 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
394 static const struct option split_options[] = {
395 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
396 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
400 #if WIM_MOUNTING_SUPPORTED
401 static const struct option unmount_options[] = {
402 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
403 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
404 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
405 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
406 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
407 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
412 static const struct option update_options[] = {
413 /* Careful: some of the options here set the defaults for update
414 * commands, but the flags given to an actual update command (and not to
415 * `imagex update' itself are also handled in
416 * update_command_add_option(). */
417 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
418 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
419 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
420 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
421 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
422 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
424 /* Default delete options */
425 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
426 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
428 /* Global add option */
429 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
431 /* Default add options */
432 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
433 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
434 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
435 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
436 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
437 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
438 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
439 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
444 static const struct option verify_options[] = {
445 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
446 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
452 # define _format_attribute(type, format_str, args_start) \
453 __attribute__((format(type, format_str, args_start)))
455 # define _format_attribute(type, format_str, args_start)
458 /* Print formatted error message to stderr. */
459 static void _format_attribute(printf, 1, 2)
460 imagex_error(const tchar *format, ...)
463 va_start(va, format);
464 tfputs(T("ERROR: "), stderr);
465 tvfprintf(stderr, format, va);
466 tputc(T('\n'), stderr);
470 /* Print formatted error message to stderr. */
471 static void _format_attribute(printf, 1, 2)
472 imagex_error_with_errno(const tchar *format, ...)
474 int errno_save = errno;
476 va_start(va, format);
477 tfputs(T("ERROR: "), stderr);
478 tvfprintf(stderr, format, va);
479 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
484 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
486 if (image == WIMLIB_NO_IMAGE) {
487 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
488 " Please specify a 1-based image index or "
489 "image name. To list the images\n"
490 " contained in the WIM archive, run\n"
492 " %"TS" \"%"TS"\"\n"),
493 image_name, wim_name,
494 get_cmd_string(CMD_INFO, false), wim_name);
495 return WIMLIB_ERR_INVALID_IMAGE;
501 verify_image_is_single(int image)
503 if (image == WIMLIB_ALL_IMAGES) {
504 imagex_error(T("Cannot specify all images for this action!"));
505 return WIMLIB_ERR_INVALID_IMAGE;
511 verify_image_exists_and_is_single(int image, const tchar *image_name,
512 const tchar *wim_name)
515 ret = verify_image_exists(image, image_name, wim_name);
517 ret = verify_image_is_single(image);
522 print_available_compression_types(FILE *fp)
524 static const tchar * const s =
526 "Available compression types:\n"
529 " xpress (alias: \"fast\")\n"
530 " lzx (alias: \"maximum\") (default for capture)\n"
531 " lzms (alias: \"recovery\")\n"
537 /* Parse the argument to --compress or --solid-compress */
539 get_compression_type(tchar *optarg, bool solid)
542 unsigned int compression_level = 0;
545 plevel = tstrchr(optarg, T(':'));
551 ultmp = tstrtoul(plevel, &ptmp, 10);
552 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
553 imagex_error(T("Compression level must be a positive integer! "
554 "e.g. --compress=lzx:80"));
555 return WIMLIB_COMPRESSION_TYPE_INVALID;
557 compression_level = ultmp;
560 if (!tstrcasecmp(optarg, T("maximum")) ||
561 !tstrcasecmp(optarg, T("lzx")) ||
562 !tstrcasecmp(optarg, T("max"))) {
563 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
564 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
565 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
566 } else if (!tstrcasecmp(optarg, T("recovery"))) {
570 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
571 " differently from DISM. Instead, you typically want to use '--solid' to\n"
572 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
573 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
574 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
575 " of '--compress=recovery'.\n"));
577 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
578 } else if (!tstrcasecmp(optarg, T("lzms"))) {
579 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
580 } else if (!tstrcasecmp(optarg, T("none"))) {
581 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
583 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
584 print_available_compression_types(stderr);
585 return WIMLIB_COMPRESSION_TYPE_INVALID;
588 if (compression_level != 0)
589 wimlib_set_default_compression_level(ctype, compression_level);
593 /* Parse the argument to --compact */
595 set_compact_mode(const tchar *arg, int *extract_flags)
598 if (!tstrcasecmp(arg, T("xpress4k")))
599 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
600 else if (!tstrcasecmp(arg, T("xpress8k")))
601 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
602 else if (!tstrcasecmp(arg, T("xpress16k")))
603 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
604 else if (!tstrcasecmp(arg, T("lzx")))
605 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
608 *extract_flags |= flag;
613 "\"%"TS"\" is not a recognized System Compression format. The options are:"
615 " --compact=xpress4k\n"
616 " --compact=xpress8k\n"
617 " --compact=xpress16k\n"
626 unsigned num_strings;
627 unsigned num_alloc_strings;
630 #define STRING_LIST_INITIALIZER \
631 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
633 #define STRING_LIST(_strings) \
634 struct string_list _strings = STRING_LIST_INITIALIZER
637 string_list_append(struct string_list *list, tchar *glob)
639 unsigned num_alloc_strings = list->num_alloc_strings;
641 if (list->num_strings == num_alloc_strings) {
644 num_alloc_strings += 4;
645 new_strings = realloc(list->strings,
646 sizeof(list->strings[0]) * num_alloc_strings);
648 imagex_error(T("Out of memory!"));
651 list->strings = new_strings;
652 list->num_alloc_strings = num_alloc_strings;
654 list->strings[list->num_strings++] = glob;
659 string_list_destroy(struct string_list *list)
665 wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags)
667 return wimlib_reference_resource_files(wim, (const tchar **)list->strings,
669 WIMLIB_REF_FLAG_GLOB_ENABLE,
674 append_image_property_argument(struct string_list *image_properties)
676 if (!tstrchr(optarg, '=')) {
677 imagex_error(T("'--image-property' argument "
678 "must be in the form NAME=VALUE"));
681 return string_list_append(image_properties, optarg);
685 apply_image_properties(struct string_list *image_properties,
686 WIMStruct *wim, int image, bool *any_changes_ret)
688 bool any_changes = false;
689 for (unsigned i = 0; i < image_properties->num_strings; i++) {
691 const tchar *current_value;
694 name = image_properties->strings[i];
695 value = tstrchr(name, '=');
698 current_value = wimlib_get_image_property(wim, image, name);
699 if (current_value && !tstrcmp(current_value, value)) {
700 imagex_printf(T("The %"TS" property of image %d "
701 "already has value \"%"TS"\".\n"),
704 imagex_printf(T("Setting the %"TS" property of image "
705 "%d to \"%"TS"\".\n"),
707 ret = wimlib_set_image_property(wim, image, name, value);
714 *any_changes_ret = any_changes;
719 do_resource_not_found_warning(const tchar *wimfile,
720 const struct wimlib_wim_info *info,
721 const struct string_list *refglobs)
723 if (info->total_parts > 1) {
724 if (refglobs->num_strings == 0) {
725 imagex_error(T("\"%"TS"\" is part of a split WIM. "
726 "Use --ref to specify the other parts."),
729 imagex_error(T("Perhaps the '--ref' argument did not "
730 "specify all other parts of the split "
734 imagex_error(T("If this is a delta WIM, use the --ref argument "
735 "to specify the WIM(s) on which it is based."));
740 do_metadata_not_found_warning(const tchar *wimfile,
741 const struct wimlib_wim_info *info)
743 if (info->part_number != 1) {
744 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
745 " You must specify the first part."),
750 /* Returns the size of a file given its name, or -1 if the file does not exist
751 * or its size cannot be determined. */
753 file_get_size(const tchar *filename)
756 if (tstat(filename, &st) == 0)
763 PARSE_STRING_SUCCESS = 0,
764 PARSE_STRING_FAILURE = 1,
765 PARSE_STRING_NONE = 2,
769 * Parses a string token from an array of characters.
771 * Tokens are either whitespace-delimited, or double or single-quoted.
773 * @line_p: Pointer to the pointer to the line of data. Will be updated
774 * to point past the string token iff the return value is
775 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
778 * @len_p: @len_p initially stores the length of the line of data, which may
779 * be 0, and it will be updated to the number of bytes remaining in
780 * the line iff the return value is PARSE_STRING_SUCCESS.
782 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
783 * parsed string token will be returned here.
785 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
786 * PARSE_STRING_FAILURE if the data was invalid due to a missing
787 * closing quote; or PARSE_STRING_NONE if the line ended before the
788 * beginning of a string token was found.
791 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
794 tchar *line = *line_p;
798 /* Skip leading whitespace */
801 return PARSE_STRING_NONE;
802 if (!istspace(*line) && *line != T('\0'))
808 if (quote_char == T('"') || quote_char == T('\'')) {
813 line = tmemchr(line, quote_char, len);
815 imagex_error(T("Missing closing quote: %"TS), fn - 1);
816 return PARSE_STRING_FAILURE;
819 /* Unquoted string. Go until whitespace. Line is terminated
820 * by '\0', so no need to check 'len'. */
824 } while (!istspace(*line) && *line != T('\0'));
831 return PARSE_STRING_SUCCESS;
834 /* Parses a line of data (not an empty line or comment) in the source list file
835 * format. (See the man page for 'wimlib-imagex capture' for details on this
836 * format and the meaning.)
838 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
839 * len == 0. The data in @line will be modified by this function call.
841 * @len: Length of the line of data.
843 * @source: On success, the capture source and target described by the line is
844 * written into this destination. Note that it will contain pointers
845 * to data in the @line array.
847 * Returns true if the line was valid; false otherwise. */
849 parse_source_list_line(tchar *line, size_t len,
850 struct wimlib_capture_source *source)
854 ret = parse_string(&line, &len, &source->fs_source_path);
855 if (ret != PARSE_STRING_SUCCESS)
857 ret = parse_string(&line, &len, &source->wim_target_path);
858 if (ret == PARSE_STRING_NONE)
859 source->wim_target_path = source->fs_source_path;
860 return ret != PARSE_STRING_FAILURE;
863 /* Returns %true if the given line of length @len > 0 is a comment or empty line
864 * in the source list file format. */
866 is_comment_line(const tchar *line, size_t len)
869 if (*line == T('#') || *line == T(';'))
871 if (!istspace(*line) && *line != T('\0'))
881 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
884 tchar *contents = *contents_p;
885 size_t nchars = *nchars_p;
888 for (i = 0; i < nchars; i++)
889 if (contents[i] == T('\n'))
892 /* Handle last line not terminated by a newline */
893 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
894 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
896 imagex_error(T("Out of memory!"));
899 contents[nchars] = T('\n');
900 *contents_p = contents;
908 /* Parses a file in the source list format. (See the man page for
909 * 'wimlib-imagex capture' for details on this format and the meaning.)
911 * @source_list_contents: Contents of the source list file. Note that this
912 * buffer will be modified to save memory allocations,
913 * and cannot be freed until the returned array of
914 * wimlib_capture_source's has also been freed.
916 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
919 * @nsources_ret: On success, the length of the returned array is
922 * Returns: An array of `struct wimlib_capture_source's that can be passed to
923 * the wimlib_add_image_multisource() function to specify how a WIM image is to
925 static struct wimlib_capture_source *
926 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
927 size_t *nsources_ret)
931 struct wimlib_capture_source *sources;
934 nlines = text_file_count_lines(source_list_contents_p,
935 &source_list_nchars);
939 /* Always allocate at least 1 slot, just in case the implementation of
940 * calloc() returns NULL if 0 bytes are requested. */
941 sources = calloc(nlines ?: 1, sizeof(*sources));
943 imagex_error(T("out of memory"));
946 p = *source_list_contents_p;
948 for (i = 0; i < nlines; i++) {
949 /* XXX: Could use rawmemchr() here instead, but it may not be
950 * available on all platforms. */
951 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
952 size_t len = endp - p + 1;
954 if (!is_comment_line(p, len)) {
955 if (!parse_source_list_line(p, len, &sources[j++])) {
967 /* Reads the contents of a file into memory. */
969 file_get_contents(const tchar *filename, size_t *len_ret)
976 if (tstat(filename, &stbuf) != 0) {
977 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
982 fp = tfopen(filename, T("rb"));
984 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
988 buf = malloc(len ? len : 1);
990 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
991 "contents of file \"%"TS"\""), len, filename);
994 if (fread(buf, 1, len, fp) != len) {
995 imagex_error_with_errno(T("Failed to read %zu bytes from the "
996 "file \"%"TS"\""), len, filename);
1010 /* Read standard input until EOF and return the full contents in a malloc()ed
1011 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1014 stdin_get_contents(size_t *len_ret)
1016 /* stdin can, of course, be a pipe or other non-seekable file, so the
1017 * total length of the data cannot be pre-determined */
1019 size_t newlen = 1024;
1023 char *p = realloc(buf, newlen);
1024 size_t bytes_read, bytes_to_read;
1026 imagex_error(T("out of memory while reading stdin"));
1030 bytes_to_read = newlen - pos;
1031 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1033 if (bytes_read != bytes_to_read) {
1038 imagex_error_with_errno(T("error reading stdin"));
1052 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1055 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1057 *num_tchars_ret = num_bytes;
1059 #else /* !__WIN32__ */
1060 /* On Windows, translate the text to UTF-16LE */
1064 if (num_bytes >= 2 &&
1065 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1066 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1068 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1069 * with something that looks like an ASCII character encoded as
1070 * a UTF-16LE code unit. Assume the file is encoded as
1071 * UTF-16LE. This is not a 100% reliable check. */
1072 num_wchars = num_bytes / 2;
1073 text_wstr = (wchar_t*)text;
1075 /* File does not look like UTF-16LE. Assume it is encoded in
1076 * the current Windows code page. I think these are always
1077 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1078 * should work as expected. */
1079 text_wstr = win32_mbs_to_wcs(text,
1084 *num_tchars_ret = num_wchars;
1086 #endif /* __WIN32__ */
1090 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1095 contents = file_get_contents(filename, &num_bytes);
1098 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1102 stdin_get_text_contents(size_t *num_tchars_ret)
1107 contents = stdin_get_contents(&num_bytes);
1110 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1113 #define TO_PERCENT(numerator, denominator) \
1114 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1116 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1117 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1118 #define KIBIBYTE_MIN_NBYTES 10000ULL
1121 get_unit(uint64_t total_bytes, const tchar **name_ret)
1123 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1124 *name_ret = T("GiB");
1126 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1127 *name_ret = T("MiB");
1129 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1130 *name_ret = T("KiB");
1133 *name_ret = T("bytes");
1138 static struct wimlib_progress_info_scan last_scan_progress;
1141 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1143 uint64_t prev_count, cur_count;
1145 prev_count = last_scan_progress.num_nondirs_scanned +
1146 last_scan_progress.num_dirs_scanned;
1147 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1149 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1150 cur_count % 128 == 0)
1152 unsigned unit_shift;
1153 const tchar *unit_name;
1155 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1156 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1157 "%"PRIu64" directories) "),
1158 scan->num_bytes_scanned >> unit_shift,
1160 scan->num_nondirs_scanned,
1161 scan->num_dirs_scanned);
1162 last_scan_progress = *scan;
1165 /* Progress callback function passed to various wimlib functions. */
1166 static enum wimlib_progress_status
1167 imagex_progress_func(enum wimlib_progress_msg msg,
1168 union wimlib_progress_info *info,
1169 void *_ignored_context)
1171 unsigned percent_done;
1172 unsigned unit_shift;
1173 const tchar *unit_name;
1176 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1178 static bool started;
1180 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1181 imagex_printf(T("Using %"TS" compression "
1182 "with %u thread%"TS"\n"),
1183 wimlib_get_compression_type_string(
1184 info->write_streams.compression_type),
1185 info->write_streams.num_threads,
1186 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1191 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1192 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1193 info->write_streams.total_bytes);
1195 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1196 info->write_streams.completed_bytes >> unit_shift,
1198 info->write_streams.total_bytes >> unit_shift,
1201 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1202 imagex_printf(T("\n"));
1204 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1205 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1206 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1207 imagex_printf(T("\n"));
1209 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1210 info->scan.wim_target_path);
1212 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1214 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1215 switch (info->scan.status) {
1216 case WIMLIB_SCAN_DENTRY_OK:
1217 report_scan_progress(&info->scan, false);
1219 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1220 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1222 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1223 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1224 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1226 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1227 /* Symlink fixups are enabled by default. This is
1228 * mainly intended for Windows, which for some reason
1229 * uses absolute junctions (with drive letters!) in the
1230 * default installation. On UNIX-like systems, warn the
1231 * user when fixing the target of an absolute symbolic
1232 * link, so they know to disable this if they want. */
1234 imagex_printf(T("\nWARNING: Adjusted target of "
1235 "absolute symbolic link \"%"TS"\"\n"
1236 " (Use --norpfix to capture "
1237 "absolute symbolic links as-is)\n"),
1238 info->scan.cur_path);
1245 case WIMLIB_PROGRESS_MSG_SCAN_END:
1246 report_scan_progress(&info->scan, true);
1247 imagex_printf(T("\n"));
1249 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1250 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1251 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1252 info->integrity.total_bytes);
1253 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1254 "of %"PRIu64" %"TS" (%u%%) done"),
1255 info->integrity.filename,
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_CALC_INTEGRITY:
1265 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1266 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1267 info->integrity.total_bytes);
1268 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1269 "of %"PRIu64" %"TS" (%u%%) done"),
1270 info->integrity.completed_bytes >> unit_shift,
1272 info->integrity.total_bytes >> unit_shift,
1275 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1276 imagex_printf(T("\n"));
1278 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1279 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1280 "to %"TS" \"%"TS"\"\n"),
1281 info->extract.image,
1282 info->extract.image_name,
1283 info->extract.wimfile_name,
1284 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1285 T("NTFS volume") : T("directory")),
1286 info->extract.target);
1288 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1289 if (info->extract.end_file_count >= 2000) {
1290 percent_done = TO_PERCENT(info->extract.current_file_count,
1291 info->extract.end_file_count);
1292 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1293 info->extract.current_file_count,
1294 info->extract.end_file_count, percent_done);
1295 if (info->extract.current_file_count == info->extract.end_file_count)
1296 imagex_printf(T("\n"));
1299 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1300 percent_done = TO_PERCENT(info->extract.completed_bytes,
1301 info->extract.total_bytes);
1302 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1303 imagex_printf(T("\rExtracting file data: "
1304 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1305 info->extract.completed_bytes >> unit_shift,
1307 info->extract.total_bytes >> unit_shift,
1310 if (info->extract.completed_bytes >= info->extract.total_bytes)
1311 imagex_printf(T("\n"));
1313 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1314 if (info->extract.end_file_count >= 2000) {
1315 percent_done = TO_PERCENT(info->extract.current_file_count,
1316 info->extract.end_file_count);
1317 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1318 info->extract.current_file_count,
1319 info->extract.end_file_count, percent_done);
1320 if (info->extract.current_file_count == info->extract.end_file_count)
1321 imagex_printf(T("\n"));
1324 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1325 if (info->extract.total_parts != 1) {
1326 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1327 info->extract.part_number,
1328 info->extract.total_parts);
1331 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1332 percent_done = TO_PERCENT(info->split.completed_bytes,
1333 info->split.total_bytes);
1334 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1335 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1336 "%"PRIu64" %"TS" (%u%%) written\n"),
1337 info->split.part_name,
1338 info->split.cur_part_number,
1339 info->split.total_parts,
1340 info->split.completed_bytes >> unit_shift,
1342 info->split.total_bytes >> unit_shift,
1346 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1347 if (info->split.completed_bytes == info->split.total_bytes) {
1348 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1349 info->split.cur_part_number,
1350 info->split.total_parts);
1353 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1354 switch (info->update.command->op) {
1355 case WIMLIB_UPDATE_OP_DELETE:
1356 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1357 info->update.command->delete_.wim_path);
1359 case WIMLIB_UPDATE_OP_RENAME:
1360 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1361 info->update.command->rename.wim_source_path,
1362 info->update.command->rename.wim_target_path);
1364 case WIMLIB_UPDATE_OP_ADD:
1369 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1370 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1371 info->replace.path_in_wim);
1373 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1374 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1375 info->wimboot_exclude.path_in_wim);
1377 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1378 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1379 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1380 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1381 info->unmount.mounted_wim,
1382 info->unmount.mounted_image);
1384 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1385 info->unmount.mounted_wim,
1386 info->unmount.mounted_image);
1387 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1391 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1392 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1393 info->verify_image.current_image,
1394 info->verify_image.total_images);
1396 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1397 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1398 info->verify_streams.total_bytes);
1399 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1400 imagex_printf(T("\rVerifying file data: "
1401 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1402 info->verify_streams.completed_bytes >> unit_shift,
1404 info->verify_streams.total_bytes >> unit_shift,
1407 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1408 imagex_printf(T("\n"));
1413 imagex_flush_output();
1414 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1418 parse_num_threads(const tchar *optarg)
1421 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1422 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1423 imagex_error(T("Number of threads must be a non-negative integer!"));
1431 parse_chunk_size(const tchar *optarg)
1434 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1435 if (chunk_size == 0) {
1436 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1437 " with optional K, M, or G suffix"));
1441 if (*tmp == T('k') || *tmp == T('K')) {
1444 } else if (*tmp == T('m') || *tmp == T('M')) {
1447 } else if (*tmp == T('g') || *tmp == T('G')) {
1451 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1452 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1456 if (chunk_size >= UINT32_MAX) {
1457 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1465 * Parse an option passed to an update command.
1467 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1470 * @option: Text string for the option (beginning with --)
1472 * @cmd: `struct wimlib_update_command' that is being constructed for
1475 * Returns true if the option was recognized; false if not.
1478 update_command_add_option(int op, const tchar *option,
1479 struct wimlib_update_command *cmd)
1481 bool recognized = true;
1483 case WIMLIB_UPDATE_OP_ADD:
1484 if (!tstrcmp(option, T("--verbose")))
1485 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1486 else if (!tstrcmp(option, T("--unix-data")))
1487 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1488 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1489 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1490 else if (!tstrcmp(option, T("--strict-acls")))
1491 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1492 else if (!tstrcmp(option, T("--dereference")))
1493 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1494 else if (!tstrcmp(option, T("--no-replace")))
1495 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1499 case WIMLIB_UPDATE_OP_DELETE:
1500 if (!tstrcmp(option, T("--force")))
1501 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1502 else if (!tstrcmp(option, T("--recursive")))
1503 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1514 /* How many nonoption arguments each `imagex update' command expects */
1515 static const unsigned update_command_num_nonoptions[] = {
1516 [WIMLIB_UPDATE_OP_ADD] = 2,
1517 [WIMLIB_UPDATE_OP_DELETE] = 1,
1518 [WIMLIB_UPDATE_OP_RENAME] = 2,
1522 update_command_add_nonoption(int op, const tchar *nonoption,
1523 struct wimlib_update_command *cmd,
1524 unsigned num_nonoptions)
1527 case WIMLIB_UPDATE_OP_ADD:
1528 if (num_nonoptions == 0)
1529 cmd->add.fs_source_path = (tchar*)nonoption;
1531 cmd->add.wim_target_path = (tchar*)nonoption;
1533 case WIMLIB_UPDATE_OP_DELETE:
1534 cmd->delete_.wim_path = (tchar*)nonoption;
1536 case WIMLIB_UPDATE_OP_RENAME:
1537 if (num_nonoptions == 0)
1538 cmd->rename.wim_source_path = (tchar*)nonoption;
1540 cmd->rename.wim_target_path = (tchar*)nonoption;
1546 * Parse a command passed on stdin to `imagex update'.
1548 * @line: Text of the command.
1549 * @len: Length of the line, including a null terminator
1552 * @command: A `struct wimlib_update_command' to fill in from the parsed
1555 * @line_number: Line number of the command, for diagnostics.
1557 * Returns true on success; returns false on parse error.
1560 parse_update_command(tchar *line, size_t len,
1561 struct wimlib_update_command *command,
1565 tchar *command_name;
1567 size_t num_nonoptions;
1569 /* Get the command name ("add", "delete", "rename") */
1570 ret = parse_string(&line, &len, &command_name);
1571 if (ret != PARSE_STRING_SUCCESS)
1574 if (!tstrcasecmp(command_name, T("add"))) {
1575 op = WIMLIB_UPDATE_OP_ADD;
1576 } else if (!tstrcasecmp(command_name, T("delete"))) {
1577 op = WIMLIB_UPDATE_OP_DELETE;
1578 } else if (!tstrcasecmp(command_name, T("rename"))) {
1579 op = WIMLIB_UPDATE_OP_RENAME;
1581 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1582 command_name, line_number);
1587 /* Parse additional options and non-options as needed */
1592 ret = parse_string(&line, &len, &next_string);
1593 if (ret == PARSE_STRING_NONE) /* End of line */
1595 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1597 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1599 if (!update_command_add_option(op, next_string, command))
1601 imagex_error(T("Unrecognized option \"%"TS"\" to "
1602 "update command \"%"TS"\" on line %zu"),
1603 next_string, command_name, line_number);
1609 if (num_nonoptions == update_command_num_nonoptions[op])
1611 imagex_error(T("Unexpected argument \"%"TS"\" in "
1612 "update command on line %zu\n"
1613 " (The \"%"TS"\" command only "
1614 "takes %zu nonoption arguments!)\n"),
1615 next_string, line_number,
1616 command_name, num_nonoptions);
1619 update_command_add_nonoption(op, next_string,
1620 command, num_nonoptions);
1625 if (num_nonoptions != update_command_num_nonoptions[op]) {
1626 imagex_error(T("Not enough arguments to update command "
1627 "\"%"TS"\" on line %zu"), command_name, line_number);
1633 static struct wimlib_update_command *
1634 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1635 size_t *num_cmds_ret)
1639 struct wimlib_update_command *cmds;
1642 nlines = text_file_count_lines(cmd_file_contents_p,
1647 /* Always allocate at least 1 slot, just in case the implementation of
1648 * calloc() returns NULL if 0 bytes are requested. */
1649 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1651 imagex_error(T("out of memory"));
1654 p = *cmd_file_contents_p;
1656 for (i = 0; i < nlines; i++) {
1657 /* XXX: Could use rawmemchr() here instead, but it may not be
1658 * available on all platforms. */
1659 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1660 size_t len = endp - p + 1;
1662 if (!is_comment_line(p, len)) {
1663 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1674 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1675 * one image from a WIM file to an NTFS volume. */
1677 imagex_apply(int argc, tchar **argv, int cmd)
1681 int image = WIMLIB_NO_IMAGE;
1683 struct wimlib_wim_info info;
1685 const tchar *wimfile;
1686 const tchar *target;
1687 const tchar *image_num_or_name = NULL;
1688 int extract_flags = 0;
1690 STRING_LIST(refglobs);
1692 for_opt(c, apply_options) {
1694 case IMAGEX_CHECK_OPTION:
1695 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1697 case IMAGEX_VERBOSE_OPTION:
1698 /* No longer does anything. */
1700 case IMAGEX_REF_OPTION:
1701 ret = string_list_append(&refglobs, optarg);
1703 goto out_free_refglobs;
1705 case IMAGEX_UNIX_DATA_OPTION:
1706 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1708 case IMAGEX_NO_ACLS_OPTION:
1709 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1711 case IMAGEX_STRICT_ACLS_OPTION:
1712 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1714 case IMAGEX_NO_ATTRIBUTES_OPTION:
1715 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1717 case IMAGEX_NORPFIX_OPTION:
1718 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1720 case IMAGEX_RPFIX_OPTION:
1721 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1723 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1724 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1725 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1727 case IMAGEX_WIMBOOT_OPTION:
1728 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1730 case IMAGEX_COMPACT_OPTION:
1731 ret = set_compact_mode(optarg, &extract_flags);
1733 goto out_free_refglobs;
1741 if (argc != 2 && argc != 3)
1746 if (!tstrcmp(wimfile, T("-"))) {
1747 /* Attempt to apply pipable WIM from standard input. */
1749 image_num_or_name = NULL;
1752 image_num_or_name = argv[1];
1757 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1758 imagex_progress_func, NULL);
1760 goto out_free_refglobs;
1762 wimlib_get_wim_info(wim, &info);
1765 /* Image explicitly specified. */
1766 image_num_or_name = argv[1];
1767 image = wimlib_resolve_image(wim, image_num_or_name);
1768 ret = verify_image_exists(image, image_num_or_name, wimfile);
1770 goto out_wimlib_free;
1773 /* No image specified; default to image 1, but only if the WIM
1774 * contains exactly one image. */
1776 if (info.image_count != 1) {
1777 imagex_error(T("\"%"TS"\" contains %d images; "
1778 "Please select one (or all)."),
1779 wimfile, info.image_count);
1788 if (refglobs.num_strings) {
1790 imagex_error(T("Can't specify --ref when applying from stdin!"));
1792 goto out_wimlib_free;
1794 ret = wim_reference_globs(wim, &refglobs, open_flags);
1796 goto out_wimlib_free;
1801 /* Interpret a regular file or block device target as an NTFS
1805 if (tstat(target, &stbuf)) {
1806 if (errno != ENOENT) {
1807 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1810 goto out_wimlib_free;
1813 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1814 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1820 ret = wimlib_extract_image(wim, image, target, extract_flags);
1822 set_fd_to_binary_mode(STDIN_FILENO);
1823 ret = wimlib_extract_image_from_pipe_with_progress(
1828 imagex_progress_func,
1832 imagex_printf(T("Done applying WIM image.\n"));
1833 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1835 do_resource_not_found_warning(wimfile, &info, &refglobs);
1837 imagex_error(T( "If you are applying an image "
1838 "from a split pipable WIM,\n"
1839 " make sure you have "
1840 "concatenated together all parts."));
1842 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1843 do_metadata_not_found_warning(wimfile, &info);
1848 string_list_destroy(&refglobs);
1852 usage(CMD_APPLY, stderr);
1854 goto out_free_refglobs;
1857 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1858 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1859 * the desired image. 'wimlib-imagex append': add a new image to an existing
1862 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1866 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1867 WIMLIB_ADD_FLAG_WINCONFIG |
1868 WIMLIB_ADD_FLAG_VERBOSE |
1869 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1870 int write_flags = 0;
1871 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1872 uint32_t chunk_size = UINT32_MAX;
1873 uint32_t solid_chunk_size = UINT32_MAX;
1874 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1875 const tchar *wimfile;
1878 STRING_LIST(image_properties);
1881 STRING_LIST(base_wimfiles);
1882 WIMStruct **base_wims;
1884 WIMStruct *template_wim = NULL;
1885 const tchar *template_wimfile = NULL;
1886 const tchar *template_image_name_or_num = NULL;
1887 int template_image = WIMLIB_NO_IMAGE;
1890 unsigned num_threads = 0;
1895 tchar *config_file = NULL;
1897 bool source_list = false;
1898 size_t source_list_nchars = 0;
1899 tchar *source_list_contents;
1900 bool capture_sources_malloced;
1901 struct wimlib_capture_source *capture_sources;
1903 bool name_defaulted;
1905 for_opt(c, capture_or_append_options) {
1907 case IMAGEX_BOOT_OPTION:
1908 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1910 case IMAGEX_CHECK_OPTION:
1911 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1913 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
1914 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1916 case IMAGEX_NOCHECK_OPTION:
1917 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1919 case IMAGEX_CONFIG_OPTION:
1920 config_file = optarg;
1921 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1923 case IMAGEX_COMPRESS_OPTION:
1924 compression_type = get_compression_type(optarg, false);
1925 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1928 case IMAGEX_CHUNK_SIZE_OPTION:
1929 chunk_size = parse_chunk_size(optarg);
1930 if (chunk_size == UINT32_MAX)
1933 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1934 solid_chunk_size = parse_chunk_size(optarg);
1935 if (solid_chunk_size == UINT32_MAX)
1938 case IMAGEX_SOLID_COMPRESS_OPTION:
1939 solid_ctype = get_compression_type(optarg, true);
1940 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1943 case IMAGEX_SOLID_OPTION:
1944 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1946 case IMAGEX_NO_SOLID_SORT_OPTION:
1947 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1949 case IMAGEX_FLAGS_OPTION: {
1950 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1951 tsprintf(p, T("FLAGS=%"TS), optarg);
1952 ret = string_list_append(&image_properties, p);
1957 case IMAGEX_IMAGE_PROPERTY_OPTION:
1958 ret = append_image_property_argument(&image_properties);
1962 case IMAGEX_DEREFERENCE_OPTION:
1963 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1965 case IMAGEX_VERBOSE_OPTION:
1966 /* No longer does anything. */
1968 case IMAGEX_THREADS_OPTION:
1969 num_threads = parse_num_threads(optarg);
1970 if (num_threads == UINT_MAX)
1973 case IMAGEX_REBUILD_OPTION:
1974 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1976 case IMAGEX_UNIX_DATA_OPTION:
1977 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1979 case IMAGEX_SOURCE_LIST_OPTION:
1982 case IMAGEX_NO_ACLS_OPTION:
1983 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1985 case IMAGEX_STRICT_ACLS_OPTION:
1986 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1988 case IMAGEX_RPFIX_OPTION:
1989 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1991 case IMAGEX_NORPFIX_OPTION:
1992 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1994 case IMAGEX_PIPABLE_OPTION:
1995 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1997 case IMAGEX_NOT_PIPABLE_OPTION:
1998 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2000 case IMAGEX_UPDATE_OF_OPTION:
2001 if (template_image_name_or_num) {
2002 imagex_error(T("'--update-of' can only be "
2003 "specified one time!"));
2007 colon = tstrrchr(optarg, T(':'));
2010 template_wimfile = optarg;
2012 template_image_name_or_num = colon + 1;
2014 template_wimfile = NULL;
2015 template_image_name_or_num = optarg;
2019 case IMAGEX_DELTA_FROM_OPTION:
2020 ret = string_list_append(&base_wimfiles, optarg);
2023 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2025 case IMAGEX_WIMBOOT_OPTION:
2026 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2028 case IMAGEX_UNSAFE_COMPACT_OPTION:
2029 if (cmd != CMD_APPEND) {
2030 imagex_error(T("'--unsafe-compact' is only "
2031 "valid for append!"));
2034 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2036 case IMAGEX_SNAPSHOT_OPTION:
2037 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2046 if (argc < 2 || argc > 4)
2052 /* Set default compression type and parameters. */
2055 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2056 /* No compression type specified. Use the default. */
2058 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2059 /* With --wimboot, default to XPRESS compression. */
2060 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2061 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2062 /* With --solid, default to LZMS compression. (However,
2063 * this will not affect solid resources!) */
2064 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2066 /* Otherwise, default to LZX compression. */
2067 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2071 if (!tstrcmp(wimfile, T("-"))) {
2072 /* Writing captured WIM to standard output. */
2074 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2075 imagex_error("Can't write a non-pipable WIM to "
2076 "standard output! Specify --pipable\n"
2077 " if you want to create a pipable WIM "
2078 "(but read the docs first).");
2082 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2084 if (cmd == CMD_APPEND) {
2085 imagex_error(T("Using standard output for append does "
2086 "not make sense."));
2089 wim_fd = STDOUT_FILENO;
2091 imagex_output_to_stderr();
2092 set_fd_to_binary_mode(wim_fd);
2095 /* If template image was specified using --update-of=IMAGE rather
2096 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2097 if (template_image_name_or_num && !template_wimfile) {
2098 if (base_wimfiles.num_strings == 1) {
2099 /* Capturing delta WIM based on single WIM: default to
2101 template_wimfile = base_wimfiles.strings[0];
2102 } else if (cmd == CMD_APPEND) {
2103 /* Appending to WIM: default to WIM being appended to.
2105 template_wimfile = wimfile;
2107 /* Capturing a normal (non-delta) WIM, so the WIM file
2108 * *must* be explicitly specified. */
2109 if (base_wimfiles.num_strings > 1) {
2110 imagex_error(T("For capture of delta WIM "
2111 "based on multiple existing "
2113 " '--update-of' must "
2114 "specify WIMFILE:IMAGE!"));
2116 imagex_error(T("For capture of non-delta WIM, "
2117 "'--update-of' must specify "
2126 name_defaulted = false;
2128 /* Set default name to SOURCE argument, omitting any directory
2129 * prefixes and trailing slashes. This requires making a copy
2130 * of @source. Leave some free characters at the end in case we
2131 * append a number to keep the name unique. */
2132 size_t source_name_len;
2134 source_name_len = tstrlen(source);
2135 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2136 name = tbasename(tstrcpy(source_copy, source));
2137 name_defaulted = true;
2140 /* Image description (if given). */
2142 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2143 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2144 ret = string_list_append(&image_properties, p);
2150 /* Set up capture sources in source list mode */
2151 if (source[0] == T('-') && source[1] == T('\0')) {
2152 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2154 source_list_contents = file_get_text_contents(source,
2155 &source_list_nchars);
2157 if (!source_list_contents)
2160 capture_sources = parse_source_list(&source_list_contents,
2163 if (!capture_sources) {
2165 goto out_free_source_list_contents;
2167 capture_sources_malloced = true;
2169 /* Set up capture source in non-source-list mode. */
2170 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2171 capture_sources[0].fs_source_path = source;
2172 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2173 capture_sources[0].reserved = 0;
2175 capture_sources_malloced = false;
2176 source_list_contents = NULL;
2179 /* Open the existing WIM, or create a new one. */
2180 if (cmd == CMD_APPEND) {
2181 ret = wimlib_open_wim_with_progress(wimfile,
2182 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2184 imagex_progress_func,
2187 goto out_free_capture_sources;
2189 ret = wimlib_create_new_wim(compression_type, &wim);
2191 goto out_free_capture_sources;
2192 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2195 /* Set chunk size if non-default. */
2196 if (chunk_size != UINT32_MAX) {
2197 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2200 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2202 int ctype = compression_type;
2204 if (cmd == CMD_APPEND) {
2205 struct wimlib_wim_info info;
2206 wimlib_get_wim_info(wim, &info);
2207 ctype = info.compression_type;
2210 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2211 ret = wimlib_set_output_chunk_size(wim, 4096);
2216 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2217 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2221 if (solid_chunk_size != UINT32_MAX) {
2222 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2228 /* Detect if source is regular file or block device and set NTFS volume
2233 if (tstat(source, &stbuf) == 0) {
2234 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2235 imagex_printf(T("Capturing WIM image from NTFS "
2236 "filesystem on \"%"TS"\"\n"), source);
2237 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2240 if (errno != ENOENT) {
2241 imagex_error_with_errno(T("Failed to stat "
2242 "\"%"TS"\""), source);
2250 /* If the user did not specify an image name, and the basename of the
2251 * source already exists as an image name in the WIM file, append a
2252 * suffix to make it unique. */
2253 if (cmd == CMD_APPEND && name_defaulted) {
2254 unsigned long conflict_idx;
2255 tchar *name_end = tstrchr(name, T('\0'));
2256 for (conflict_idx = 1;
2257 wimlib_image_name_in_use(wim, name);
2260 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2264 /* If capturing a delta WIM, reference resources from the base WIMs
2265 * before adding the new image. */
2266 if (base_wimfiles.num_strings) {
2267 base_wims = calloc(base_wimfiles.num_strings,
2268 sizeof(base_wims[0]));
2269 if (base_wims == NULL) {
2270 imagex_error(T("Out of memory!"));
2275 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2276 ret = wimlib_open_wim_with_progress(
2277 base_wimfiles.strings[i], open_flags,
2278 &base_wims[i], imagex_progress_func, NULL);
2280 goto out_free_base_wims;
2284 ret = wimlib_reference_resources(wim, base_wims,
2285 base_wimfiles.num_strings, 0);
2287 goto out_free_base_wims;
2289 if (base_wimfiles.num_strings == 1) {
2290 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2291 base_wimfiles.strings[0]);
2293 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2294 base_wimfiles.num_strings);
2301 /* If capturing or appending as an update of an existing (template) image,
2302 * open the WIM if needed and parse the image index. */
2303 if (template_image_name_or_num) {
2305 if (cmd == CMD_APPEND && !tstrcmp(template_wimfile, wimfile)) {
2308 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2309 if (!tstrcmp(template_wimfile,
2310 base_wimfiles.strings[i])) {
2311 template_wim = base_wims[i];
2317 if (!template_wim) {
2318 ret = wimlib_open_wim_with_progress(template_wimfile,
2321 imagex_progress_func,
2324 goto out_free_base_wims;
2327 template_image = wimlib_resolve_image(template_wim,
2328 template_image_name_or_num);
2330 if (template_image_name_or_num[0] == T('-')) {
2333 struct wimlib_wim_info info;
2335 wimlib_get_wim_info(template_wim, &info);
2336 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2337 if (n >= 1 && n <= info.image_count &&
2339 tmp != template_image_name_or_num + 1)
2341 template_image = info.image_count - (n - 1);
2344 ret = verify_image_exists_and_is_single(template_image,
2345 template_image_name_or_num,
2348 goto out_free_template_wim;
2351 ret = wimlib_add_image_multisource(wim,
2358 goto out_free_template_wim;
2360 if (image_properties.num_strings || template_image_name_or_num) {
2361 /* User asked to set additional image properties, or an image on
2362 * which the added one is to be based has been specified with
2364 struct wimlib_wim_info info;
2366 wimlib_get_wim_info(wim, &info);
2368 ret = apply_image_properties(&image_properties, wim,
2369 info.image_count, NULL);
2371 goto out_free_template_wim;
2373 /* Reference template image if the user provided one. */
2374 if (template_image_name_or_num) {
2375 imagex_printf(T("Using image %d "
2376 "from \"%"TS"\" as template\n"),
2377 template_image, template_wimfile);
2378 ret = wimlib_reference_template_image(wim,
2384 goto out_free_template_wim;
2388 /* Write the new WIM or overwrite the existing WIM with the new image
2390 if (cmd == CMD_APPEND) {
2391 ret = wimlib_overwrite(wim, write_flags, num_threads);
2392 } else if (wimfile) {
2393 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2394 write_flags, num_threads);
2396 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2397 write_flags, num_threads);
2399 out_free_template_wim:
2400 /* 'template_wim' may alias 'wim' or any of the 'base_wims' */
2401 if (template_wim == wim)
2402 goto out_free_base_wims;
2403 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2404 if (template_wim == base_wims[i])
2405 goto out_free_base_wims;
2406 wimlib_free(template_wim);
2408 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2409 wimlib_free(base_wims[i]);
2413 out_free_capture_sources:
2414 if (capture_sources_malloced)
2415 free(capture_sources);
2416 out_free_source_list_contents:
2417 free(source_list_contents);
2419 string_list_destroy(&image_properties);
2420 string_list_destroy(&base_wimfiles);
2430 /* Remove image(s) from a WIM. */
2432 imagex_delete(int argc, tchar **argv, int cmd)
2435 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2436 int write_flags = 0;
2437 const tchar *wimfile;
2438 const tchar *image_num_or_name;
2443 for_opt(c, delete_options) {
2445 case IMAGEX_CHECK_OPTION:
2446 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2448 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2449 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2451 case IMAGEX_SOFT_OPTION:
2452 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2454 case IMAGEX_UNSAFE_COMPACT_OPTION:
2455 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2466 imagex_error(T("Must specify a WIM file"));
2468 imagex_error(T("Must specify an image"));
2472 image_num_or_name = argv[1];
2474 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2475 imagex_progress_func, NULL);
2479 image = wimlib_resolve_image(wim, image_num_or_name);
2481 ret = verify_image_exists(image, image_num_or_name, wimfile);
2483 goto out_wimlib_free;
2485 ret = wimlib_delete_image(wim, image);
2487 imagex_error(T("Failed to delete image from \"%"TS"\""),
2489 goto out_wimlib_free;
2492 ret = wimlib_overwrite(wim, write_flags, 0);
2494 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2495 "deleted"), wimfile);
2503 usage(CMD_DELETE, stderr);
2508 struct print_dentry_options {
2513 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2515 tprintf(T("%"TS"\n"), dentry->full_path);
2518 static const struct {
2521 } file_attr_flags[] = {
2522 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2523 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2524 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2525 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2526 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2527 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2528 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2529 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2530 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2531 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2532 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2533 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2534 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2535 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2536 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2539 #define TIMESTR_MAX 100
2542 print_time(const tchar *type, const struct wimlib_timespec *wts,
2545 tchar timestr[TIMESTR_MAX];
2549 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2550 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2555 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2556 timestr[TIMESTR_MAX - 1] = '\0';
2558 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2561 static void print_byte_field(const uint8_t field[], size_t len)
2564 tprintf(T("%02hhx"), *field++);
2568 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2570 tchar attr_string[256];
2573 tputs(T("WIM Information:"));
2574 tputs(T("----------------"));
2575 tprintf(T("Path: %"TS"\n"), wimfile);
2576 tprintf(T("GUID: 0x"));
2577 print_byte_field(info->guid, sizeof(info->guid));
2579 tprintf(T("Version: %u\n"), info->wim_version);
2580 tprintf(T("Image Count: %d\n"), info->image_count);
2581 tprintf(T("Compression: %"TS"\n"),
2582 wimlib_get_compression_type_string(info->compression_type));
2583 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2585 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2586 tprintf(T("Boot Index: %d\n"), info->boot_index);
2587 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2589 attr_string[0] = T('\0');
2592 tstrcat(attr_string, T("Pipable, "));
2594 if (info->has_integrity_table)
2595 tstrcat(attr_string, T("Integrity info, "));
2597 if (info->has_rpfix)
2598 tstrcat(attr_string, T("Relative path junction, "));
2600 if (info->resource_only)
2601 tstrcat(attr_string, T("Resource only, "));
2603 if (info->metadata_only)
2604 tstrcat(attr_string, T("Metadata only, "));
2606 if (info->is_marked_readonly)
2607 tstrcat(attr_string, T("Readonly, "));
2609 p = tstrchr(attr_string, T('\0'));
2610 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2613 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2617 print_resource(const struct wimlib_resource_entry *resource,
2620 tprintf(T("Hash = 0x"));
2621 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2624 if (!resource->is_missing) {
2625 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2626 resource->uncompressed_size);
2627 if (resource->packed) {
2628 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2629 "bytes @ offset %"PRIu64"\n"),
2630 resource->raw_resource_uncompressed_size,
2631 resource->raw_resource_compressed_size,
2632 resource->raw_resource_offset_in_wim);
2634 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2637 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2638 resource->compressed_size);
2640 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2644 tprintf(T("Part Number = %u\n"), resource->part_number);
2645 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2647 tprintf(T("Flags = "));
2648 if (resource->is_compressed)
2649 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2650 if (resource->is_metadata)
2651 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2652 if (resource->is_free)
2653 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2654 if (resource->is_spanned)
2655 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2656 if (resource->packed)
2657 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2665 print_blobs(WIMStruct *wim)
2667 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2672 default_print_security_descriptor(const uint8_t *sd, size_t size)
2674 tprintf(T("Security Descriptor = "));
2675 print_byte_field(sd, size);
2681 is_null_guid(const uint8_t *guid)
2683 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2685 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2689 print_guid(const tchar *label, const uint8_t *guid)
2691 if (is_null_guid(guid))
2693 tprintf(T("%-20"TS"= 0x"), label);
2694 print_byte_field(guid, WIMLIB_GUID_LEN);
2699 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2702 "----------------------------------------------------------------------------\n"));
2703 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2704 if (dentry->dos_name)
2705 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2706 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2707 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2708 if (file_attr_flags[i].flag & dentry->attributes)
2709 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2710 file_attr_flags[i].name);
2712 if (dentry->security_descriptor) {
2713 print_security_descriptor(dentry->security_descriptor,
2714 dentry->security_descriptor_size);
2717 print_time(T("Creation Time"),
2718 &dentry->creation_time, dentry->creation_time_high);
2719 print_time(T("Last Write Time"),
2720 &dentry->last_write_time, dentry->last_write_time_high);
2721 print_time(T("Last Access Time"),
2722 &dentry->last_access_time, dentry->last_access_time_high);
2725 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2726 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2728 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2729 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2731 if (dentry->unix_mode != 0) {
2732 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2733 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2734 dentry->unix_uid, dentry->unix_gid,
2735 dentry->unix_mode, dentry->unix_rdev);
2738 if (!is_null_guid(dentry->object_id.object_id)) {
2739 print_guid(T("Object ID"), dentry->object_id.object_id);
2740 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2741 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2742 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2745 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2746 if (dentry->streams[i].stream_name) {
2747 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2748 dentry->streams[i].stream_name);
2749 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2750 tprintf(T("\tRaw encrypted data stream:\n"));
2751 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2752 tprintf(T("\tReparse point stream:\n"));
2754 tprintf(T("\tUnnamed data stream:\n"));
2756 print_resource(&dentry->streams[i].resource, NULL);
2761 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2763 const struct print_dentry_options *options = _options;
2764 if (!options->detailed)
2765 print_dentry_full_path(dentry);
2767 print_dentry_detailed(dentry);
2771 /* Print the files contained in an image(s) in a WIM file. */
2773 imagex_dir(int argc, tchar **argv, int cmd)
2775 const tchar *wimfile;
2776 WIMStruct *wim = NULL;
2779 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2781 struct print_dentry_options options = {
2784 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2786 STRING_LIST(refglobs);
2788 for_opt(c, dir_options) {
2790 case IMAGEX_PATH_OPTION:
2793 case IMAGEX_DETAILED_OPTION:
2794 options.detailed = true;
2796 case IMAGEX_ONE_FILE_ONLY_OPTION:
2797 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2799 case IMAGEX_REF_OPTION:
2800 ret = string_list_append(&refglobs, optarg);
2802 goto out_free_refglobs;
2812 imagex_error(T("Must specify a WIM file"));
2816 imagex_error(T("Too many arguments"));
2821 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2822 imagex_progress_func, NULL);
2824 goto out_free_refglobs;
2827 image = wimlib_resolve_image(wim, argv[1]);
2828 ret = verify_image_exists(image, argv[1], wimfile);
2830 goto out_wimlib_free;
2832 /* No image specified; default to image 1, but only if the WIM
2833 * contains exactly one image. */
2835 struct wimlib_wim_info info;
2837 wimlib_get_wim_info(wim, &info);
2838 if (info.image_count != 1) {
2839 imagex_error(T("\"%"TS"\" contains %d images; Please "
2840 "select one (or all)."),
2841 wimfile, info.image_count);
2848 if (refglobs.num_strings) {
2849 ret = wim_reference_globs(wim, &refglobs, 0);
2851 goto out_wimlib_free;
2854 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2855 print_dentry, &options);
2856 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2857 struct wimlib_wim_info info;
2859 wimlib_get_wim_info(wim, &info);
2860 do_metadata_not_found_warning(wimfile, &info);
2865 string_list_destroy(&refglobs);
2869 usage(CMD_DIR, stderr);
2871 goto out_free_refglobs;
2874 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2877 imagex_export(int argc, tchar **argv, int cmd)
2881 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2882 int write_flags = 0;
2883 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2884 const tchar *src_wimfile;
2885 const tchar *src_image_num_or_name;
2886 const tchar *dest_wimfile;
2888 const tchar *dest_name;
2889 const tchar *dest_desc;
2891 struct wimlib_wim_info src_info;
2892 WIMStruct *dest_wim;
2897 STRING_LIST(refglobs);
2898 unsigned num_threads = 0;
2899 uint32_t chunk_size = UINT32_MAX;
2900 uint32_t solid_chunk_size = UINT32_MAX;
2901 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2903 for_opt(c, export_options) {
2905 case IMAGEX_BOOT_OPTION:
2906 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2908 case IMAGEX_CHECK_OPTION:
2909 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2911 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2912 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2914 case IMAGEX_NOCHECK_OPTION:
2915 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2917 case IMAGEX_COMPRESS_OPTION:
2918 compression_type = get_compression_type(optarg, false);
2919 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2922 case IMAGEX_RECOMPRESS_OPTION:
2923 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2925 case IMAGEX_SOLID_OPTION:
2926 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2928 case IMAGEX_NO_SOLID_SORT_OPTION:
2929 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2931 case IMAGEX_CHUNK_SIZE_OPTION:
2932 chunk_size = parse_chunk_size(optarg);
2933 if (chunk_size == UINT32_MAX)
2936 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2937 solid_chunk_size = parse_chunk_size(optarg);
2938 if (solid_chunk_size == UINT32_MAX)
2941 case IMAGEX_SOLID_COMPRESS_OPTION:
2942 solid_ctype = get_compression_type(optarg, true);
2943 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2946 case IMAGEX_REF_OPTION:
2947 ret = string_list_append(&refglobs, optarg);
2949 goto out_free_refglobs;
2951 case IMAGEX_THREADS_OPTION:
2952 num_threads = parse_num_threads(optarg);
2953 if (num_threads == UINT_MAX)
2956 case IMAGEX_REBUILD_OPTION:
2957 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2959 case IMAGEX_PIPABLE_OPTION:
2960 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2962 case IMAGEX_NOT_PIPABLE_OPTION:
2963 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2965 case IMAGEX_WIMBOOT_OPTION:
2966 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2968 case IMAGEX_UNSAFE_COMPACT_OPTION:
2969 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2977 if (argc < 3 || argc > 5)
2979 src_wimfile = argv[0];
2980 src_image_num_or_name = argv[1];
2981 dest_wimfile = argv[2];
2982 dest_name = (argc >= 4) ? argv[3] : NULL;
2983 dest_desc = (argc >= 5) ? argv[4] : NULL;
2984 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2985 imagex_progress_func, NULL);
2987 goto out_free_refglobs;
2989 wimlib_get_wim_info(src_wim, &src_info);
2991 /* Determine if the destination is an existing file or not. If so, we
2992 * try to append the exported image(s) to it; otherwise, we create a new
2993 * WIM containing the exported image(s). Furthermore, determine if we
2994 * need to write a pipable WIM directly to standard output. */
2996 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2998 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2999 imagex_error("Can't write a non-pipable WIM to "
3000 "standard output! Specify --pipable\n"
3001 " if you want to create a pipable WIM "
3002 "(but read the docs first).");
3004 goto out_free_src_wim;
3007 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3009 dest_wimfile = NULL;
3010 dest_wim_fd = STDOUT_FILENO;
3011 imagex_output_to_stderr();
3012 set_fd_to_binary_mode(dest_wim_fd);
3015 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
3017 /* Destination file exists. */
3019 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
3020 imagex_error(T("\"%"TS"\" is not a regular file "
3021 "or block device"), dest_wimfile);
3023 goto out_free_src_wim;
3025 ret = wimlib_open_wim_with_progress(dest_wimfile,
3027 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3029 imagex_progress_func,
3032 goto out_free_src_wim;
3034 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3035 /* The user specified a compression type, but we're
3036 * exporting to an existing WIM. Make sure the
3037 * specified compression type is the same as the
3038 * compression type of the existing destination WIM. */
3039 struct wimlib_wim_info dest_info;
3041 wimlib_get_wim_info(dest_wim, &dest_info);
3042 if (compression_type != dest_info.compression_type) {
3043 imagex_error(T("Cannot specify a compression type that is "
3044 "not the same as that used in the "
3045 "destination WIM"));
3047 goto out_free_dest_wim;
3053 if (errno != ENOENT) {
3054 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3057 goto out_free_src_wim;
3060 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3061 imagex_error(T("'--unsafe-compact' is only valid when "
3062 "exporting to an existing WIM file!"));
3064 goto out_free_src_wim;
3067 /* dest_wimfile is not an existing file, so create a new WIM. */
3069 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3070 /* The user did not specify a compression type; default
3071 * to that of the source WIM, unless --solid or
3072 * --wimboot was specified. */
3074 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3075 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3076 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3077 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3079 compression_type = src_info.compression_type;
3081 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3083 goto out_free_src_wim;
3085 wimlib_register_progress_function(dest_wim,
3086 imagex_progress_func, NULL);
3088 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3089 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3091 /* For --wimboot export, use small XPRESS chunks. */
3092 wimlib_set_output_chunk_size(dest_wim, 4096);
3093 } else if (compression_type == src_info.compression_type &&
3094 chunk_size == UINT32_MAX)
3096 /* Use same chunk size if compression type is the same. */
3097 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3101 if (chunk_size != UINT32_MAX) {
3102 /* Set destination chunk size. */
3103 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3105 goto out_free_dest_wim;
3107 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3108 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3110 goto out_free_dest_wim;
3112 if (solid_chunk_size != UINT32_MAX) {
3113 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3115 goto out_free_dest_wim;
3118 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3119 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3121 goto out_free_dest_wim;
3123 if (refglobs.num_strings) {
3124 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3126 goto out_free_dest_wim;
3129 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3130 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3132 imagex_error(T("--boot specified for all-images export, but source WIM "
3133 "has no bootable image."));
3135 goto out_free_dest_wim;
3138 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3139 dest_desc, export_flags);
3141 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3142 do_resource_not_found_warning(src_wimfile,
3143 &src_info, &refglobs);
3144 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3145 do_metadata_not_found_warning(src_wimfile, &src_info);
3147 goto out_free_dest_wim;
3151 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3152 else if (dest_wimfile)
3153 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3154 write_flags, num_threads);
3156 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3157 WIMLIB_ALL_IMAGES, write_flags,
3160 wimlib_free(dest_wim);
3162 wimlib_free(src_wim);
3164 string_list_destroy(&refglobs);
3168 usage(CMD_EXPORT, stderr);
3171 goto out_free_refglobs;
3174 /* Extract files or directories from a WIM image */
3176 imagex_extract(int argc, tchar **argv, int cmd)
3183 const tchar *wimfile;
3184 const tchar *image_num_or_name;
3185 tchar *dest_dir = T(".");
3186 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3187 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3188 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3189 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3191 STRING_LIST(refglobs);
3193 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3195 for_opt(c, extract_options) {
3197 case IMAGEX_CHECK_OPTION:
3198 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3200 case IMAGEX_VERBOSE_OPTION:
3201 /* No longer does anything. */
3203 case IMAGEX_REF_OPTION:
3204 ret = string_list_append(&refglobs, optarg);
3206 goto out_free_refglobs;
3208 case IMAGEX_UNIX_DATA_OPTION:
3209 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3211 case IMAGEX_NO_ACLS_OPTION:
3212 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3214 case IMAGEX_STRICT_ACLS_OPTION:
3215 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3217 case IMAGEX_NO_ATTRIBUTES_OPTION:
3218 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3220 case IMAGEX_DEST_DIR_OPTION:
3223 case IMAGEX_TO_STDOUT_OPTION:
3224 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3225 imagex_suppress_output();
3226 set_fd_to_binary_mode(STDOUT_FILENO);
3228 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3229 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3230 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3232 case IMAGEX_NO_GLOBS_OPTION:
3233 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3235 case IMAGEX_NULLGLOB_OPTION:
3236 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3238 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3239 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3241 case IMAGEX_WIMBOOT_OPTION:
3242 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3244 case IMAGEX_COMPACT_OPTION:
3245 ret = set_compact_mode(optarg, &extract_flags);
3247 goto out_free_refglobs;
3259 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3260 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3262 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3267 image_num_or_name = argv[1];
3272 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3273 imagex_progress_func, NULL);
3275 goto out_free_refglobs;
3277 image = wimlib_resolve_image(wim, image_num_or_name);
3278 ret = verify_image_exists_and_is_single(image,
3282 goto out_wimlib_free;
3284 if (refglobs.num_strings) {
3285 ret = wim_reference_globs(wim, &refglobs, open_flags);
3287 goto out_wimlib_free;
3293 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3296 while (argc != 0 && ret == 0) {
3300 num_paths < argc && argv[num_paths][0] != T('@');
3305 ret = wimlib_extract_paths(wim, image, dest_dir,
3306 (const tchar **)argv,
3308 extract_flags | notlist_extract_flags);
3312 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3321 imagex_printf(T("Done extracting files.\n"));
3322 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3323 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3324 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3325 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3326 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3329 T("Note: You can use the '--nullglob' "
3330 "option to ignore missing files.\n"));
3332 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3333 "files and directories\n"
3334 " are in the WIM image.\n"),
3335 get_cmd_string(CMD_DIR, false));
3336 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3337 struct wimlib_wim_info info;
3339 wimlib_get_wim_info(wim, &info);
3340 do_resource_not_found_warning(wimfile, &info, &refglobs);
3341 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3342 struct wimlib_wim_info info;
3344 wimlib_get_wim_info(wim, &info);
3345 do_metadata_not_found_warning(wimfile, &info);
3350 string_list_destroy(&refglobs);
3354 usage(CMD_EXTRACT, stderr);
3357 goto out_free_refglobs;
3360 /* Prints information about a WIM file; also can mark an image as bootable,
3361 * change the name of an image, or change the description of an image. */
3363 imagex_info(int argc, tchar **argv, int cmd)
3367 bool header = false;
3370 bool short_header = true;
3371 const tchar *xml_out_file = NULL;
3372 const tchar *wimfile;
3373 const tchar *image_num_or_name;
3374 STRING_LIST(image_properties);
3379 int write_flags = 0;
3380 struct wimlib_wim_info info;
3382 for_opt(c, info_options) {
3384 case IMAGEX_BOOT_OPTION:
3387 case IMAGEX_CHECK_OPTION:
3388 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3390 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3391 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3393 case IMAGEX_NOCHECK_OPTION:
3394 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3396 case IMAGEX_HEADER_OPTION:
3398 short_header = false;
3400 case IMAGEX_BLOBS_OPTION:
3402 short_header = false;
3404 case IMAGEX_XML_OPTION:
3406 short_header = false;
3408 case IMAGEX_EXTRACT_XML_OPTION:
3409 xml_out_file = optarg;
3410 short_header = false;
3412 case IMAGEX_IMAGE_PROPERTY_OPTION:
3413 ret = append_image_property_argument(&image_properties);
3424 if (argc < 1 || argc > 4)
3428 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3432 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3433 tsprintf(p, T("NAME=%"TS), argv[2]);
3434 ret = string_list_append(&image_properties, p);
3441 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3442 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3443 ret = string_list_append(&image_properties, p);
3448 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3449 imagex_progress_func, NULL);
3453 wimlib_get_wim_info(wim, &info);
3455 image = wimlib_resolve_image(wim, image_num_or_name);
3456 ret = WIMLIB_ERR_INVALID_IMAGE;
3457 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3458 verify_image_exists(image, image_num_or_name, wimfile);
3460 imagex_error(T("If you would like to set the boot "
3461 "index to 0, specify image \"0\" with "
3462 "the --boot flag."));
3464 goto out_wimlib_free;
3467 if (boot && info.image_count == 0) {
3468 imagex_error(T("--boot is meaningless on a WIM with no images"));
3469 goto out_wimlib_free;
3472 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3474 imagex_error(T("Cannot specify the --boot flag "
3475 "without specifying a specific "
3476 "image in a multi-image WIM"));
3477 goto out_wimlib_free;
3479 if (image_properties.num_strings) {
3480 imagex_error(T("Can't change image properties without "
3481 "specifying a specific image in a "
3482 "multi-image WIM"));
3483 goto out_wimlib_free;
3487 /* Operations that print information are separated from operations that
3488 * recreate the WIM file. */
3489 if (!image_properties.num_strings && !boot) {
3491 /* Read-only operations */
3493 if (image == WIMLIB_NO_IMAGE) {
3494 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3495 image_num_or_name, wimfile);
3496 goto out_wimlib_free;
3499 if (image == WIMLIB_ALL_IMAGES && short_header)
3500 print_wim_information(wimfile, &info);
3503 wimlib_print_header(wim);
3506 if (info.total_parts != 1) {
3507 tfprintf(stderr, T("Warning: Only showing the blobs "
3508 "for part %d of a %d-part WIM.\n"),
3509 info.part_number, info.total_parts);
3515 ret = wimlib_extract_xml_data(wim, stdout);
3517 goto out_wimlib_free;
3523 fp = tfopen(xml_out_file, T("wb"));
3525 imagex_error_with_errno(T("Failed to open the "
3526 "file \"%"TS"\" for "
3530 goto out_wimlib_free;
3532 ret = wimlib_extract_xml_data(wim, fp);
3534 imagex_error(T("Failed to close the file "
3540 goto out_wimlib_free;
3544 wimlib_print_available_images(wim, image);
3548 /* Modification operations */
3549 bool any_property_changes;
3551 if (image == WIMLIB_ALL_IMAGES)
3554 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3555 imagex_error(T("Cannot change image properties "
3556 "when using image 0"));
3558 goto out_wimlib_free;
3562 if (image == info.boot_index) {
3563 imagex_printf(T("Image %d is already marked as "
3564 "bootable.\n"), image);
3567 imagex_printf(T("Marking image %d as bootable.\n"),
3569 info.boot_index = image;
3570 ret = wimlib_set_wim_info(wim, &info,
3571 WIMLIB_CHANGE_BOOT_INDEX);
3573 goto out_wimlib_free;
3577 ret = apply_image_properties(&image_properties, wim, image,
3578 &any_property_changes);
3580 goto out_wimlib_free;
3582 /* Only call wimlib_overwrite() if something actually needs to
3584 if (boot || any_property_changes ||
3585 ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) &&
3586 !info.has_integrity_table) ||
3587 ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) &&
3588 info.has_integrity_table))
3590 ret = wimlib_overwrite(wim, write_flags, 1);
3592 imagex_printf(T("The file \"%"TS"\" was not modified "
3593 "because nothing needed to be done.\n"),
3601 string_list_destroy(&image_properties);
3605 usage(CMD_INFO, stderr);
3610 /* Join split WIMs into one part WIM */
3612 imagex_join(int argc, tchar **argv, int cmd)
3615 int swm_open_flags = 0;
3616 int wim_write_flags = 0;
3617 const tchar *output_path;
3620 for_opt(c, join_options) {
3622 case IMAGEX_CHECK_OPTION:
3623 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3625 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3626 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3636 imagex_error(T("Must specify one or more split WIM (.swm) "
3640 output_path = argv[0];
3641 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3646 imagex_progress_func,
3652 usage(CMD_JOIN, stderr);
3657 #if WIM_MOUNTING_SUPPORTED
3659 /* Mounts a WIM image. */
3661 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3664 int mount_flags = 0;
3666 const tchar *staging_dir = NULL;
3667 const tchar *wimfile;
3670 struct wimlib_wim_info info;
3674 STRING_LIST(refglobs);
3676 if (cmd == CMD_MOUNTRW) {
3677 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3678 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3681 for_opt(c, mount_options) {
3683 case IMAGEX_ALLOW_OTHER_OPTION:
3684 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3686 case IMAGEX_CHECK_OPTION:
3687 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3689 case IMAGEX_DEBUG_OPTION:
3690 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3692 case IMAGEX_STREAMS_INTERFACE_OPTION:
3693 if (!tstrcasecmp(optarg, T("none")))
3694 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3695 else if (!tstrcasecmp(optarg, T("xattr")))
3696 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3697 else if (!tstrcasecmp(optarg, T("windows")))
3698 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3700 imagex_error(T("Unknown stream interface \"%"TS"\""),
3705 case IMAGEX_REF_OPTION:
3706 ret = string_list_append(&refglobs, optarg);
3708 goto out_free_refglobs;
3710 case IMAGEX_STAGING_DIR_OPTION:
3711 staging_dir = optarg;
3713 case IMAGEX_UNIX_DATA_OPTION:
3714 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3722 if (argc != 2 && argc != 3)
3727 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3728 imagex_progress_func, NULL);
3730 goto out_free_refglobs;
3732 wimlib_get_wim_info(wim, &info);
3735 /* Image explicitly specified. */
3736 image = wimlib_resolve_image(wim, argv[1]);
3738 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3742 /* No image specified; default to image 1, but only if the WIM
3743 * contains exactly one image. */
3745 if (info.image_count != 1) {
3746 imagex_error(T("\"%"TS"\" contains %d images; Please "
3747 "select one."), wimfile, info.image_count);
3755 if (refglobs.num_strings) {
3756 ret = wim_reference_globs(wim, &refglobs, open_flags);
3761 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3763 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3764 do_metadata_not_found_warning(wimfile, &info);
3766 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3768 image, wimfile, dir);
3774 string_list_destroy(&refglobs);
3780 goto out_free_refglobs;
3782 #endif /* WIM_MOUNTING_SUPPORTED */
3784 /* Rebuild a WIM file */
3786 imagex_optimize(int argc, tchar **argv, int cmd)
3789 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3790 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3791 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3792 uint32_t chunk_size = UINT32_MAX;
3793 uint32_t solid_chunk_size = UINT32_MAX;
3794 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3797 const tchar *wimfile;
3800 unsigned num_threads = 0;
3802 for_opt(c, optimize_options) {
3804 case IMAGEX_CHECK_OPTION:
3805 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3807 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3808 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3810 case IMAGEX_NOCHECK_OPTION:
3811 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3813 case IMAGEX_COMPRESS_OPTION:
3814 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3815 compression_type = get_compression_type(optarg, false);
3816 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3819 case IMAGEX_RECOMPRESS_OPTION:
3820 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3822 case IMAGEX_CHUNK_SIZE_OPTION:
3823 chunk_size = parse_chunk_size(optarg);
3824 if (chunk_size == UINT32_MAX)
3827 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3828 solid_chunk_size = parse_chunk_size(optarg);
3829 if (solid_chunk_size == UINT32_MAX)
3832 case IMAGEX_SOLID_COMPRESS_OPTION:
3833 solid_ctype = get_compression_type(optarg, true);
3834 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3837 case IMAGEX_SOLID_OPTION:
3838 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3839 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3841 case IMAGEX_NO_SOLID_SORT_OPTION:
3842 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3844 case IMAGEX_THREADS_OPTION:
3845 num_threads = parse_num_threads(optarg);
3846 if (num_threads == UINT_MAX)
3849 case IMAGEX_PIPABLE_OPTION:
3850 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3852 case IMAGEX_NOT_PIPABLE_OPTION:
3853 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3855 case IMAGEX_UNSAFE_COMPACT_OPTION:
3856 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3870 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3871 imagex_progress_func, NULL);
3875 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3876 /* Change compression type. */
3877 ret = wimlib_set_output_compression_type(wim, compression_type);
3879 goto out_wimlib_free;
3882 if (chunk_size != UINT32_MAX) {
3883 /* Change chunk size. */
3884 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3886 goto out_wimlib_free;
3888 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3889 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3891 goto out_wimlib_free;
3893 if (solid_chunk_size != UINT32_MAX) {
3894 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3896 goto out_wimlib_free;
3899 old_size = file_get_size(wimfile);
3900 tprintf(T("\"%"TS"\" original size: "), wimfile);
3902 tputs(T("Unknown"));
3904 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3906 ret = wimlib_overwrite(wim, write_flags, num_threads);
3908 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3909 goto out_wimlib_free;
3912 new_size = file_get_size(wimfile);
3913 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3915 tputs(T("Unknown"));
3917 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3919 tfputs(T("Space saved: "), stdout);
3920 if (new_size != -1 && old_size != -1) {
3921 tprintf(T("%lld KiB\n"),
3922 ((long long)old_size - (long long)new_size) >> 10);
3924 tputs(T("Unknown"));
3933 usage(CMD_OPTIMIZE, stderr);
3939 /* Split a WIM into a spanned set */
3941 imagex_split(int argc, tchar **argv, int cmd)
3945 int write_flags = 0;
3946 unsigned long part_size;
3951 for_opt(c, split_options) {
3953 case IMAGEX_CHECK_OPTION:
3954 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3956 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3957 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3969 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3970 if (tmp == argv[2] || *tmp) {
3971 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3972 imagex_error(T("The part size must be an integer or "
3973 "floating-point number of megabytes."));
3976 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3977 imagex_progress_func, NULL);
3981 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3987 usage(CMD_SPLIT, stderr);
3993 #if WIM_MOUNTING_SUPPORTED
3994 /* Unmounts a mounted WIM image. */
3996 imagex_unmount(int argc, tchar **argv, int cmd)
3999 int unmount_flags = 0;
4002 for_opt(c, unmount_options) {
4004 case IMAGEX_COMMIT_OPTION:
4005 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
4007 case IMAGEX_CHECK_OPTION:
4008 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
4010 case IMAGEX_REBUILD_OPTION:
4011 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
4013 case IMAGEX_LAZY_OPTION:
4014 case IMAGEX_FORCE_OPTION:
4015 /* Now, unmount is lazy by default. However, committing
4016 * the image will fail with
4017 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4018 * file descriptors on the WIM image. The
4019 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4020 * descriptors to be closed. */
4021 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4023 case IMAGEX_NEW_IMAGE_OPTION:
4024 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4035 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4036 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4037 imagex_error(T("--new-image is meaningless "
4038 "without --commit also specified!"));
4043 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4044 imagex_progress_func, NULL);
4046 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4047 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4049 "\tNote: Use --commit --force to force changes "
4050 "to be committed, regardless\n"
4051 "\t of open files.\n"));
4058 usage(CMD_UNMOUNT, stderr);
4063 #endif /* WIM_MOUNTING_SUPPORTED */
4066 * Add, delete, or rename files in a WIM image.
4069 imagex_update(int argc, tchar **argv, int cmd)
4071 const tchar *wimfile;
4075 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4076 int write_flags = 0;
4077 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4078 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4079 WIMLIB_ADD_FLAG_VERBOSE |
4080 WIMLIB_ADD_FLAG_WINCONFIG;
4081 int default_delete_flags = 0;
4082 unsigned num_threads = 0;
4084 tchar *cmd_file_contents;
4085 size_t cmd_file_nchars;
4086 struct wimlib_update_command *cmds;
4088 tchar *command_str = NULL;
4089 tchar *config_file = NULL;
4090 tchar *wimboot_config = NULL;
4092 for_opt(c, update_options) {
4094 /* Generic or write options */
4095 case IMAGEX_THREADS_OPTION:
4096 num_threads = parse_num_threads(optarg);
4097 if (num_threads == UINT_MAX)
4100 case IMAGEX_CHECK_OPTION:
4101 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4103 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4104 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4106 case IMAGEX_REBUILD_OPTION:
4107 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4109 case IMAGEX_COMMAND_OPTION:
4111 imagex_error(T("--command may only be specified "
4112 "one time. Please provide\n"
4113 " the update commands "
4114 "on standard input instead."));
4117 command_str = tstrdup(optarg);
4119 imagex_error(T("Out of memory!"));
4123 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4124 wimboot_config = optarg;
4126 /* Default delete options */
4127 case IMAGEX_FORCE_OPTION:
4128 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4130 case IMAGEX_RECURSIVE_OPTION:
4131 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4134 /* Global add option */
4135 case IMAGEX_CONFIG_OPTION:
4136 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4137 config_file = optarg;
4140 /* Default add options */
4141 case IMAGEX_VERBOSE_OPTION:
4142 /* No longer does anything. */
4144 case IMAGEX_DEREFERENCE_OPTION:
4145 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4147 case IMAGEX_UNIX_DATA_OPTION:
4148 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4150 case IMAGEX_NO_ACLS_OPTION:
4151 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4153 case IMAGEX_STRICT_ACLS_OPTION:
4154 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4156 case IMAGEX_NO_REPLACE_OPTION:
4157 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4159 case IMAGEX_UNSAFE_COMPACT_OPTION:
4160 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4169 if (argc != 1 && argc != 2)
4173 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4174 imagex_progress_func, NULL);
4176 goto out_free_command_str;
4179 /* Image explicitly specified. */
4180 image = wimlib_resolve_image(wim, argv[1]);
4181 ret = verify_image_exists_and_is_single(image, argv[1],
4184 goto out_wimlib_free;
4186 /* No image specified; default to image 1, but only if the WIM
4187 * contains exactly one image. */
4188 struct wimlib_wim_info info;
4190 wimlib_get_wim_info(wim, &info);
4191 if (info.image_count != 1) {
4192 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4193 wimfile, info.image_count);
4200 /* Read update commands from standard input, or the command string if
4203 cmd_file_contents = NULL;
4204 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4208 goto out_free_cmd_file_contents;
4210 } else if (!wimboot_config) {
4211 if (isatty(STDIN_FILENO)) {
4212 tputs(T("Reading update commands from standard input..."));
4213 recommend_man_page(CMD_UPDATE, stdout);
4215 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4216 if (!cmd_file_contents) {
4218 goto out_wimlib_free;
4221 /* Parse the update commands */
4222 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4226 goto out_free_cmd_file_contents;
4229 cmd_file_contents = NULL;
4234 /* Set default flags and capture config on the update commands */
4235 for (size_t i = 0; i < num_cmds; i++) {
4236 switch (cmds[i].op) {
4237 case WIMLIB_UPDATE_OP_ADD:
4238 cmds[i].add.add_flags |= default_add_flags;
4239 cmds[i].add.config_file = config_file;
4241 case WIMLIB_UPDATE_OP_DELETE:
4242 cmds[i].delete_.delete_flags |= default_delete_flags;
4249 /* Execute the update commands */
4250 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4254 if (wimboot_config) {
4255 /* --wimboot-config=FILE is short for an
4256 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4258 struct wimlib_update_command cmd;
4260 cmd.op = WIMLIB_UPDATE_OP_ADD;
4261 cmd.add.fs_source_path = wimboot_config;
4262 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4263 cmd.add.config_file = NULL;
4264 cmd.add.add_flags = 0;
4266 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4271 /* Overwrite the updated WIM */
4272 ret = wimlib_overwrite(wim, write_flags, num_threads);
4275 out_free_cmd_file_contents:
4276 free(cmd_file_contents);
4279 out_free_command_str:
4284 usage(CMD_UPDATE, stderr);
4287 goto out_free_command_str;
4290 /* Verify a WIM file. */
4292 imagex_verify(int argc, tchar **argv, int cmd)
4295 const tchar *wimfile;
4297 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4298 int verify_flags = 0;
4299 STRING_LIST(refglobs);
4302 for_opt(c, verify_options) {
4304 case IMAGEX_REF_OPTION:
4305 ret = string_list_append(&refglobs, optarg);
4307 goto out_free_refglobs;
4309 case IMAGEX_NOCHECK_OPTION:
4310 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4322 imagex_error(T("Must specify a WIM file!"));
4324 imagex_error(T("At most one WIM file can be specified!"));
4330 ret = wimlib_open_wim_with_progress(wimfile,
4333 imagex_progress_func,
4336 goto out_free_refglobs;
4338 ret = wim_reference_globs(wim, &refglobs, open_flags);
4340 goto out_wimlib_free;
4342 ret = wimlib_verify_wim(wim, verify_flags);
4344 tputc(T('\n'), stderr);
4345 imagex_error(T("\"%"TS"\" failed verification!"),
4347 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4348 refglobs.num_strings == 0)
4350 imagex_printf(T("Note: if this WIM file is not standalone, "
4351 "use the --ref option to specify the other parts.\n"));
4354 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4361 string_list_destroy(&refglobs);
4365 usage(CMD_VERIFY, stderr);
4367 goto out_free_refglobs;
4370 struct imagex_command {
4372 int (*func)(int argc, tchar **argv, int cmd);
4375 static const struct imagex_command imagex_commands[] = {
4376 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4377 [CMD_APPLY] = {T("apply"), imagex_apply},
4378 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4379 [CMD_DELETE] = {T("delete"), imagex_delete},
4380 [CMD_DIR ] = {T("dir"), imagex_dir},
4381 [CMD_EXPORT] = {T("export"), imagex_export},
4382 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4383 [CMD_INFO] = {T("info"), imagex_info},
4384 [CMD_JOIN] = {T("join"), imagex_join},
4385 #if WIM_MOUNTING_SUPPORTED
4386 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4387 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4389 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4390 [CMD_SPLIT] = {T("split"), imagex_split},
4391 #if WIM_MOUNTING_SUPPORTED
4392 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4394 [CMD_UPDATE] = {T("update"), imagex_update},
4395 [CMD_VERIFY] = {T("verify"), imagex_verify},
4400 /* Can be a directory or source list file. But source list file is probably
4401 * a rare use case, so just say directory. */
4402 # define SOURCE_STR T("DIRECTORY")
4404 /* Can only be a directory */
4405 # define TARGET_STR T("DIRECTORY")
4408 /* Can be a directory, NTFS volume, or source list file. */
4409 # define SOURCE_STR T("SOURCE")
4411 /* Can be a directory or NTFS volume. */
4412 # define TARGET_STR T("TARGET")
4416 static const tchar * const usage_strings[] = {
4419 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4420 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4421 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4422 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4423 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4424 " [--dereference] [--snapshot]\n"
4428 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4429 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4430 " [--no-attributes] [--rpfix] [--norpfix]\n"
4431 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4432 " [--compact=FORMAT]\n"
4436 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4437 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4438 " [--config=FILE] [--threads=NUM_THREADS]\n"
4439 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4440 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4441 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4446 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4450 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4454 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4455 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4456 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4457 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4458 " [--wimboot] [--solid]\n"
4462 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4463 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4464 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4465 " [--no-attributes] [--include-invalid-names]\n"
4466 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4470 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4471 " [--boot] [--check] [--nocheck] [--xml]\n"
4472 " [--extract-xml FILE] [--header] [--blobs]\n"
4473 " [--image-property NAME=VALUE]\n"
4477 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4479 #if WIM_MOUNTING_SUPPORTED
4482 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4483 " [--check] [--streams-interface=INTERFACE]\n"
4484 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4488 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4489 " [--check] [--streams-interface=INTERFACE]\n"
4490 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4496 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4497 " [--check] [--nocheck] [--solid]\n"
4502 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4504 #if WIM_MOUNTING_SUPPORTED
4507 " %"TS" DIRECTORY\n"
4508 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4513 " %"TS" WIMFILE [IMAGE]\n"
4514 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4515 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4516 " [--command=STRING] [--wimboot-config=FILE]\n"
4521 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4525 static const tchar *invocation_name;
4526 static int invocation_cmd = CMD_NONE;
4528 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4530 static tchar buf[50];
4532 if (cmd == CMD_NONE)
4533 return T("wimlib-imagex");
4535 if (only_short_form || invocation_cmd != CMD_NONE) {
4536 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4538 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4539 imagex_commands[cmd].name);
4547 static const tchar * const fmt =
4549 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
4550 "Copyright (C) 2012-2018 Eric Biggers\n"
4551 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4552 "This is free software: you are free to change and redistribute it.\n"
4553 "There is NO WARRANTY, to the extent permitted by law.\n"
4555 "Report bugs to "PACKAGE_BUGREPORT".\n"
4557 tfprintf(stdout, fmt, wimlib_get_version_string());
4561 do_common_options(int *argc_p, tchar **argv, int cmd)
4567 for (i = 1; i < argc; i++) {
4569 if (p[0] == T('-') && p[1] == T('-')) {
4571 if (!tstrcmp(p, T("help"))) {
4572 if (cmd == CMD_NONE)
4577 } else if (!tstrcmp(p, T("version"))) {
4580 } else if (!tstrcmp(p, T("quiet"))) {
4581 imagex_suppress_output();
4582 memmove(&argv[i], &argv[i + 1],
4583 (argc - i) * sizeof(argv[i]));
4586 } else if (!*p) /* reached "--", no more options */
4595 print_usage_string(int cmd, FILE *fp)
4597 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4601 recommend_man_page(int cmd, FILE *fp)
4603 const tchar *format_str;
4605 format_str = T("Some uncommon options are not listed;\n"
4606 "See %"TS".pdf in the doc directory for more details.\n");
4608 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4610 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4614 usage(int cmd, FILE *fp)
4616 tfprintf(fp, T("Usage:\n"));
4617 print_usage_string(cmd, fp);
4618 tfprintf(fp, T("\n"));
4619 recommend_man_page(cmd, fp);
4625 tfprintf(fp, T("Usage:\n"));
4626 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4627 print_usage_string(cmd, fp);
4628 tfprintf(fp, T("\n"));
4630 static const tchar * const extra =
4633 " %"TS" --version\n"
4636 tfprintf(fp, extra, invocation_name, invocation_name);
4638 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4639 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4640 "For some commands IMAGE may be \"all\".\n"
4642 recommend_man_page(CMD_NONE, fp);
4646 extern int wmain(int argc, wchar_t **argv);
4650 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4651 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4652 * something else), while on Windows the command arguments will be UTF-16LE
4653 * encoded 'wchar_t' strings. */
4655 main(int argc, tchar **argv)
4661 imagex_info_file = stdout;
4662 invocation_name = tbasename(argv[0]);
4665 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4666 if (igcase != NULL) {
4667 if (!tstrcmp(igcase, T("no")) ||
4668 !tstrcmp(igcase, T("0")))
4669 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4670 else if (!tstrcmp(igcase, T("yes")) ||
4671 !tstrcmp(igcase, T("1")))
4672 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4675 "WARNING: Ignoring unknown setting of "
4676 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4681 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4683 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4684 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4685 for (int i = 0; i < CMD_MAX; i++) {
4686 if (!tstrcmp(invocation_name + 3,
4687 imagex_commands[i].name))
4696 /* Unless already known from the invocation name, determine which
4697 * command was specified. */
4698 if (cmd == CMD_NONE) {
4700 imagex_error(T("No command specified!\n"));
4704 for (int i = 0; i < CMD_MAX; i++) {
4705 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4710 if (cmd != CMD_NONE) {
4716 /* Handle common options. May exit early (for --help or --version). */
4717 do_common_options(&argc, argv, cmd);
4719 /* Bail if a valid command was not specified. */
4720 if (cmd == CMD_NONE) {
4721 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4726 /* Enable warning and error messages in wimlib to be more user-friendly.
4728 wimlib_set_print_errors(true);
4730 /* Initialize wimlib. */
4731 ret = wimlib_global_init(init_flags);
4733 goto out_check_status;
4735 /* Call the command handler function. */
4736 ret = imagex_commands[cmd].func(argc, argv, cmd);
4738 /* Check for error writing to standard output, especially since for some
4739 * commands, writing to standard output is part of the program's actual
4740 * behavior and not just for informational purposes. */
4741 if (ferror(stdout) || fclose(stdout)) {
4742 imagex_error_with_errno(T("error writing to standard output"));
4747 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4748 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4749 * error code from which an error message can be printed. */
4751 imagex_error(T("Exiting with error code %d:\n"
4753 wimlib_get_error_string(ret));
4754 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4755 imagex_error_with_errno(T("errno"));
4757 /* Make wimlib free any resources it's holding (although this is not
4758 * strictly necessary because the process is ending anyway). */
4759 wimlib_global_cleanup();