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 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
52 # include "imagex-win32.h"
53 # define print_security_descriptor win32_print_security_descriptor
56 # include <langinfo.h>
57 # define print_security_descriptor default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
61 /* NetBSD is missing getopt_long_only() but has getopt_long() */
62 #ifndef HAVE_GETOPT_LONG_ONLY
63 # define getopt_long_only getopt_long
67 /* Don't confuse the user by presenting the mounting commands on Windows when
68 * they will never work. However on UNIX-like systems we always present them,
69 * even if WITH_FUSE is not defined at this point, as to not tie the build of
70 * wimlib-imagex to a specific build of wimlib. */
72 # define WIM_MOUNTING_SUPPORTED 0
74 # define WIM_MOUNTING_SUPPORTED 1
77 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
80 is_any_path_separator(tchar c)
82 return c == T('/') || c == T('\\');
85 /* Like basename(), but handles both forward and backwards slashes. */
87 tbasename(tchar *path)
89 tchar *p = tstrchr(path, T('\0'));
94 if (!is_any_path_separator(*--p))
102 if (is_any_path_separator(*--p))
107 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
121 #if WIM_MOUNTING_SUPPORTED
127 #if WIM_MOUNTING_SUPPORTED
135 static void usage(int cmd, FILE *fp);
136 static void usage_all(FILE *fp);
137 static void recommend_man_page(int cmd, FILE *fp);
138 static const tchar *get_cmd_string(int cmd, bool only_short_form);
140 static FILE *imagex_info_file;
142 #define imagex_printf(format, ...) \
143 if (imagex_info_file) \
144 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
146 static void imagex_suppress_output(void)
148 imagex_info_file = NULL;
151 static void imagex_output_to_stderr(void)
153 if (imagex_info_file)
154 imagex_info_file = stderr;
157 static void imagex_flush_output(void)
159 if (imagex_info_file)
160 fflush(imagex_info_file);
164 IMAGEX_ALLOW_OTHER_OPTION,
168 IMAGEX_CHUNK_SIZE_OPTION,
169 IMAGEX_COMMAND_OPTION,
170 IMAGEX_COMMIT_OPTION,
171 IMAGEX_COMPACT_OPTION,
172 IMAGEX_COMPRESS_OPTION,
173 IMAGEX_CONFIG_OPTION,
175 IMAGEX_DELTA_FROM_OPTION,
176 IMAGEX_DEREFERENCE_OPTION,
177 IMAGEX_DEST_DIR_OPTION,
178 IMAGEX_DETAILED_OPTION,
179 IMAGEX_EXTRACT_XML_OPTION,
182 IMAGEX_HEADER_OPTION,
183 IMAGEX_IMAGE_PROPERTY_OPTION,
184 IMAGEX_INCLUDE_INTEGRITY_OPTION,
185 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
187 IMAGEX_METADATA_OPTION,
188 IMAGEX_NEW_IMAGE_OPTION,
189 IMAGEX_NOCHECK_OPTION,
190 IMAGEX_NORPFIX_OPTION,
191 IMAGEX_NOT_PIPABLE_OPTION,
192 IMAGEX_NO_ACLS_OPTION,
193 IMAGEX_NO_ATTRIBUTES_OPTION,
194 IMAGEX_NO_GLOBS_OPTION,
195 IMAGEX_NO_REPLACE_OPTION,
196 IMAGEX_NO_SOLID_SORT_OPTION,
197 IMAGEX_NULLGLOB_OPTION,
198 IMAGEX_ONE_FILE_ONLY_OPTION,
200 IMAGEX_PIPABLE_OPTION,
201 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
202 IMAGEX_REBUILD_OPTION,
203 IMAGEX_RECOMPRESS_OPTION,
204 IMAGEX_RECURSIVE_OPTION,
207 IMAGEX_SNAPSHOT_OPTION,
209 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
210 IMAGEX_SOLID_COMPRESS_OPTION,
212 IMAGEX_SOURCE_LIST_OPTION,
213 IMAGEX_STAGING_DIR_OPTION,
214 IMAGEX_STREAMS_INTERFACE_OPTION,
215 IMAGEX_STRICT_ACLS_OPTION,
216 IMAGEX_THREADS_OPTION,
217 IMAGEX_TO_STDOUT_OPTION,
218 IMAGEX_UNIX_DATA_OPTION,
219 IMAGEX_UNSAFE_COMPACT_OPTION,
220 IMAGEX_UPDATE_OF_OPTION,
221 IMAGEX_VERBOSE_OPTION,
222 IMAGEX_WIMBOOT_CONFIG_OPTION,
223 IMAGEX_WIMBOOT_OPTION,
227 static const struct option apply_options[] = {
228 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
229 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
230 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
231 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
232 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
233 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
234 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
235 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
236 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
237 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
238 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
239 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
240 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
244 static const struct option capture_or_append_options[] = {
245 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
246 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
247 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
248 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
249 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
250 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
251 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
252 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
253 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
254 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
255 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
256 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
257 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
258 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
259 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
260 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
261 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
262 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
263 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
264 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
265 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
266 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
267 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
268 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
269 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
270 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
271 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
272 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
273 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
274 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
275 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
276 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
280 static const struct option delete_options[] = {
281 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
282 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
283 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
284 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
288 static const struct option dir_options[] = {
289 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
290 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
291 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
292 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
296 static const struct option export_options[] = {
297 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
298 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
299 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
300 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
301 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
302 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
303 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
304 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
305 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
306 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
307 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
308 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
309 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
310 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
311 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
312 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
313 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
314 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
315 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
319 static const struct option extract_options[] = {
320 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
321 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
322 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
323 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
324 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
325 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
326 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
327 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
328 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
329 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
330 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
331 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
332 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
333 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
334 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
335 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
336 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
340 static const struct option info_options[] = {
341 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
342 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
343 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
344 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
345 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
346 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
347 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
348 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
349 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
350 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
351 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
355 static const struct option join_options[] = {
356 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
357 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
361 #if WIM_MOUNTING_SUPPORTED
362 static const struct option mount_options[] = {
363 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
364 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
365 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
366 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
367 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
368 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
369 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
374 static const struct option optimize_options[] = {
375 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
376 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
377 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
378 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
379 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
380 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
381 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
382 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
383 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
384 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
385 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
386 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
387 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
388 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
389 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
393 static const struct option split_options[] = {
394 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
395 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
399 #if WIM_MOUNTING_SUPPORTED
400 static const struct option unmount_options[] = {
401 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
402 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
403 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
404 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
405 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
406 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
411 static const struct option update_options[] = {
412 /* Careful: some of the options here set the defaults for update
413 * commands, but the flags given to an actual update command (and not to
414 * `imagex update' itself are also handled in
415 * update_command_add_option(). */
416 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
417 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
418 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
419 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
420 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
421 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
423 /* Default delete options */
424 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
425 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
427 /* Global add option */
428 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
430 /* Default add options */
431 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
432 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
433 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
434 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
435 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
436 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
437 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
438 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
443 static const struct option verify_options[] = {
444 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
445 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
451 # define _format_attribute(type, format_str, args_start) \
452 __attribute__((format(type, format_str, args_start)))
454 # define _format_attribute(type, format_str, args_start)
457 /* Print formatted error message to stderr. */
458 static void _format_attribute(printf, 1, 2)
459 imagex_error(const tchar *format, ...)
462 va_start(va, format);
463 tfputs(T("ERROR: "), stderr);
464 tvfprintf(stderr, format, va);
465 tputc(T('\n'), stderr);
469 /* Print formatted error message to stderr. */
470 static void _format_attribute(printf, 1, 2)
471 imagex_error_with_errno(const tchar *format, ...)
473 int errno_save = errno;
475 va_start(va, format);
476 tfputs(T("ERROR: "), stderr);
477 tvfprintf(stderr, format, va);
478 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
483 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
485 if (image == WIMLIB_NO_IMAGE) {
486 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
487 " Please specify a 1-based image index or "
488 "image name. To list the images\n"
489 " contained in the WIM archive, run\n"
491 " %"TS" \"%"TS"\"\n"),
492 image_name, wim_name,
493 get_cmd_string(CMD_INFO, false), wim_name);
494 return WIMLIB_ERR_INVALID_IMAGE;
500 verify_image_is_single(int image)
502 if (image == WIMLIB_ALL_IMAGES) {
503 imagex_error(T("Cannot specify all images for this action!"));
504 return WIMLIB_ERR_INVALID_IMAGE;
510 verify_image_exists_and_is_single(int image, const tchar *image_name,
511 const tchar *wim_name)
514 ret = verify_image_exists(image, image_name, wim_name);
516 ret = verify_image_is_single(image);
521 print_available_compression_types(FILE *fp)
523 static const tchar * const s =
525 "Available compression types:\n"
528 " xpress (alias: \"fast\")\n"
529 " lzx (alias: \"maximum\") (default for capture)\n"
530 " lzms (alias: \"recovery\")\n"
536 /* Parse the argument to --compress or --solid-compress */
538 get_compression_type(tchar *optarg, bool solid)
541 unsigned int compression_level = 0;
544 plevel = tstrchr(optarg, T(':'));
550 ultmp = tstrtoul(plevel, &ptmp, 10);
551 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
552 imagex_error(T("Compression level must be a positive integer! "
553 "e.g. --compress=lzx:80"));
554 return WIMLIB_COMPRESSION_TYPE_INVALID;
556 compression_level = ultmp;
559 if (!tstrcasecmp(optarg, T("maximum")) ||
560 !tstrcasecmp(optarg, T("lzx")) ||
561 !tstrcasecmp(optarg, T("max"))) {
562 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
563 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
564 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
565 } else if (!tstrcasecmp(optarg, T("recovery"))) {
569 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
570 " differently from DISM. Instead, you typically want to use '--solid' to\n"
571 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
572 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
573 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
574 " of '--compress=recovery'.\n"));
576 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
577 } else if (!tstrcasecmp(optarg, T("lzms"))) {
578 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
579 } else if (!tstrcasecmp(optarg, T("none"))) {
580 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
582 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
583 print_available_compression_types(stderr);
584 return WIMLIB_COMPRESSION_TYPE_INVALID;
587 if (compression_level != 0)
588 wimlib_set_default_compression_level(ctype, compression_level);
592 /* Parse the argument to --compact */
594 set_compact_mode(const tchar *arg, int *extract_flags)
597 if (!tstrcasecmp(arg, T("xpress4k")))
598 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
599 else if (!tstrcasecmp(arg, T("xpress8k")))
600 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
601 else if (!tstrcasecmp(arg, T("xpress16k")))
602 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
603 else if (!tstrcasecmp(arg, T("lzx")))
604 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
607 *extract_flags |= flag;
612 "\"%"TS"\" is not a recognized System Compression format. The options are:"
614 " --compact=xpress4k\n"
615 " --compact=xpress8k\n"
616 " --compact=xpress16k\n"
625 unsigned num_strings;
626 unsigned num_alloc_strings;
629 #define STRING_LIST_INITIALIZER \
630 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
632 #define STRING_LIST(_strings) \
633 struct string_list _strings = STRING_LIST_INITIALIZER
636 string_list_append(struct string_list *list, tchar *glob)
638 unsigned num_alloc_strings = list->num_alloc_strings;
640 if (list->num_strings == num_alloc_strings) {
643 num_alloc_strings += 4;
644 new_strings = realloc(list->strings,
645 sizeof(list->strings[0]) * num_alloc_strings);
647 imagex_error(T("Out of memory!"));
650 list->strings = new_strings;
651 list->num_alloc_strings = num_alloc_strings;
653 list->strings[list->num_strings++] = glob;
658 string_list_destroy(struct string_list *list)
664 wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags)
666 return wimlib_reference_resource_files(wim, (const tchar **)list->strings,
668 WIMLIB_REF_FLAG_GLOB_ENABLE,
673 append_image_property_argument(struct string_list *image_properties)
675 if (!tstrchr(optarg, '=')) {
676 imagex_error(T("'--image-property' argument "
677 "must be in the form NAME=VALUE"));
680 return string_list_append(image_properties, optarg);
684 apply_image_properties(struct string_list *image_properties,
685 WIMStruct *wim, int image, bool *any_changes_ret)
687 bool any_changes = false;
688 for (unsigned i = 0; i < image_properties->num_strings; i++) {
690 const tchar *current_value;
693 name = image_properties->strings[i];
694 value = tstrchr(name, '=');
697 current_value = wimlib_get_image_property(wim, image, name);
698 if (current_value && !tstrcmp(current_value, value)) {
699 imagex_printf(T("The %"TS" property of image %d "
700 "already has value \"%"TS"\".\n"),
703 imagex_printf(T("Setting the %"TS" property of image "
704 "%d to \"%"TS"\".\n"),
706 ret = wimlib_set_image_property(wim, image, name, value);
713 *any_changes_ret = any_changes;
718 do_resource_not_found_warning(const tchar *wimfile,
719 const struct wimlib_wim_info *info,
720 const struct string_list *refglobs)
722 if (info->total_parts > 1) {
723 if (refglobs->num_strings == 0) {
724 imagex_error(T("\"%"TS"\" is part of a split WIM. "
725 "Use --ref to specify the other parts."),
728 imagex_error(T("Perhaps the '--ref' argument did not "
729 "specify all other parts of the split "
733 imagex_error(T("If this is a delta WIM, use the --ref argument "
734 "to specify the WIM(s) on which it is based."));
739 do_metadata_not_found_warning(const tchar *wimfile,
740 const struct wimlib_wim_info *info)
742 if (info->part_number != 1) {
743 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
744 " You must specify the first part."),
749 /* Returns the size of a file given its name, or -1 if the file does not exist
750 * or its size cannot be determined. */
752 file_get_size(const tchar *filename)
755 if (tstat(filename, &st) == 0)
762 PARSE_STRING_SUCCESS = 0,
763 PARSE_STRING_FAILURE = 1,
764 PARSE_STRING_NONE = 2,
768 * Parses a string token from an array of characters.
770 * Tokens are either whitespace-delimited, or double or single-quoted.
772 * @line_p: Pointer to the pointer to the line of data. Will be updated
773 * to point past the string token iff the return value is
774 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
777 * @len_p: @len_p initially stores the length of the line of data, which may
778 * be 0, and it will be updated to the number of bytes remaining in
779 * the line iff the return value is PARSE_STRING_SUCCESS.
781 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
782 * parsed string token will be returned here.
784 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
785 * PARSE_STRING_FAILURE if the data was invalid due to a missing
786 * closing quote; or PARSE_STRING_NONE if the line ended before the
787 * beginning of a string token was found.
790 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
793 tchar *line = *line_p;
797 /* Skip leading whitespace */
800 return PARSE_STRING_NONE;
801 if (!istspace(*line) && *line != T('\0'))
807 if (quote_char == T('"') || quote_char == T('\'')) {
812 line = tmemchr(line, quote_char, len);
814 imagex_error(T("Missing closing quote: %"TS), fn - 1);
815 return PARSE_STRING_FAILURE;
818 /* Unquoted string. Go until whitespace. Line is terminated
819 * by '\0', so no need to check 'len'. */
823 } while (!istspace(*line) && *line != T('\0'));
830 return PARSE_STRING_SUCCESS;
833 /* Parses a line of data (not an empty line or comment) in the source list file
834 * format. (See the man page for 'wimlib-imagex capture' for details on this
835 * format and the meaning.)
837 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
838 * len == 0. The data in @line will be modified by this function call.
840 * @len: Length of the line of data.
842 * @source: On success, the capture source and target described by the line is
843 * written into this destination. Note that it will contain pointers
844 * to data in the @line array.
846 * Returns true if the line was valid; false otherwise. */
848 parse_source_list_line(tchar *line, size_t len,
849 struct wimlib_capture_source *source)
853 ret = parse_string(&line, &len, &source->fs_source_path);
854 if (ret != PARSE_STRING_SUCCESS)
856 ret = parse_string(&line, &len, &source->wim_target_path);
857 if (ret == PARSE_STRING_NONE)
858 source->wim_target_path = source->fs_source_path;
859 return ret != PARSE_STRING_FAILURE;
862 /* Returns %true if the given line of length @len > 0 is a comment or empty line
863 * in the source list file format. */
865 is_comment_line(const tchar *line, size_t len)
868 if (*line == T('#') || *line == T(';'))
870 if (!istspace(*line) && *line != T('\0'))
880 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
883 tchar *contents = *contents_p;
884 size_t nchars = *nchars_p;
887 for (i = 0; i < nchars; i++)
888 if (contents[i] == T('\n'))
891 /* Handle last line not terminated by a newline */
892 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
893 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
895 imagex_error(T("Out of memory!"));
898 contents[nchars] = T('\n');
899 *contents_p = contents;
907 /* Parses a file in the source list format. (See the man page for
908 * 'wimlib-imagex capture' for details on this format and the meaning.)
910 * @source_list_contents: Contents of the source list file. Note that this
911 * buffer will be modified to save memory allocations,
912 * and cannot be freed until the returned array of
913 * wimlib_capture_source's has also been freed.
915 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
918 * @nsources_ret: On success, the length of the returned array is
921 * Returns: An array of `struct wimlib_capture_source's that can be passed to
922 * the wimlib_add_image_multisource() function to specify how a WIM image is to
924 static struct wimlib_capture_source *
925 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
926 size_t *nsources_ret)
930 struct wimlib_capture_source *sources;
933 nlines = text_file_count_lines(source_list_contents_p,
934 &source_list_nchars);
938 /* Always allocate at least 1 slot, just in case the implementation of
939 * calloc() returns NULL if 0 bytes are requested. */
940 sources = calloc(nlines ?: 1, sizeof(*sources));
942 imagex_error(T("out of memory"));
945 p = *source_list_contents_p;
947 for (i = 0; i < nlines; i++) {
948 /* XXX: Could use rawmemchr() here instead, but it may not be
949 * available on all platforms. */
950 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
951 size_t len = endp - p + 1;
953 if (!is_comment_line(p, len)) {
954 if (!parse_source_list_line(p, len, &sources[j++])) {
966 /* Reads the contents of a file into memory. */
968 file_get_contents(const tchar *filename, size_t *len_ret)
975 if (tstat(filename, &stbuf) != 0) {
976 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
981 fp = tfopen(filename, T("rb"));
983 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
987 buf = malloc(len ? len : 1);
989 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
990 "contents of file \"%"TS"\""), len, filename);
993 if (fread(buf, 1, len, fp) != len) {
994 imagex_error_with_errno(T("Failed to read %zu bytes from the "
995 "file \"%"TS"\""), len, filename);
1009 /* Read standard input until EOF and return the full contents in a malloc()ed
1010 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1013 stdin_get_contents(size_t *len_ret)
1015 /* stdin can, of course, be a pipe or other non-seekable file, so the
1016 * total length of the data cannot be pre-determined */
1018 size_t newlen = 1024;
1022 char *p = realloc(buf, newlen);
1023 size_t bytes_read, bytes_to_read;
1025 imagex_error(T("out of memory while reading stdin"));
1029 bytes_to_read = newlen - pos;
1030 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1032 if (bytes_read != bytes_to_read) {
1037 imagex_error_with_errno(T("error reading stdin"));
1051 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1054 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1056 *num_tchars_ret = num_bytes;
1058 #else /* !__WIN32__ */
1059 /* On Windows, translate the text to UTF-16LE */
1063 if (num_bytes >= 2 &&
1064 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1065 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1067 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1068 * with something that looks like an ASCII character encoded as
1069 * a UTF-16LE code unit. Assume the file is encoded as
1070 * UTF-16LE. This is not a 100% reliable check. */
1071 num_wchars = num_bytes / 2;
1072 text_wstr = (wchar_t*)text;
1074 /* File does not look like UTF-16LE. Assume it is encoded in
1075 * the current Windows code page. I think these are always
1076 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1077 * should work as expected. */
1078 text_wstr = win32_mbs_to_wcs(text,
1083 *num_tchars_ret = num_wchars;
1085 #endif /* __WIN32__ */
1089 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1094 contents = file_get_contents(filename, &num_bytes);
1097 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1101 stdin_get_text_contents(size_t *num_tchars_ret)
1106 contents = stdin_get_contents(&num_bytes);
1109 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1112 #define TO_PERCENT(numerator, denominator) \
1113 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1115 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1116 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1117 #define KIBIBYTE_MIN_NBYTES 10000ULL
1120 get_unit(uint64_t total_bytes, const tchar **name_ret)
1122 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1123 *name_ret = T("GiB");
1125 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1126 *name_ret = T("MiB");
1128 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1129 *name_ret = T("KiB");
1132 *name_ret = T("bytes");
1137 static struct wimlib_progress_info_scan last_scan_progress;
1140 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1142 uint64_t prev_count, cur_count;
1144 prev_count = last_scan_progress.num_nondirs_scanned +
1145 last_scan_progress.num_dirs_scanned;
1146 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1148 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1149 cur_count % 128 == 0)
1151 unsigned unit_shift;
1152 const tchar *unit_name;
1154 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1155 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1156 "%"PRIu64" directories) "),
1157 scan->num_bytes_scanned >> unit_shift,
1159 scan->num_nondirs_scanned,
1160 scan->num_dirs_scanned);
1161 last_scan_progress = *scan;
1164 /* Progress callback function passed to various wimlib functions. */
1165 static enum wimlib_progress_status
1166 imagex_progress_func(enum wimlib_progress_msg msg,
1167 union wimlib_progress_info *info,
1168 void *_ignored_context)
1170 unsigned percent_done;
1171 unsigned unit_shift;
1172 const tchar *unit_name;
1175 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1177 static bool started;
1179 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1180 imagex_printf(T("Using %"TS" compression "
1181 "with %u thread%"TS"\n"),
1182 wimlib_get_compression_type_string(
1183 info->write_streams.compression_type),
1184 info->write_streams.num_threads,
1185 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1190 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1191 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1192 info->write_streams.total_bytes);
1194 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1195 info->write_streams.completed_bytes >> unit_shift,
1197 info->write_streams.total_bytes >> unit_shift,
1200 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1201 imagex_printf(T("\n"));
1203 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1204 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1205 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1206 imagex_printf(T("\n"));
1208 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1209 info->scan.wim_target_path);
1211 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1213 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1214 switch (info->scan.status) {
1215 case WIMLIB_SCAN_DENTRY_OK:
1216 report_scan_progress(&info->scan, false);
1218 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1219 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1221 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1222 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1223 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1225 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1226 /* Symlink fixups are enabled by default. This is
1227 * mainly intended for Windows, which for some reason
1228 * uses absolute junctions (with drive letters!) in the
1229 * default installation. On UNIX-like systems, warn the
1230 * user when fixing the target of an absolute symbolic
1231 * link, so they know to disable this if they want. */
1233 imagex_printf(T("\nWARNING: Adjusted target of "
1234 "absolute symbolic link \"%"TS"\"\n"
1235 " (Use --norpfix to capture "
1236 "absolute symbolic links as-is)\n"),
1237 info->scan.cur_path);
1244 case WIMLIB_PROGRESS_MSG_SCAN_END:
1245 report_scan_progress(&info->scan, true);
1246 imagex_printf(T("\n"));
1248 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1249 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1250 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1251 info->integrity.total_bytes);
1252 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1253 "of %"PRIu64" %"TS" (%u%%) done"),
1254 info->integrity.filename,
1255 info->integrity.completed_bytes >> unit_shift,
1257 info->integrity.total_bytes >> unit_shift,
1260 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1261 imagex_printf(T("\n"));
1263 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1264 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1265 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1266 info->integrity.total_bytes);
1267 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1268 "of %"PRIu64" %"TS" (%u%%) done"),
1269 info->integrity.completed_bytes >> unit_shift,
1271 info->integrity.total_bytes >> unit_shift,
1274 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1275 imagex_printf(T("\n"));
1277 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1278 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1279 "to %"TS" \"%"TS"\"\n"),
1280 info->extract.image,
1281 info->extract.image_name,
1282 info->extract.wimfile_name,
1283 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1284 T("NTFS volume") : T("directory")),
1285 info->extract.target);
1287 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1288 if (info->extract.end_file_count >= 2000) {
1289 percent_done = TO_PERCENT(info->extract.current_file_count,
1290 info->extract.end_file_count);
1291 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1292 info->extract.current_file_count,
1293 info->extract.end_file_count, percent_done);
1294 if (info->extract.current_file_count == info->extract.end_file_count)
1295 imagex_printf(T("\n"));
1298 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1299 percent_done = TO_PERCENT(info->extract.completed_bytes,
1300 info->extract.total_bytes);
1301 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1302 imagex_printf(T("\rExtracting file data: "
1303 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1304 info->extract.completed_bytes >> unit_shift,
1306 info->extract.total_bytes >> unit_shift,
1309 if (info->extract.completed_bytes >= info->extract.total_bytes)
1310 imagex_printf(T("\n"));
1312 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1313 if (info->extract.end_file_count >= 2000) {
1314 percent_done = TO_PERCENT(info->extract.current_file_count,
1315 info->extract.end_file_count);
1316 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1317 info->extract.current_file_count,
1318 info->extract.end_file_count, percent_done);
1319 if (info->extract.current_file_count == info->extract.end_file_count)
1320 imagex_printf(T("\n"));
1323 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1324 if (info->extract.total_parts != 1) {
1325 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1326 info->extract.part_number,
1327 info->extract.total_parts);
1330 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1331 percent_done = TO_PERCENT(info->split.completed_bytes,
1332 info->split.total_bytes);
1333 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1334 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1335 "%"PRIu64" %"TS" (%u%%) written\n"),
1336 info->split.part_name,
1337 info->split.cur_part_number,
1338 info->split.total_parts,
1339 info->split.completed_bytes >> unit_shift,
1341 info->split.total_bytes >> unit_shift,
1345 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1346 if (info->split.completed_bytes == info->split.total_bytes) {
1347 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1348 info->split.cur_part_number,
1349 info->split.total_parts);
1352 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1353 switch (info->update.command->op) {
1354 case WIMLIB_UPDATE_OP_DELETE:
1355 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1356 info->update.command->delete_.wim_path);
1358 case WIMLIB_UPDATE_OP_RENAME:
1359 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1360 info->update.command->rename.wim_source_path,
1361 info->update.command->rename.wim_target_path);
1363 case WIMLIB_UPDATE_OP_ADD:
1368 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1369 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1370 info->replace.path_in_wim);
1372 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1373 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1374 info->wimboot_exclude.path_in_wim);
1376 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1377 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1378 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1379 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1380 info->unmount.mounted_wim,
1381 info->unmount.mounted_image);
1383 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1384 info->unmount.mounted_wim,
1385 info->unmount.mounted_image);
1386 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1390 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1391 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1392 info->verify_image.current_image,
1393 info->verify_image.total_images);
1395 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1396 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1397 info->verify_streams.total_bytes);
1398 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1399 imagex_printf(T("\rVerifying file data: "
1400 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1401 info->verify_streams.completed_bytes >> unit_shift,
1403 info->verify_streams.total_bytes >> unit_shift,
1406 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1407 imagex_printf(T("\n"));
1412 imagex_flush_output();
1413 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1417 parse_num_threads(const tchar *optarg)
1420 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1421 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1422 imagex_error(T("Number of threads must be a non-negative integer!"));
1430 parse_chunk_size(const tchar *optarg)
1433 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1434 if (chunk_size == 0) {
1435 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1436 " with optional K, M, or G suffix"));
1440 if (*tmp == T('k') || *tmp == T('K')) {
1443 } else if (*tmp == T('m') || *tmp == T('M')) {
1446 } else if (*tmp == T('g') || *tmp == T('G')) {
1450 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1451 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1455 if (chunk_size >= UINT32_MAX) {
1456 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1464 * Parse an option passed to an update command.
1466 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1469 * @option: Text string for the option (beginning with --)
1471 * @cmd: `struct wimlib_update_command' that is being constructed for
1474 * Returns true if the option was recognized; false if not.
1477 update_command_add_option(int op, const tchar *option,
1478 struct wimlib_update_command *cmd)
1480 bool recognized = true;
1482 case WIMLIB_UPDATE_OP_ADD:
1483 if (!tstrcmp(option, T("--verbose")))
1484 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1485 else if (!tstrcmp(option, T("--unix-data")))
1486 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1487 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1488 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1489 else if (!tstrcmp(option, T("--strict-acls")))
1490 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1491 else if (!tstrcmp(option, T("--dereference")))
1492 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1493 else if (!tstrcmp(option, T("--no-replace")))
1494 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1498 case WIMLIB_UPDATE_OP_DELETE:
1499 if (!tstrcmp(option, T("--force")))
1500 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1501 else if (!tstrcmp(option, T("--recursive")))
1502 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1513 /* How many nonoption arguments each `imagex update' command expects */
1514 static const unsigned update_command_num_nonoptions[] = {
1515 [WIMLIB_UPDATE_OP_ADD] = 2,
1516 [WIMLIB_UPDATE_OP_DELETE] = 1,
1517 [WIMLIB_UPDATE_OP_RENAME] = 2,
1521 update_command_add_nonoption(int op, const tchar *nonoption,
1522 struct wimlib_update_command *cmd,
1523 unsigned num_nonoptions)
1526 case WIMLIB_UPDATE_OP_ADD:
1527 if (num_nonoptions == 0)
1528 cmd->add.fs_source_path = (tchar*)nonoption;
1530 cmd->add.wim_target_path = (tchar*)nonoption;
1532 case WIMLIB_UPDATE_OP_DELETE:
1533 cmd->delete_.wim_path = (tchar*)nonoption;
1535 case WIMLIB_UPDATE_OP_RENAME:
1536 if (num_nonoptions == 0)
1537 cmd->rename.wim_source_path = (tchar*)nonoption;
1539 cmd->rename.wim_target_path = (tchar*)nonoption;
1545 * Parse a command passed on stdin to `imagex update'.
1547 * @line: Text of the command.
1548 * @len: Length of the line, including a null terminator
1551 * @command: A `struct wimlib_update_command' to fill in from the parsed
1554 * @line_number: Line number of the command, for diagnostics.
1556 * Returns true on success; returns false on parse error.
1559 parse_update_command(tchar *line, size_t len,
1560 struct wimlib_update_command *command,
1564 tchar *command_name;
1566 size_t num_nonoptions;
1568 /* Get the command name ("add", "delete", "rename") */
1569 ret = parse_string(&line, &len, &command_name);
1570 if (ret != PARSE_STRING_SUCCESS)
1573 if (!tstrcasecmp(command_name, T("add"))) {
1574 op = WIMLIB_UPDATE_OP_ADD;
1575 } else if (!tstrcasecmp(command_name, T("delete"))) {
1576 op = WIMLIB_UPDATE_OP_DELETE;
1577 } else if (!tstrcasecmp(command_name, T("rename"))) {
1578 op = WIMLIB_UPDATE_OP_RENAME;
1580 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1581 command_name, line_number);
1586 /* Parse additional options and non-options as needed */
1591 ret = parse_string(&line, &len, &next_string);
1592 if (ret == PARSE_STRING_NONE) /* End of line */
1594 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1596 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1598 if (!update_command_add_option(op, next_string, command))
1600 imagex_error(T("Unrecognized option \"%"TS"\" to "
1601 "update command \"%"TS"\" on line %zu"),
1602 next_string, command_name, line_number);
1608 if (num_nonoptions == update_command_num_nonoptions[op])
1610 imagex_error(T("Unexpected argument \"%"TS"\" in "
1611 "update command on line %zu\n"
1612 " (The \"%"TS"\" command only "
1613 "takes %zu nonoption arguments!)\n"),
1614 next_string, line_number,
1615 command_name, num_nonoptions);
1618 update_command_add_nonoption(op, next_string,
1619 command, num_nonoptions);
1624 if (num_nonoptions != update_command_num_nonoptions[op]) {
1625 imagex_error(T("Not enough arguments to update command "
1626 "\"%"TS"\" on line %zu"), command_name, line_number);
1632 static struct wimlib_update_command *
1633 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1634 size_t *num_cmds_ret)
1638 struct wimlib_update_command *cmds;
1641 nlines = text_file_count_lines(cmd_file_contents_p,
1646 /* Always allocate at least 1 slot, just in case the implementation of
1647 * calloc() returns NULL if 0 bytes are requested. */
1648 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1650 imagex_error(T("out of memory"));
1653 p = *cmd_file_contents_p;
1655 for (i = 0; i < nlines; i++) {
1656 /* XXX: Could use rawmemchr() here instead, but it may not be
1657 * available on all platforms. */
1658 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1659 size_t len = endp - p + 1;
1661 if (!is_comment_line(p, len)) {
1662 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1673 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1674 * one image from a WIM file to an NTFS volume. */
1676 imagex_apply(int argc, tchar **argv, int cmd)
1680 int image = WIMLIB_NO_IMAGE;
1682 struct wimlib_wim_info info;
1684 const tchar *wimfile;
1685 const tchar *target;
1686 const tchar *image_num_or_name = NULL;
1687 int extract_flags = 0;
1689 STRING_LIST(refglobs);
1691 for_opt(c, apply_options) {
1693 case IMAGEX_CHECK_OPTION:
1694 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1696 case IMAGEX_VERBOSE_OPTION:
1697 /* No longer does anything. */
1699 case IMAGEX_REF_OPTION:
1700 ret = string_list_append(&refglobs, optarg);
1702 goto out_free_refglobs;
1704 case IMAGEX_UNIX_DATA_OPTION:
1705 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1707 case IMAGEX_NO_ACLS_OPTION:
1708 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1710 case IMAGEX_STRICT_ACLS_OPTION:
1711 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1713 case IMAGEX_NO_ATTRIBUTES_OPTION:
1714 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1716 case IMAGEX_NORPFIX_OPTION:
1717 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1719 case IMAGEX_RPFIX_OPTION:
1720 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1722 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1723 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1724 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1726 case IMAGEX_WIMBOOT_OPTION:
1727 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1729 case IMAGEX_COMPACT_OPTION:
1730 ret = set_compact_mode(optarg, &extract_flags);
1732 goto out_free_refglobs;
1740 if (argc != 2 && argc != 3)
1745 if (!tstrcmp(wimfile, T("-"))) {
1746 /* Attempt to apply pipable WIM from standard input. */
1748 image_num_or_name = NULL;
1751 image_num_or_name = argv[1];
1756 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1757 imagex_progress_func, NULL);
1759 goto out_free_refglobs;
1761 wimlib_get_wim_info(wim, &info);
1764 /* Image explicitly specified. */
1765 image_num_or_name = argv[1];
1766 image = wimlib_resolve_image(wim, image_num_or_name);
1767 ret = verify_image_exists(image, image_num_or_name, wimfile);
1769 goto out_wimlib_free;
1772 /* No image specified; default to image 1, but only if the WIM
1773 * contains exactly one image. */
1775 if (info.image_count != 1) {
1776 imagex_error(T("\"%"TS"\" contains %d images; "
1777 "Please select one (or all)."),
1778 wimfile, info.image_count);
1787 if (refglobs.num_strings) {
1789 imagex_error(T("Can't specify --ref when applying from stdin!"));
1791 goto out_wimlib_free;
1793 ret = wim_reference_globs(wim, &refglobs, open_flags);
1795 goto out_wimlib_free;
1800 /* Interpret a regular file or block device target as an NTFS
1804 if (tstat(target, &stbuf)) {
1805 if (errno != ENOENT) {
1806 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1809 goto out_wimlib_free;
1812 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1813 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1819 ret = wimlib_extract_image(wim, image, target, extract_flags);
1821 set_fd_to_binary_mode(STDIN_FILENO);
1822 ret = wimlib_extract_image_from_pipe_with_progress(
1827 imagex_progress_func,
1831 imagex_printf(T("Done applying WIM image.\n"));
1832 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1834 do_resource_not_found_warning(wimfile, &info, &refglobs);
1836 imagex_error(T( "If you are applying an image "
1837 "from a split pipable WIM,\n"
1838 " make sure you have "
1839 "concatenated together all parts."));
1841 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1842 do_metadata_not_found_warning(wimfile, &info);
1847 string_list_destroy(&refglobs);
1851 usage(CMD_APPLY, stderr);
1853 goto out_free_refglobs;
1856 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1857 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1858 * the desired image. 'wimlib-imagex append': add a new image to an existing
1861 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1865 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1866 WIMLIB_ADD_FLAG_WINCONFIG |
1867 WIMLIB_ADD_FLAG_VERBOSE |
1868 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1869 int write_flags = 0;
1870 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1871 uint32_t chunk_size = UINT32_MAX;
1872 uint32_t solid_chunk_size = UINT32_MAX;
1873 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1874 const tchar *wimfile;
1877 STRING_LIST(image_properties);
1880 STRING_LIST(base_wimfiles);
1881 WIMStruct **base_wims;
1883 WIMStruct *template_wim = NULL;
1884 const tchar *template_wimfile = NULL;
1885 const tchar *template_image_name_or_num = NULL;
1886 int template_image = WIMLIB_NO_IMAGE;
1889 unsigned num_threads = 0;
1894 tchar *config_file = NULL;
1896 bool source_list = false;
1897 size_t source_list_nchars = 0;
1898 tchar *source_list_contents;
1899 bool capture_sources_malloced;
1900 struct wimlib_capture_source *capture_sources;
1902 bool name_defaulted;
1904 for_opt(c, capture_or_append_options) {
1906 case IMAGEX_BOOT_OPTION:
1907 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1909 case IMAGEX_CHECK_OPTION:
1910 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1912 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
1913 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1915 case IMAGEX_NOCHECK_OPTION:
1916 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1918 case IMAGEX_CONFIG_OPTION:
1919 config_file = optarg;
1920 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1922 case IMAGEX_COMPRESS_OPTION:
1923 compression_type = get_compression_type(optarg, false);
1924 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1927 case IMAGEX_CHUNK_SIZE_OPTION:
1928 chunk_size = parse_chunk_size(optarg);
1929 if (chunk_size == UINT32_MAX)
1932 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1933 solid_chunk_size = parse_chunk_size(optarg);
1934 if (solid_chunk_size == UINT32_MAX)
1937 case IMAGEX_SOLID_COMPRESS_OPTION:
1938 solid_ctype = get_compression_type(optarg, true);
1939 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1942 case IMAGEX_SOLID_OPTION:
1943 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1945 case IMAGEX_NO_SOLID_SORT_OPTION:
1946 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1948 case IMAGEX_FLAGS_OPTION: {
1949 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1950 tsprintf(p, T("FLAGS=%"TS), optarg);
1951 ret = string_list_append(&image_properties, p);
1956 case IMAGEX_IMAGE_PROPERTY_OPTION:
1957 ret = append_image_property_argument(&image_properties);
1961 case IMAGEX_DEREFERENCE_OPTION:
1962 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1964 case IMAGEX_VERBOSE_OPTION:
1965 /* No longer does anything. */
1967 case IMAGEX_THREADS_OPTION:
1968 num_threads = parse_num_threads(optarg);
1969 if (num_threads == UINT_MAX)
1972 case IMAGEX_REBUILD_OPTION:
1973 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1975 case IMAGEX_UNIX_DATA_OPTION:
1976 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1978 case IMAGEX_SOURCE_LIST_OPTION:
1981 case IMAGEX_NO_ACLS_OPTION:
1982 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1984 case IMAGEX_STRICT_ACLS_OPTION:
1985 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1987 case IMAGEX_RPFIX_OPTION:
1988 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1990 case IMAGEX_NORPFIX_OPTION:
1991 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1993 case IMAGEX_PIPABLE_OPTION:
1994 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1996 case IMAGEX_NOT_PIPABLE_OPTION:
1997 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1999 case IMAGEX_UPDATE_OF_OPTION:
2000 if (template_image_name_or_num) {
2001 imagex_error(T("'--update-of' can only be "
2002 "specified one time!"));
2006 colon = tstrrchr(optarg, T(':'));
2009 template_wimfile = optarg;
2011 template_image_name_or_num = colon + 1;
2013 template_wimfile = NULL;
2014 template_image_name_or_num = optarg;
2018 case IMAGEX_DELTA_FROM_OPTION:
2019 ret = string_list_append(&base_wimfiles, optarg);
2022 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2024 case IMAGEX_WIMBOOT_OPTION:
2025 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2027 case IMAGEX_UNSAFE_COMPACT_OPTION:
2028 if (cmd != CMD_APPEND) {
2029 imagex_error(T("'--unsafe-compact' is only "
2030 "valid for append!"));
2033 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2035 case IMAGEX_SNAPSHOT_OPTION:
2036 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2045 if (argc < 2 || argc > 4)
2051 /* Set default compression type and parameters. */
2054 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2055 /* No compression type specified. Use the default. */
2057 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2058 /* With --wimboot, default to XPRESS compression. */
2059 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2060 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2061 /* With --solid, default to LZMS compression. (However,
2062 * this will not affect solid resources!) */
2063 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2065 /* Otherwise, default to LZX compression. */
2066 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2070 if (!tstrcmp(wimfile, T("-"))) {
2071 /* Writing captured WIM to standard output. */
2073 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2074 imagex_error("Can't write a non-pipable WIM to "
2075 "standard output! Specify --pipable\n"
2076 " if you want to create a pipable WIM "
2077 "(but read the docs first).");
2081 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2083 if (cmd == CMD_APPEND) {
2084 imagex_error(T("Using standard output for append does "
2085 "not make sense."));
2088 wim_fd = STDOUT_FILENO;
2090 imagex_output_to_stderr();
2091 set_fd_to_binary_mode(wim_fd);
2094 /* If template image was specified using --update-of=IMAGE rather
2095 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2096 if (template_image_name_or_num && !template_wimfile) {
2097 if (base_wimfiles.num_strings == 1) {
2098 /* Capturing delta WIM based on single WIM: default to
2100 template_wimfile = base_wimfiles.strings[0];
2101 } else if (cmd == CMD_APPEND) {
2102 /* Appending to WIM: default to WIM being appended to.
2104 template_wimfile = wimfile;
2106 /* Capturing a normal (non-delta) WIM, so the WIM file
2107 * *must* be explicitly specified. */
2108 if (base_wimfiles.num_strings > 1) {
2109 imagex_error(T("For capture of delta WIM "
2110 "based on multiple existing "
2112 " '--update-of' must "
2113 "specify WIMFILE:IMAGE!"));
2115 imagex_error(T("For capture of non-delta WIM, "
2116 "'--update-of' must specify "
2125 name_defaulted = false;
2127 /* Set default name to SOURCE argument, omitting any directory
2128 * prefixes and trailing slashes. This requires making a copy
2129 * of @source. Leave some free characters at the end in case we
2130 * append a number to keep the name unique. */
2131 size_t source_name_len;
2133 source_name_len = tstrlen(source);
2134 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2135 name = tbasename(tstrcpy(source_copy, source));
2136 name_defaulted = true;
2139 /* Image description (if given). */
2141 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2142 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2143 ret = string_list_append(&image_properties, p);
2149 /* Set up capture sources in source list mode */
2150 if (source[0] == T('-') && source[1] == T('\0')) {
2151 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2153 source_list_contents = file_get_text_contents(source,
2154 &source_list_nchars);
2156 if (!source_list_contents)
2159 capture_sources = parse_source_list(&source_list_contents,
2162 if (!capture_sources) {
2164 goto out_free_source_list_contents;
2166 capture_sources_malloced = true;
2168 /* Set up capture source in non-source-list mode. */
2169 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2170 capture_sources[0].fs_source_path = source;
2171 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2172 capture_sources[0].reserved = 0;
2174 capture_sources_malloced = false;
2175 source_list_contents = NULL;
2178 /* Open the existing WIM, or create a new one. */
2179 if (cmd == CMD_APPEND) {
2180 ret = wimlib_open_wim_with_progress(wimfile,
2181 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2183 imagex_progress_func,
2186 goto out_free_capture_sources;
2188 ret = wimlib_create_new_wim(compression_type, &wim);
2190 goto out_free_capture_sources;
2191 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2194 /* Set chunk size if non-default. */
2195 if (chunk_size != UINT32_MAX) {
2196 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2199 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2201 int ctype = compression_type;
2203 if (cmd == CMD_APPEND) {
2204 struct wimlib_wim_info info;
2205 wimlib_get_wim_info(wim, &info);
2206 ctype = info.compression_type;
2209 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2210 ret = wimlib_set_output_chunk_size(wim, 4096);
2215 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2216 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2220 if (solid_chunk_size != UINT32_MAX) {
2221 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2227 /* Detect if source is regular file or block device and set NTFS volume
2232 if (tstat(source, &stbuf) == 0) {
2233 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2234 imagex_printf(T("Capturing WIM image from NTFS "
2235 "filesystem on \"%"TS"\"\n"), source);
2236 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2239 if (errno != ENOENT) {
2240 imagex_error_with_errno(T("Failed to stat "
2241 "\"%"TS"\""), source);
2249 /* If the user did not specify an image name, and the basename of the
2250 * source already exists as an image name in the WIM file, append a
2251 * suffix to make it unique. */
2252 if (cmd == CMD_APPEND && name_defaulted) {
2253 unsigned long conflict_idx;
2254 tchar *name_end = tstrchr(name, T('\0'));
2255 for (conflict_idx = 1;
2256 wimlib_image_name_in_use(wim, name);
2259 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2263 /* If capturing a delta WIM, reference resources from the base WIMs
2264 * before adding the new image. */
2265 if (base_wimfiles.num_strings) {
2266 base_wims = calloc(base_wimfiles.num_strings,
2267 sizeof(base_wims[0]));
2268 if (base_wims == NULL) {
2269 imagex_error(T("Out of memory!"));
2274 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2275 ret = wimlib_open_wim_with_progress(
2276 base_wimfiles.strings[i], open_flags,
2277 &base_wims[i], imagex_progress_func, NULL);
2279 goto out_free_base_wims;
2283 ret = wimlib_reference_resources(wim, base_wims,
2284 base_wimfiles.num_strings, 0);
2286 goto out_free_base_wims;
2288 if (base_wimfiles.num_strings == 1) {
2289 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2290 base_wimfiles.strings[0]);
2292 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2293 base_wimfiles.num_strings);
2300 /* If capturing or appending as an update of an existing (template) image,
2301 * open the WIM if needed and parse the image index. */
2302 if (template_image_name_or_num) {
2304 if (cmd == CMD_APPEND && !tstrcmp(template_wimfile, wimfile)) {
2307 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2308 if (!tstrcmp(template_wimfile,
2309 base_wimfiles.strings[i])) {
2310 template_wim = base_wims[i];
2316 if (!template_wim) {
2317 ret = wimlib_open_wim_with_progress(template_wimfile,
2320 imagex_progress_func,
2323 goto out_free_base_wims;
2326 template_image = wimlib_resolve_image(template_wim,
2327 template_image_name_or_num);
2329 if (template_image_name_or_num[0] == T('-')) {
2332 struct wimlib_wim_info info;
2334 wimlib_get_wim_info(template_wim, &info);
2335 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2336 if (n >= 1 && n <= info.image_count &&
2338 tmp != template_image_name_or_num + 1)
2340 template_image = info.image_count - (n - 1);
2343 ret = verify_image_exists_and_is_single(template_image,
2344 template_image_name_or_num,
2347 goto out_free_template_wim;
2350 ret = wimlib_add_image_multisource(wim,
2357 goto out_free_template_wim;
2359 if (image_properties.num_strings || template_image_name_or_num) {
2360 /* User asked to set additional image properties, or an image on
2361 * which the added one is to be based has been specified with
2363 struct wimlib_wim_info info;
2365 wimlib_get_wim_info(wim, &info);
2367 ret = apply_image_properties(&image_properties, wim,
2368 info.image_count, NULL);
2370 goto out_free_template_wim;
2372 /* Reference template image if the user provided one. */
2373 if (template_image_name_or_num) {
2374 imagex_printf(T("Using image %d "
2375 "from \"%"TS"\" as template\n"),
2376 template_image, template_wimfile);
2377 ret = wimlib_reference_template_image(wim,
2383 goto out_free_template_wim;
2387 /* Write the new WIM or overwrite the existing WIM with the new image
2389 if (cmd == CMD_APPEND) {
2390 ret = wimlib_overwrite(wim, write_flags, num_threads);
2391 } else if (wimfile) {
2392 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2393 write_flags, num_threads);
2395 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2396 write_flags, num_threads);
2398 out_free_template_wim:
2399 /* 'template_wim' may alias 'wim' or any of the 'base_wims' */
2400 if (template_wim == wim)
2401 goto out_free_base_wims;
2402 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2403 if (template_wim == base_wims[i])
2404 goto out_free_base_wims;
2405 wimlib_free(template_wim);
2407 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2408 wimlib_free(base_wims[i]);
2412 out_free_capture_sources:
2413 if (capture_sources_malloced)
2414 free(capture_sources);
2415 out_free_source_list_contents:
2416 free(source_list_contents);
2418 string_list_destroy(&image_properties);
2419 string_list_destroy(&base_wimfiles);
2429 /* Remove image(s) from a WIM. */
2431 imagex_delete(int argc, tchar **argv, int cmd)
2434 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2435 int write_flags = 0;
2436 const tchar *wimfile;
2437 const tchar *image_num_or_name;
2442 for_opt(c, delete_options) {
2444 case IMAGEX_CHECK_OPTION:
2445 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2447 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2448 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2450 case IMAGEX_SOFT_OPTION:
2451 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2453 case IMAGEX_UNSAFE_COMPACT_OPTION:
2454 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2465 imagex_error(T("Must specify a WIM file"));
2467 imagex_error(T("Must specify an image"));
2471 image_num_or_name = argv[1];
2473 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2474 imagex_progress_func, NULL);
2478 image = wimlib_resolve_image(wim, image_num_or_name);
2480 ret = verify_image_exists(image, image_num_or_name, wimfile);
2482 goto out_wimlib_free;
2484 ret = wimlib_delete_image(wim, image);
2486 imagex_error(T("Failed to delete image from \"%"TS"\""),
2488 goto out_wimlib_free;
2491 ret = wimlib_overwrite(wim, write_flags, 0);
2493 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2494 "deleted"), wimfile);
2502 usage(CMD_DELETE, stderr);
2507 struct print_dentry_options {
2512 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2514 tprintf(T("%"TS"\n"), dentry->full_path);
2517 static const struct {
2520 } file_attr_flags[] = {
2521 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2522 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2523 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2524 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2525 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2526 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2527 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2528 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2529 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2530 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2531 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2532 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2533 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2534 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2535 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2538 #define TIMESTR_MAX 100
2541 print_time(const tchar *type, const struct wimlib_timespec *wts,
2544 tchar timestr[TIMESTR_MAX];
2548 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2549 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2554 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2555 timestr[TIMESTR_MAX - 1] = '\0';
2557 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2560 static void print_byte_field(const uint8_t field[], size_t len)
2563 tprintf(T("%02hhx"), *field++);
2567 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2569 tchar attr_string[256];
2572 tputs(T("WIM Information:"));
2573 tputs(T("----------------"));
2574 tprintf(T("Path: %"TS"\n"), wimfile);
2575 tprintf(T("GUID: 0x"));
2576 print_byte_field(info->guid, sizeof(info->guid));
2578 tprintf(T("Version: %u\n"), info->wim_version);
2579 tprintf(T("Image Count: %d\n"), info->image_count);
2580 tprintf(T("Compression: %"TS"\n"),
2581 wimlib_get_compression_type_string(info->compression_type));
2582 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2584 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2585 tprintf(T("Boot Index: %d\n"), info->boot_index);
2586 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2588 attr_string[0] = T('\0');
2591 tstrcat(attr_string, T("Pipable, "));
2593 if (info->has_integrity_table)
2594 tstrcat(attr_string, T("Integrity info, "));
2596 if (info->has_rpfix)
2597 tstrcat(attr_string, T("Relative path junction, "));
2599 if (info->resource_only)
2600 tstrcat(attr_string, T("Resource only, "));
2602 if (info->metadata_only)
2603 tstrcat(attr_string, T("Metadata only, "));
2605 if (info->is_marked_readonly)
2606 tstrcat(attr_string, T("Readonly, "));
2608 p = tstrchr(attr_string, T('\0'));
2609 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2612 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2616 print_resource(const struct wimlib_resource_entry *resource,
2619 tprintf(T("Hash = 0x"));
2620 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2623 if (!resource->is_missing) {
2624 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2625 resource->uncompressed_size);
2626 if (resource->packed) {
2627 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2628 "bytes @ offset %"PRIu64"\n"),
2629 resource->raw_resource_uncompressed_size,
2630 resource->raw_resource_compressed_size,
2631 resource->raw_resource_offset_in_wim);
2633 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2636 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2637 resource->compressed_size);
2639 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2643 tprintf(T("Part Number = %u\n"), resource->part_number);
2644 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2646 tprintf(T("Flags = "));
2647 if (resource->is_compressed)
2648 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2649 if (resource->is_metadata)
2650 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2651 if (resource->is_free)
2652 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2653 if (resource->is_spanned)
2654 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2655 if (resource->packed)
2656 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2664 print_blobs(WIMStruct *wim)
2666 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2671 default_print_security_descriptor(const uint8_t *sd, size_t size)
2673 tprintf(T("Security Descriptor = "));
2674 print_byte_field(sd, size);
2680 is_null_guid(const uint8_t *guid)
2682 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2684 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2688 print_guid(const tchar *label, const uint8_t *guid)
2690 if (is_null_guid(guid))
2692 tprintf(T("%-20"TS"= 0x"), label);
2693 print_byte_field(guid, WIMLIB_GUID_LEN);
2698 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2701 "----------------------------------------------------------------------------\n"));
2702 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2703 if (dentry->dos_name)
2704 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2705 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2706 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2707 if (file_attr_flags[i].flag & dentry->attributes)
2708 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2709 file_attr_flags[i].name);
2711 if (dentry->security_descriptor) {
2712 print_security_descriptor(dentry->security_descriptor,
2713 dentry->security_descriptor_size);
2716 print_time(T("Creation Time"),
2717 &dentry->creation_time, dentry->creation_time_high);
2718 print_time(T("Last Write Time"),
2719 &dentry->last_write_time, dentry->last_write_time_high);
2720 print_time(T("Last Access Time"),
2721 &dentry->last_access_time, dentry->last_access_time_high);
2724 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2725 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2727 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2728 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2730 if (dentry->unix_mode != 0) {
2731 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2732 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2733 dentry->unix_uid, dentry->unix_gid,
2734 dentry->unix_mode, dentry->unix_rdev);
2737 if (!is_null_guid(dentry->object_id.object_id)) {
2738 print_guid(T("Object ID"), dentry->object_id.object_id);
2739 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2740 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2741 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2744 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2745 if (dentry->streams[i].stream_name) {
2746 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2747 dentry->streams[i].stream_name);
2748 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2749 tprintf(T("\tRaw encrypted data stream:\n"));
2750 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2751 tprintf(T("\tReparse point stream:\n"));
2753 tprintf(T("\tUnnamed data stream:\n"));
2755 print_resource(&dentry->streams[i].resource, NULL);
2760 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2762 const struct print_dentry_options *options = _options;
2763 if (!options->detailed)
2764 print_dentry_full_path(dentry);
2766 print_dentry_detailed(dentry);
2770 /* Print the files contained in an image(s) in a WIM file. */
2772 imagex_dir(int argc, tchar **argv, int cmd)
2774 const tchar *wimfile;
2775 WIMStruct *wim = NULL;
2778 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2780 struct print_dentry_options options = {
2783 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2785 STRING_LIST(refglobs);
2787 for_opt(c, dir_options) {
2789 case IMAGEX_PATH_OPTION:
2792 case IMAGEX_DETAILED_OPTION:
2793 options.detailed = true;
2795 case IMAGEX_ONE_FILE_ONLY_OPTION:
2796 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2798 case IMAGEX_REF_OPTION:
2799 ret = string_list_append(&refglobs, optarg);
2801 goto out_free_refglobs;
2811 imagex_error(T("Must specify a WIM file"));
2815 imagex_error(T("Too many arguments"));
2820 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2821 imagex_progress_func, NULL);
2823 goto out_free_refglobs;
2826 image = wimlib_resolve_image(wim, argv[1]);
2827 ret = verify_image_exists(image, argv[1], wimfile);
2829 goto out_wimlib_free;
2831 /* No image specified; default to image 1, but only if the WIM
2832 * contains exactly one image. */
2834 struct wimlib_wim_info info;
2836 wimlib_get_wim_info(wim, &info);
2837 if (info.image_count != 1) {
2838 imagex_error(T("\"%"TS"\" contains %d images; Please "
2839 "select one (or all)."),
2840 wimfile, info.image_count);
2847 if (refglobs.num_strings) {
2848 ret = wim_reference_globs(wim, &refglobs, 0);
2850 goto out_wimlib_free;
2853 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2854 print_dentry, &options);
2855 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2856 struct wimlib_wim_info info;
2858 wimlib_get_wim_info(wim, &info);
2859 do_metadata_not_found_warning(wimfile, &info);
2864 string_list_destroy(&refglobs);
2868 usage(CMD_DIR, stderr);
2870 goto out_free_refglobs;
2873 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2876 imagex_export(int argc, tchar **argv, int cmd)
2880 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2881 int write_flags = 0;
2882 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2883 const tchar *src_wimfile;
2884 const tchar *src_image_num_or_name;
2885 const tchar *dest_wimfile;
2887 const tchar *dest_name;
2888 const tchar *dest_desc;
2890 struct wimlib_wim_info src_info;
2891 WIMStruct *dest_wim;
2896 STRING_LIST(refglobs);
2897 unsigned num_threads = 0;
2898 uint32_t chunk_size = UINT32_MAX;
2899 uint32_t solid_chunk_size = UINT32_MAX;
2900 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2902 for_opt(c, export_options) {
2904 case IMAGEX_BOOT_OPTION:
2905 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2907 case IMAGEX_CHECK_OPTION:
2908 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2910 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2911 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2913 case IMAGEX_NOCHECK_OPTION:
2914 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2916 case IMAGEX_COMPRESS_OPTION:
2917 compression_type = get_compression_type(optarg, false);
2918 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2921 case IMAGEX_RECOMPRESS_OPTION:
2922 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2924 case IMAGEX_SOLID_OPTION:
2925 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2927 case IMAGEX_NO_SOLID_SORT_OPTION:
2928 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2930 case IMAGEX_CHUNK_SIZE_OPTION:
2931 chunk_size = parse_chunk_size(optarg);
2932 if (chunk_size == UINT32_MAX)
2935 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2936 solid_chunk_size = parse_chunk_size(optarg);
2937 if (solid_chunk_size == UINT32_MAX)
2940 case IMAGEX_SOLID_COMPRESS_OPTION:
2941 solid_ctype = get_compression_type(optarg, true);
2942 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2945 case IMAGEX_REF_OPTION:
2946 ret = string_list_append(&refglobs, optarg);
2948 goto out_free_refglobs;
2950 case IMAGEX_THREADS_OPTION:
2951 num_threads = parse_num_threads(optarg);
2952 if (num_threads == UINT_MAX)
2955 case IMAGEX_REBUILD_OPTION:
2956 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2958 case IMAGEX_PIPABLE_OPTION:
2959 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2961 case IMAGEX_NOT_PIPABLE_OPTION:
2962 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2964 case IMAGEX_WIMBOOT_OPTION:
2965 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2967 case IMAGEX_UNSAFE_COMPACT_OPTION:
2968 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2976 if (argc < 3 || argc > 5)
2978 src_wimfile = argv[0];
2979 src_image_num_or_name = argv[1];
2980 dest_wimfile = argv[2];
2981 dest_name = (argc >= 4) ? argv[3] : NULL;
2982 dest_desc = (argc >= 5) ? argv[4] : NULL;
2983 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2984 imagex_progress_func, NULL);
2986 goto out_free_refglobs;
2988 wimlib_get_wim_info(src_wim, &src_info);
2990 /* Determine if the destination is an existing file or not. If so, we
2991 * try to append the exported image(s) to it; otherwise, we create a new
2992 * WIM containing the exported image(s). Furthermore, determine if we
2993 * need to write a pipable WIM directly to standard output. */
2995 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2997 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2998 imagex_error("Can't write a non-pipable WIM to "
2999 "standard output! Specify --pipable\n"
3000 " if you want to create a pipable WIM "
3001 "(but read the docs first).");
3003 goto out_free_src_wim;
3006 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3008 dest_wimfile = NULL;
3009 dest_wim_fd = STDOUT_FILENO;
3010 imagex_output_to_stderr();
3011 set_fd_to_binary_mode(dest_wim_fd);
3014 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
3016 /* Destination file exists. */
3018 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
3019 imagex_error(T("\"%"TS"\" is not a regular file "
3020 "or block device"), dest_wimfile);
3022 goto out_free_src_wim;
3024 ret = wimlib_open_wim_with_progress(dest_wimfile,
3026 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3028 imagex_progress_func,
3031 goto out_free_src_wim;
3033 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3034 /* The user specified a compression type, but we're
3035 * exporting to an existing WIM. Make sure the
3036 * specified compression type is the same as the
3037 * compression type of the existing destination WIM. */
3038 struct wimlib_wim_info dest_info;
3040 wimlib_get_wim_info(dest_wim, &dest_info);
3041 if (compression_type != dest_info.compression_type) {
3042 imagex_error(T("Cannot specify a compression type that is "
3043 "not the same as that used in the "
3044 "destination WIM"));
3046 goto out_free_dest_wim;
3052 if (errno != ENOENT) {
3053 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3056 goto out_free_src_wim;
3059 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3060 imagex_error(T("'--unsafe-compact' is only valid when "
3061 "exporting to an existing WIM file!"));
3063 goto out_free_src_wim;
3066 /* dest_wimfile is not an existing file, so create a new WIM. */
3068 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3069 /* The user did not specify a compression type; default
3070 * to that of the source WIM, unless --solid or
3071 * --wimboot was specified. */
3073 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3074 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3075 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3076 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3078 compression_type = src_info.compression_type;
3080 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3082 goto out_free_src_wim;
3084 wimlib_register_progress_function(dest_wim,
3085 imagex_progress_func, NULL);
3087 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3088 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3090 /* For --wimboot export, use small XPRESS chunks. */
3091 wimlib_set_output_chunk_size(dest_wim, 4096);
3092 } else if (compression_type == src_info.compression_type &&
3093 chunk_size == UINT32_MAX)
3095 /* Use same chunk size if compression type is the same. */
3096 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3100 if (chunk_size != UINT32_MAX) {
3101 /* Set destination chunk size. */
3102 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3104 goto out_free_dest_wim;
3106 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3107 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3109 goto out_free_dest_wim;
3111 if (solid_chunk_size != UINT32_MAX) {
3112 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3114 goto out_free_dest_wim;
3117 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3118 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3120 goto out_free_dest_wim;
3122 if (refglobs.num_strings) {
3123 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3125 goto out_free_dest_wim;
3128 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3129 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3131 imagex_error(T("--boot specified for all-images export, but source WIM "
3132 "has no bootable image."));
3134 goto out_free_dest_wim;
3137 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3138 dest_desc, export_flags);
3140 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3141 do_resource_not_found_warning(src_wimfile,
3142 &src_info, &refglobs);
3143 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3144 do_metadata_not_found_warning(src_wimfile, &src_info);
3146 goto out_free_dest_wim;
3150 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3151 else if (dest_wimfile)
3152 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3153 write_flags, num_threads);
3155 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3156 WIMLIB_ALL_IMAGES, write_flags,
3159 wimlib_free(dest_wim);
3161 wimlib_free(src_wim);
3163 string_list_destroy(&refglobs);
3167 usage(CMD_EXPORT, stderr);
3170 goto out_free_refglobs;
3173 /* Extract files or directories from a WIM image */
3175 imagex_extract(int argc, tchar **argv, int cmd)
3182 const tchar *wimfile;
3183 const tchar *image_num_or_name;
3184 tchar *dest_dir = T(".");
3185 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3186 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3187 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3188 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3190 STRING_LIST(refglobs);
3192 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3194 for_opt(c, extract_options) {
3196 case IMAGEX_CHECK_OPTION:
3197 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3199 case IMAGEX_VERBOSE_OPTION:
3200 /* No longer does anything. */
3202 case IMAGEX_REF_OPTION:
3203 ret = string_list_append(&refglobs, optarg);
3205 goto out_free_refglobs;
3207 case IMAGEX_UNIX_DATA_OPTION:
3208 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3210 case IMAGEX_NO_ACLS_OPTION:
3211 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3213 case IMAGEX_STRICT_ACLS_OPTION:
3214 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3216 case IMAGEX_NO_ATTRIBUTES_OPTION:
3217 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3219 case IMAGEX_DEST_DIR_OPTION:
3222 case IMAGEX_TO_STDOUT_OPTION:
3223 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3224 imagex_suppress_output();
3225 set_fd_to_binary_mode(STDOUT_FILENO);
3227 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3228 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3229 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3231 case IMAGEX_NO_GLOBS_OPTION:
3232 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3234 case IMAGEX_NULLGLOB_OPTION:
3235 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3237 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3238 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3240 case IMAGEX_WIMBOOT_OPTION:
3241 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3243 case IMAGEX_COMPACT_OPTION:
3244 ret = set_compact_mode(optarg, &extract_flags);
3246 goto out_free_refglobs;
3258 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3259 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3261 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3266 image_num_or_name = argv[1];
3271 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3272 imagex_progress_func, NULL);
3274 goto out_free_refglobs;
3276 image = wimlib_resolve_image(wim, image_num_or_name);
3277 ret = verify_image_exists_and_is_single(image,
3281 goto out_wimlib_free;
3283 if (refglobs.num_strings) {
3284 ret = wim_reference_globs(wim, &refglobs, open_flags);
3286 goto out_wimlib_free;
3292 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3295 while (argc != 0 && ret == 0) {
3299 num_paths < argc && argv[num_paths][0] != T('@');
3304 ret = wimlib_extract_paths(wim, image, dest_dir,
3305 (const tchar **)argv,
3307 extract_flags | notlist_extract_flags);
3311 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3320 imagex_printf(T("Done extracting files.\n"));
3321 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3322 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3323 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3324 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3325 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3328 T("Note: You can use the '--nullglob' "
3329 "option to ignore missing files.\n"));
3331 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3332 "files and directories\n"
3333 " are in the WIM image.\n"),
3334 get_cmd_string(CMD_DIR, false));
3335 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3336 struct wimlib_wim_info info;
3338 wimlib_get_wim_info(wim, &info);
3339 do_resource_not_found_warning(wimfile, &info, &refglobs);
3340 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3341 struct wimlib_wim_info info;
3343 wimlib_get_wim_info(wim, &info);
3344 do_metadata_not_found_warning(wimfile, &info);
3349 string_list_destroy(&refglobs);
3353 usage(CMD_EXTRACT, stderr);
3356 goto out_free_refglobs;
3359 /* Prints information about a WIM file; also can mark an image as bootable,
3360 * change the name of an image, or change the description of an image. */
3362 imagex_info(int argc, tchar **argv, int cmd)
3366 bool header = false;
3369 bool short_header = true;
3370 const tchar *xml_out_file = NULL;
3371 const tchar *wimfile;
3372 const tchar *image_num_or_name;
3373 STRING_LIST(image_properties);
3378 int write_flags = 0;
3379 struct wimlib_wim_info info;
3381 for_opt(c, info_options) {
3383 case IMAGEX_BOOT_OPTION:
3386 case IMAGEX_CHECK_OPTION:
3387 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3389 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3390 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3392 case IMAGEX_NOCHECK_OPTION:
3393 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3395 case IMAGEX_HEADER_OPTION:
3397 short_header = false;
3399 case IMAGEX_BLOBS_OPTION:
3401 short_header = false;
3403 case IMAGEX_XML_OPTION:
3405 short_header = false;
3407 case IMAGEX_EXTRACT_XML_OPTION:
3408 xml_out_file = optarg;
3409 short_header = false;
3411 case IMAGEX_IMAGE_PROPERTY_OPTION:
3412 ret = append_image_property_argument(&image_properties);
3423 if (argc < 1 || argc > 4)
3427 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3431 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3432 tsprintf(p, T("NAME=%"TS), argv[2]);
3433 ret = string_list_append(&image_properties, p);
3440 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3441 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3442 ret = string_list_append(&image_properties, p);
3447 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3448 imagex_progress_func, NULL);
3452 wimlib_get_wim_info(wim, &info);
3454 image = wimlib_resolve_image(wim, image_num_or_name);
3455 ret = WIMLIB_ERR_INVALID_IMAGE;
3456 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3457 verify_image_exists(image, image_num_or_name, wimfile);
3459 imagex_error(T("If you would like to set the boot "
3460 "index to 0, specify image \"0\" with "
3461 "the --boot flag."));
3463 goto out_wimlib_free;
3466 if (boot && info.image_count == 0) {
3467 imagex_error(T("--boot is meaningless on a WIM with no images"));
3468 goto out_wimlib_free;
3471 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3473 imagex_error(T("Cannot specify the --boot flag "
3474 "without specifying a specific "
3475 "image in a multi-image WIM"));
3476 goto out_wimlib_free;
3478 if (image_properties.num_strings) {
3479 imagex_error(T("Can't change image properties without "
3480 "specifying a specific image in a "
3481 "multi-image WIM"));
3482 goto out_wimlib_free;
3486 /* Operations that print information are separated from operations that
3487 * recreate the WIM file. */
3488 if (!image_properties.num_strings && !boot) {
3490 /* Read-only operations */
3492 if (image == WIMLIB_NO_IMAGE) {
3493 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3494 image_num_or_name, wimfile);
3495 goto out_wimlib_free;
3498 if (image == WIMLIB_ALL_IMAGES && short_header)
3499 print_wim_information(wimfile, &info);
3502 wimlib_print_header(wim);
3505 if (info.total_parts != 1) {
3506 tfprintf(stderr, T("Warning: Only showing the blobs "
3507 "for part %d of a %d-part WIM.\n"),
3508 info.part_number, info.total_parts);
3514 ret = wimlib_extract_xml_data(wim, stdout);
3516 goto out_wimlib_free;
3522 fp = tfopen(xml_out_file, T("wb"));
3524 imagex_error_with_errno(T("Failed to open the "
3525 "file \"%"TS"\" for "
3529 goto out_wimlib_free;
3531 ret = wimlib_extract_xml_data(wim, fp);
3533 imagex_error(T("Failed to close the file "
3539 goto out_wimlib_free;
3543 wimlib_print_available_images(wim, image);
3547 /* Modification operations */
3548 bool any_property_changes;
3550 if (image == WIMLIB_ALL_IMAGES)
3553 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3554 imagex_error(T("Cannot change image properties "
3555 "when using image 0"));
3557 goto out_wimlib_free;
3561 if (image == info.boot_index) {
3562 imagex_printf(T("Image %d is already marked as "
3563 "bootable.\n"), image);
3566 imagex_printf(T("Marking image %d as bootable.\n"),
3568 info.boot_index = image;
3569 ret = wimlib_set_wim_info(wim, &info,
3570 WIMLIB_CHANGE_BOOT_INDEX);
3572 goto out_wimlib_free;
3576 ret = apply_image_properties(&image_properties, wim, image,
3577 &any_property_changes);
3579 goto out_wimlib_free;
3581 /* Only call wimlib_overwrite() if something actually needs to
3583 if (boot || any_property_changes ||
3584 ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) &&
3585 !info.has_integrity_table) ||
3586 ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) &&
3587 info.has_integrity_table))
3589 ret = wimlib_overwrite(wim, write_flags, 1);
3591 imagex_printf(T("The file \"%"TS"\" was not modified "
3592 "because nothing needed to be done.\n"),
3600 string_list_destroy(&image_properties);
3604 usage(CMD_INFO, stderr);
3609 /* Join split WIMs into one part WIM */
3611 imagex_join(int argc, tchar **argv, int cmd)
3614 int swm_open_flags = 0;
3615 int wim_write_flags = 0;
3616 const tchar *output_path;
3619 for_opt(c, join_options) {
3621 case IMAGEX_CHECK_OPTION:
3622 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3624 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3625 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3635 imagex_error(T("Must specify one or more split WIM (.swm) "
3639 output_path = argv[0];
3640 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3645 imagex_progress_func,
3651 usage(CMD_JOIN, stderr);
3656 #if WIM_MOUNTING_SUPPORTED
3658 /* Mounts a WIM image. */
3660 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3663 int mount_flags = 0;
3665 const tchar *staging_dir = NULL;
3666 const tchar *wimfile;
3669 struct wimlib_wim_info info;
3673 STRING_LIST(refglobs);
3675 if (cmd == CMD_MOUNTRW) {
3676 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3677 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3680 for_opt(c, mount_options) {
3682 case IMAGEX_ALLOW_OTHER_OPTION:
3683 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3685 case IMAGEX_CHECK_OPTION:
3686 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3688 case IMAGEX_DEBUG_OPTION:
3689 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3691 case IMAGEX_STREAMS_INTERFACE_OPTION:
3692 if (!tstrcasecmp(optarg, T("none")))
3693 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3694 else if (!tstrcasecmp(optarg, T("xattr")))
3695 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3696 else if (!tstrcasecmp(optarg, T("windows")))
3697 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3699 imagex_error(T("Unknown stream interface \"%"TS"\""),
3704 case IMAGEX_REF_OPTION:
3705 ret = string_list_append(&refglobs, optarg);
3707 goto out_free_refglobs;
3709 case IMAGEX_STAGING_DIR_OPTION:
3710 staging_dir = optarg;
3712 case IMAGEX_UNIX_DATA_OPTION:
3713 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3721 if (argc != 2 && argc != 3)
3726 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3727 imagex_progress_func, NULL);
3729 goto out_free_refglobs;
3731 wimlib_get_wim_info(wim, &info);
3734 /* Image explicitly specified. */
3735 image = wimlib_resolve_image(wim, argv[1]);
3737 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3741 /* No image specified; default to image 1, but only if the WIM
3742 * contains exactly one image. */
3744 if (info.image_count != 1) {
3745 imagex_error(T("\"%"TS"\" contains %d images; Please "
3746 "select one."), wimfile, info.image_count);
3754 if (refglobs.num_strings) {
3755 ret = wim_reference_globs(wim, &refglobs, open_flags);
3760 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3762 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3763 do_metadata_not_found_warning(wimfile, &info);
3765 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3767 image, wimfile, dir);
3773 string_list_destroy(&refglobs);
3779 goto out_free_refglobs;
3781 #endif /* WIM_MOUNTING_SUPPORTED */
3783 /* Rebuild a WIM file */
3785 imagex_optimize(int argc, tchar **argv, int cmd)
3788 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3789 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3790 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3791 uint32_t chunk_size = UINT32_MAX;
3792 uint32_t solid_chunk_size = UINT32_MAX;
3793 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3796 const tchar *wimfile;
3799 unsigned num_threads = 0;
3801 for_opt(c, optimize_options) {
3803 case IMAGEX_CHECK_OPTION:
3804 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3806 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3807 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3809 case IMAGEX_NOCHECK_OPTION:
3810 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3812 case IMAGEX_COMPRESS_OPTION:
3813 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3814 compression_type = get_compression_type(optarg, false);
3815 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3818 case IMAGEX_RECOMPRESS_OPTION:
3819 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3821 case IMAGEX_CHUNK_SIZE_OPTION:
3822 chunk_size = parse_chunk_size(optarg);
3823 if (chunk_size == UINT32_MAX)
3826 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3827 solid_chunk_size = parse_chunk_size(optarg);
3828 if (solid_chunk_size == UINT32_MAX)
3831 case IMAGEX_SOLID_COMPRESS_OPTION:
3832 solid_ctype = get_compression_type(optarg, true);
3833 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3836 case IMAGEX_SOLID_OPTION:
3837 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3838 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3840 case IMAGEX_NO_SOLID_SORT_OPTION:
3841 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3843 case IMAGEX_THREADS_OPTION:
3844 num_threads = parse_num_threads(optarg);
3845 if (num_threads == UINT_MAX)
3848 case IMAGEX_PIPABLE_OPTION:
3849 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3851 case IMAGEX_NOT_PIPABLE_OPTION:
3852 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3854 case IMAGEX_UNSAFE_COMPACT_OPTION:
3855 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3869 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3870 imagex_progress_func, NULL);
3874 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3875 /* Change compression type. */
3876 ret = wimlib_set_output_compression_type(wim, compression_type);
3878 goto out_wimlib_free;
3881 if (chunk_size != UINT32_MAX) {
3882 /* Change chunk size. */
3883 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3885 goto out_wimlib_free;
3887 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3888 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3890 goto out_wimlib_free;
3892 if (solid_chunk_size != UINT32_MAX) {
3893 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3895 goto out_wimlib_free;
3898 old_size = file_get_size(wimfile);
3899 tprintf(T("\"%"TS"\" original size: "), wimfile);
3901 tputs(T("Unknown"));
3903 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3905 ret = wimlib_overwrite(wim, write_flags, num_threads);
3907 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3908 goto out_wimlib_free;
3911 new_size = file_get_size(wimfile);
3912 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3914 tputs(T("Unknown"));
3916 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3918 tfputs(T("Space saved: "), stdout);
3919 if (new_size != -1 && old_size != -1) {
3920 tprintf(T("%lld KiB\n"),
3921 ((long long)old_size - (long long)new_size) >> 10);
3923 tputs(T("Unknown"));
3932 usage(CMD_OPTIMIZE, stderr);
3938 /* Split a WIM into a spanned set */
3940 imagex_split(int argc, tchar **argv, int cmd)
3944 int write_flags = 0;
3945 unsigned long part_size;
3950 for_opt(c, split_options) {
3952 case IMAGEX_CHECK_OPTION:
3953 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3955 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3956 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3968 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3969 if (tmp == argv[2] || *tmp) {
3970 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3971 imagex_error(T("The part size must be an integer or "
3972 "floating-point number of megabytes."));
3975 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3976 imagex_progress_func, NULL);
3980 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3986 usage(CMD_SPLIT, stderr);
3992 #if WIM_MOUNTING_SUPPORTED
3993 /* Unmounts a mounted WIM image. */
3995 imagex_unmount(int argc, tchar **argv, int cmd)
3998 int unmount_flags = 0;
4001 for_opt(c, unmount_options) {
4003 case IMAGEX_COMMIT_OPTION:
4004 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
4006 case IMAGEX_CHECK_OPTION:
4007 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
4009 case IMAGEX_REBUILD_OPTION:
4010 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
4012 case IMAGEX_LAZY_OPTION:
4013 case IMAGEX_FORCE_OPTION:
4014 /* Now, unmount is lazy by default. However, committing
4015 * the image will fail with
4016 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4017 * file descriptors on the WIM image. The
4018 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4019 * descriptors to be closed. */
4020 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4022 case IMAGEX_NEW_IMAGE_OPTION:
4023 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4034 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4035 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4036 imagex_error(T("--new-image is meaningless "
4037 "without --commit also specified!"));
4042 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4043 imagex_progress_func, NULL);
4045 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4046 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4048 "\tNote: Use --commit --force to force changes "
4049 "to be committed, regardless\n"
4050 "\t of open files.\n"));
4057 usage(CMD_UNMOUNT, stderr);
4062 #endif /* WIM_MOUNTING_SUPPORTED */
4065 * Add, delete, or rename files in a WIM image.
4068 imagex_update(int argc, tchar **argv, int cmd)
4070 const tchar *wimfile;
4074 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4075 int write_flags = 0;
4076 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4077 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4078 WIMLIB_ADD_FLAG_VERBOSE |
4079 WIMLIB_ADD_FLAG_WINCONFIG;
4080 int default_delete_flags = 0;
4081 unsigned num_threads = 0;
4083 tchar *cmd_file_contents;
4084 size_t cmd_file_nchars;
4085 struct wimlib_update_command *cmds;
4087 tchar *command_str = NULL;
4088 tchar *config_file = NULL;
4089 tchar *wimboot_config = NULL;
4091 for_opt(c, update_options) {
4093 /* Generic or write options */
4094 case IMAGEX_THREADS_OPTION:
4095 num_threads = parse_num_threads(optarg);
4096 if (num_threads == UINT_MAX)
4099 case IMAGEX_CHECK_OPTION:
4100 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4102 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4103 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4105 case IMAGEX_REBUILD_OPTION:
4106 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4108 case IMAGEX_COMMAND_OPTION:
4110 imagex_error(T("--command may only be specified "
4111 "one time. Please provide\n"
4112 " the update commands "
4113 "on standard input instead."));
4116 command_str = tstrdup(optarg);
4118 imagex_error(T("Out of memory!"));
4122 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4123 wimboot_config = optarg;
4125 /* Default delete options */
4126 case IMAGEX_FORCE_OPTION:
4127 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4129 case IMAGEX_RECURSIVE_OPTION:
4130 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4133 /* Global add option */
4134 case IMAGEX_CONFIG_OPTION:
4135 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4136 config_file = optarg;
4139 /* Default add options */
4140 case IMAGEX_VERBOSE_OPTION:
4141 /* No longer does anything. */
4143 case IMAGEX_DEREFERENCE_OPTION:
4144 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4146 case IMAGEX_UNIX_DATA_OPTION:
4147 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4149 case IMAGEX_NO_ACLS_OPTION:
4150 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4152 case IMAGEX_STRICT_ACLS_OPTION:
4153 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4155 case IMAGEX_NO_REPLACE_OPTION:
4156 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4158 case IMAGEX_UNSAFE_COMPACT_OPTION:
4159 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4168 if (argc != 1 && argc != 2)
4172 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4173 imagex_progress_func, NULL);
4175 goto out_free_command_str;
4178 /* Image explicitly specified. */
4179 image = wimlib_resolve_image(wim, argv[1]);
4180 ret = verify_image_exists_and_is_single(image, argv[1],
4183 goto out_wimlib_free;
4185 /* No image specified; default to image 1, but only if the WIM
4186 * contains exactly one image. */
4187 struct wimlib_wim_info info;
4189 wimlib_get_wim_info(wim, &info);
4190 if (info.image_count != 1) {
4191 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4192 wimfile, info.image_count);
4199 /* Read update commands from standard input, or the command string if
4202 cmd_file_contents = NULL;
4203 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4207 goto out_free_cmd_file_contents;
4209 } else if (!wimboot_config) {
4210 if (isatty(STDIN_FILENO)) {
4211 tputs(T("Reading update commands from standard input..."));
4212 recommend_man_page(CMD_UPDATE, stdout);
4214 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4215 if (!cmd_file_contents) {
4217 goto out_wimlib_free;
4220 /* Parse the update commands */
4221 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4225 goto out_free_cmd_file_contents;
4228 cmd_file_contents = NULL;
4233 /* Set default flags and capture config on the update commands */
4234 for (size_t i = 0; i < num_cmds; i++) {
4235 switch (cmds[i].op) {
4236 case WIMLIB_UPDATE_OP_ADD:
4237 cmds[i].add.add_flags |= default_add_flags;
4238 cmds[i].add.config_file = config_file;
4240 case WIMLIB_UPDATE_OP_DELETE:
4241 cmds[i].delete_.delete_flags |= default_delete_flags;
4248 /* Execute the update commands */
4249 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4253 if (wimboot_config) {
4254 /* --wimboot-config=FILE is short for an
4255 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4257 struct wimlib_update_command cmd;
4259 cmd.op = WIMLIB_UPDATE_OP_ADD;
4260 cmd.add.fs_source_path = wimboot_config;
4261 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4262 cmd.add.config_file = NULL;
4263 cmd.add.add_flags = 0;
4265 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4270 /* Overwrite the updated WIM */
4271 ret = wimlib_overwrite(wim, write_flags, num_threads);
4274 out_free_cmd_file_contents:
4275 free(cmd_file_contents);
4278 out_free_command_str:
4283 usage(CMD_UPDATE, stderr);
4286 goto out_free_command_str;
4289 /* Verify a WIM file. */
4291 imagex_verify(int argc, tchar **argv, int cmd)
4294 const tchar *wimfile;
4296 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4297 int verify_flags = 0;
4298 STRING_LIST(refglobs);
4301 for_opt(c, verify_options) {
4303 case IMAGEX_REF_OPTION:
4304 ret = string_list_append(&refglobs, optarg);
4306 goto out_free_refglobs;
4308 case IMAGEX_NOCHECK_OPTION:
4309 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4321 imagex_error(T("Must specify a WIM file!"));
4323 imagex_error(T("At most one WIM file can be specified!"));
4329 ret = wimlib_open_wim_with_progress(wimfile,
4332 imagex_progress_func,
4335 goto out_free_refglobs;
4337 ret = wim_reference_globs(wim, &refglobs, open_flags);
4339 goto out_wimlib_free;
4341 ret = wimlib_verify_wim(wim, verify_flags);
4343 tputc(T('\n'), stderr);
4344 imagex_error(T("\"%"TS"\" failed verification!"),
4346 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4347 refglobs.num_strings == 0)
4349 imagex_printf(T("Note: if this WIM file is not standalone, "
4350 "use the --ref option to specify the other parts.\n"));
4353 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4360 string_list_destroy(&refglobs);
4364 usage(CMD_VERIFY, stderr);
4366 goto out_free_refglobs;
4369 struct imagex_command {
4371 int (*func)(int argc, tchar **argv, int cmd);
4374 static const struct imagex_command imagex_commands[] = {
4375 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4376 [CMD_APPLY] = {T("apply"), imagex_apply},
4377 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4378 [CMD_DELETE] = {T("delete"), imagex_delete},
4379 [CMD_DIR ] = {T("dir"), imagex_dir},
4380 [CMD_EXPORT] = {T("export"), imagex_export},
4381 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4382 [CMD_INFO] = {T("info"), imagex_info},
4383 [CMD_JOIN] = {T("join"), imagex_join},
4384 #if WIM_MOUNTING_SUPPORTED
4385 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4386 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4388 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4389 [CMD_SPLIT] = {T("split"), imagex_split},
4390 #if WIM_MOUNTING_SUPPORTED
4391 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4393 [CMD_UPDATE] = {T("update"), imagex_update},
4394 [CMD_VERIFY] = {T("verify"), imagex_verify},
4399 /* Can be a directory or source list file. But source list file is probably
4400 * a rare use case, so just say directory. */
4401 # define SOURCE_STR T("DIRECTORY")
4403 /* Can only be a directory */
4404 # define TARGET_STR T("DIRECTORY")
4407 /* Can be a directory, NTFS volume, or source list file. */
4408 # define SOURCE_STR T("SOURCE")
4410 /* Can be a directory or NTFS volume. */
4411 # define TARGET_STR T("TARGET")
4415 static const tchar * const usage_strings[] = {
4418 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4419 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4420 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4421 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4422 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4423 " [--dereference] [--snapshot]\n"
4427 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4428 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4429 " [--no-attributes] [--rpfix] [--norpfix]\n"
4430 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4431 " [--compact=FORMAT]\n"
4435 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4436 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4437 " [--config=FILE] [--threads=NUM_THREADS]\n"
4438 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4439 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4440 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4445 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4449 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4453 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4454 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4455 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4456 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4457 " [--wimboot] [--solid]\n"
4461 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4462 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4463 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4464 " [--no-attributes] [--include-invalid-names]\n"
4465 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4469 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4470 " [--boot] [--check] [--nocheck] [--xml]\n"
4471 " [--extract-xml FILE] [--header] [--blobs]\n"
4472 " [--image-property NAME=VALUE]\n"
4476 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4478 #if WIM_MOUNTING_SUPPORTED
4481 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4482 " [--check] [--streams-interface=INTERFACE]\n"
4483 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4487 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4488 " [--check] [--streams-interface=INTERFACE]\n"
4489 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4495 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4496 " [--check] [--nocheck] [--solid]\n"
4501 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4503 #if WIM_MOUNTING_SUPPORTED
4506 " %"TS" DIRECTORY\n"
4507 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4512 " %"TS" WIMFILE [IMAGE]\n"
4513 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4514 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4515 " [--command=STRING] [--wimboot-config=FILE]\n"
4520 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4524 static const tchar *invocation_name;
4525 static int invocation_cmd = CMD_NONE;
4527 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4529 static tchar buf[50];
4531 if (cmd == CMD_NONE)
4532 return T("wimlib-imagex");
4534 if (only_short_form || invocation_cmd != CMD_NONE) {
4535 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4537 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4538 imagex_commands[cmd].name);
4546 static const tchar * const fmt =
4548 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
4549 "Copyright (C) 2012-2018 Eric Biggers\n"
4550 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4551 "This is free software: you are free to change and redistribute it.\n"
4552 "There is NO WARRANTY, to the extent permitted by law.\n"
4554 "Report bugs to "PACKAGE_BUGREPORT".\n"
4556 tfprintf(stdout, fmt, wimlib_get_version_string());
4560 do_common_options(int *argc_p, tchar **argv, int cmd)
4566 for (i = 1; i < argc; i++) {
4568 if (p[0] == T('-') && p[1] == T('-')) {
4570 if (!tstrcmp(p, T("help"))) {
4571 if (cmd == CMD_NONE)
4576 } else if (!tstrcmp(p, T("version"))) {
4579 } else if (!tstrcmp(p, T("quiet"))) {
4580 imagex_suppress_output();
4581 memmove(&argv[i], &argv[i + 1],
4582 (argc - i) * sizeof(argv[i]));
4585 } else if (!*p) /* reached "--", no more options */
4594 print_usage_string(int cmd, FILE *fp)
4596 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4600 recommend_man_page(int cmd, FILE *fp)
4602 const tchar *format_str;
4604 format_str = T("Some uncommon options are not listed;\n"
4605 "See %"TS".pdf in the doc directory for more details.\n");
4607 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4609 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4613 usage(int cmd, FILE *fp)
4615 tfprintf(fp, T("Usage:\n"));
4616 print_usage_string(cmd, fp);
4617 tfprintf(fp, T("\n"));
4618 recommend_man_page(cmd, fp);
4624 tfprintf(fp, T("Usage:\n"));
4625 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4626 print_usage_string(cmd, fp);
4627 tfprintf(fp, T("\n"));
4629 static const tchar * const extra =
4632 " %"TS" --version\n"
4635 tfprintf(fp, extra, invocation_name, invocation_name);
4637 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4638 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4639 "For some commands IMAGE may be \"all\".\n"
4641 recommend_man_page(CMD_NONE, fp);
4645 extern int wmain(int argc, wchar_t **argv);
4649 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4650 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4651 * something else), while on Windows the command arguments will be UTF-16LE
4652 * encoded 'wchar_t' strings. */
4654 main(int argc, tchar **argv)
4660 imagex_info_file = stdout;
4661 invocation_name = tbasename(argv[0]);
4664 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4665 if (igcase != NULL) {
4666 if (!tstrcmp(igcase, T("no")) ||
4667 !tstrcmp(igcase, T("0")))
4668 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4669 else if (!tstrcmp(igcase, T("yes")) ||
4670 !tstrcmp(igcase, T("1")))
4671 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4674 "WARNING: Ignoring unknown setting of "
4675 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4680 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4682 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4683 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4684 for (int i = 0; i < CMD_MAX; i++) {
4685 if (!tstrcmp(invocation_name + 3,
4686 imagex_commands[i].name))
4695 /* Unless already known from the invocation name, determine which
4696 * command was specified. */
4697 if (cmd == CMD_NONE) {
4699 imagex_error(T("No command specified!\n"));
4703 for (int i = 0; i < CMD_MAX; i++) {
4704 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4709 if (cmd != CMD_NONE) {
4715 /* Handle common options. May exit early (for --help or --version). */
4716 do_common_options(&argc, argv, cmd);
4718 /* Bail if a valid command was not specified. */
4719 if (cmd == CMD_NONE) {
4720 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4725 /* Enable warning and error messages in wimlib to be more user-friendly.
4727 wimlib_set_print_errors(true);
4729 /* Initialize wimlib. */
4730 ret = wimlib_global_init(init_flags);
4732 goto out_check_status;
4734 /* Call the command handler function. */
4735 ret = imagex_commands[cmd].func(argc, argv, cmd);
4737 /* Check for error writing to standard output, especially since for some
4738 * commands, writing to standard output is part of the program's actual
4739 * behavior and not just for informational purposes. */
4740 if (ferror(stdout) || fclose(stdout)) {
4741 imagex_error_with_errno(T("error writing to standard output"));
4746 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4747 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4748 * error code from which an error message can be printed. */
4750 imagex_error(T("Exiting with error code %d:\n"
4752 wimlib_get_error_string(ret));
4753 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4754 imagex_error_with_errno(T("errno"));
4756 /* Make wimlib free any resources it's holding (although this is not
4757 * strictly necessary because the process is ending anyway). */
4758 wimlib_global_cleanup();