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,
174 IMAGEX_CREATE_OPTION,
176 IMAGEX_DELTA_FROM_OPTION,
177 IMAGEX_DEREFERENCE_OPTION,
178 IMAGEX_DEST_DIR_OPTION,
179 IMAGEX_DETAILED_OPTION,
180 IMAGEX_EXTRACT_XML_OPTION,
183 IMAGEX_HEADER_OPTION,
184 IMAGEX_IMAGE_PROPERTY_OPTION,
185 IMAGEX_INCLUDE_INTEGRITY_OPTION,
186 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
188 IMAGEX_METADATA_OPTION,
189 IMAGEX_NEW_IMAGE_OPTION,
190 IMAGEX_NOCHECK_OPTION,
191 IMAGEX_NORPFIX_OPTION,
192 IMAGEX_NOT_PIPABLE_OPTION,
193 IMAGEX_NO_ACLS_OPTION,
194 IMAGEX_NO_ATTRIBUTES_OPTION,
195 IMAGEX_NO_GLOBS_OPTION,
196 IMAGEX_NO_REPLACE_OPTION,
197 IMAGEX_NO_SOLID_SORT_OPTION,
198 IMAGEX_NULLGLOB_OPTION,
199 IMAGEX_ONE_FILE_ONLY_OPTION,
201 IMAGEX_PIPABLE_OPTION,
202 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
203 IMAGEX_REBUILD_OPTION,
204 IMAGEX_RECOMPRESS_OPTION,
205 IMAGEX_RECURSIVE_OPTION,
208 IMAGEX_SNAPSHOT_OPTION,
210 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
211 IMAGEX_SOLID_COMPRESS_OPTION,
213 IMAGEX_SOURCE_LIST_OPTION,
214 IMAGEX_STAGING_DIR_OPTION,
215 IMAGEX_STREAMS_INTERFACE_OPTION,
216 IMAGEX_STRICT_ACLS_OPTION,
217 IMAGEX_THREADS_OPTION,
218 IMAGEX_TO_STDOUT_OPTION,
219 IMAGEX_UNIX_DATA_OPTION,
220 IMAGEX_UNSAFE_COMPACT_OPTION,
221 IMAGEX_UPDATE_OF_OPTION,
222 IMAGEX_VERBOSE_OPTION,
223 IMAGEX_WIMBOOT_CONFIG_OPTION,
224 IMAGEX_WIMBOOT_OPTION,
228 static const struct option apply_options[] = {
229 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
230 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
231 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
232 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
233 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
234 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
235 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
236 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
237 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
238 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
239 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
240 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
241 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
245 static const struct option capture_or_append_options[] = {
246 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
247 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
248 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
249 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
250 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
251 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
252 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
253 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
254 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
255 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
256 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
257 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
258 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
259 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
260 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
261 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
262 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
263 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
264 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
265 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
266 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
267 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
268 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
269 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
270 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
271 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
272 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
273 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
274 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
275 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
276 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
277 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
278 {T("create"), no_argument, NULL, IMAGEX_CREATE_OPTION},
282 static const struct option delete_options[] = {
283 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
284 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
285 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
286 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
290 static const struct option dir_options[] = {
291 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
292 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
293 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
294 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
298 static const struct option export_options[] = {
299 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
300 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
301 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
302 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
303 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
304 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
305 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
306 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
307 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
308 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
309 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
310 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
311 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
312 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
313 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
314 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
315 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
316 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
317 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
321 static const struct option extract_options[] = {
322 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
323 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
324 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
325 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
326 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
327 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
328 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
329 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
330 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
331 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
332 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
333 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
334 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
335 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
336 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
337 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
338 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
342 static const struct option info_options[] = {
343 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
344 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
345 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
346 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
347 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
348 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
349 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
350 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
351 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
352 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
353 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
357 static const struct option join_options[] = {
358 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
359 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
363 #if WIM_MOUNTING_SUPPORTED
364 static const struct option mount_options[] = {
365 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
366 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
367 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
368 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
369 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
370 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
371 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
376 static const struct option optimize_options[] = {
377 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
378 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
379 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
380 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
381 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
382 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
383 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
384 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
385 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
386 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
387 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
388 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
389 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
390 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
391 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
395 static const struct option split_options[] = {
396 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
397 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
401 #if WIM_MOUNTING_SUPPORTED
402 static const struct option unmount_options[] = {
403 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
404 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
405 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
406 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
407 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
408 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
413 static const struct option update_options[] = {
414 /* Careful: some of the options here set the defaults for update
415 * commands, but the flags given to an actual update command (and not to
416 * `imagex update' itself are also handled in
417 * update_command_add_option(). */
418 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
419 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
420 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
421 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
422 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
423 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
425 /* Default delete options */
426 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
427 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
429 /* Global add option */
430 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
432 /* Default add options */
433 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
434 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
435 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
436 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
437 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
438 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
439 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
440 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
445 static const struct option verify_options[] = {
446 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
447 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
453 # define _format_attribute(type, format_str, args_start) \
454 __attribute__((format(type, format_str, args_start)))
456 # define _format_attribute(type, format_str, args_start)
459 /* Print formatted error message to stderr. */
460 static void _format_attribute(printf, 1, 2)
461 imagex_error(const tchar *format, ...)
464 va_start(va, format);
465 tfputs(T("ERROR: "), stderr);
466 tvfprintf(stderr, format, va);
467 tputc(T('\n'), stderr);
471 /* Print formatted error message to stderr. */
472 static void _format_attribute(printf, 1, 2)
473 imagex_error_with_errno(const tchar *format, ...)
475 int errno_save = errno;
477 va_start(va, format);
478 tfputs(T("ERROR: "), stderr);
479 tvfprintf(stderr, format, va);
480 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
485 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
487 if (image == WIMLIB_NO_IMAGE) {
488 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
489 " Please specify a 1-based image index or "
490 "image name. To list the images\n"
491 " contained in the WIM archive, run\n"
493 " %"TS" \"%"TS"\"\n"),
494 image_name, wim_name,
495 get_cmd_string(CMD_INFO, false), wim_name);
496 return WIMLIB_ERR_INVALID_IMAGE;
502 verify_image_is_single(int image)
504 if (image == WIMLIB_ALL_IMAGES) {
505 imagex_error(T("Cannot specify all images for this action!"));
506 return WIMLIB_ERR_INVALID_IMAGE;
512 verify_image_exists_and_is_single(int image, const tchar *image_name,
513 const tchar *wim_name)
516 ret = verify_image_exists(image, image_name, wim_name);
518 ret = verify_image_is_single(image);
523 print_available_compression_types(FILE *fp)
525 static const tchar * const s =
527 "Available compression types:\n"
530 " xpress (alias: \"fast\")\n"
531 " lzx (alias: \"maximum\") (default for capture)\n"
532 " lzms (alias: \"recovery\")\n"
538 /* Parse the argument to --compress or --solid-compress */
540 get_compression_type(tchar *optarg, bool solid)
543 unsigned int compression_level = 0;
546 plevel = tstrchr(optarg, T(':'));
552 ultmp = tstrtoul(plevel, &ptmp, 10);
553 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
554 imagex_error(T("Compression level must be a positive integer! "
555 "e.g. --compress=lzx:80"));
556 return WIMLIB_COMPRESSION_TYPE_INVALID;
558 compression_level = ultmp;
561 if (!tstrcasecmp(optarg, T("maximum")) ||
562 !tstrcasecmp(optarg, T("lzx")) ||
563 !tstrcasecmp(optarg, T("max"))) {
564 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
565 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
566 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
567 } else if (!tstrcasecmp(optarg, T("recovery"))) {
571 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
572 " differently from DISM. Instead, you typically want to use '--solid' to\n"
573 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
574 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
575 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
576 " of '--compress=recovery'.\n"));
578 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
579 } else if (!tstrcasecmp(optarg, T("lzms"))) {
580 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
581 } else if (!tstrcasecmp(optarg, T("none"))) {
582 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
584 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
585 print_available_compression_types(stderr);
586 return WIMLIB_COMPRESSION_TYPE_INVALID;
589 if (compression_level != 0)
590 wimlib_set_default_compression_level(ctype, compression_level);
594 /* Parse the argument to --compact */
596 set_compact_mode(const tchar *arg, int *extract_flags)
599 if (!tstrcasecmp(arg, T("xpress4k")))
600 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
601 else if (!tstrcasecmp(arg, T("xpress8k")))
602 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
603 else if (!tstrcasecmp(arg, T("xpress16k")))
604 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
605 else if (!tstrcasecmp(arg, T("lzx")))
606 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
609 *extract_flags |= flag;
614 "\"%"TS"\" is not a recognized System Compression format. The options are:"
616 " --compact=xpress4k\n"
617 " --compact=xpress8k\n"
618 " --compact=xpress16k\n"
627 unsigned num_strings;
628 unsigned num_alloc_strings;
631 #define STRING_LIST_INITIALIZER \
632 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
634 #define STRING_LIST(_strings) \
635 struct string_list _strings = STRING_LIST_INITIALIZER
638 string_list_append(struct string_list *list, tchar *glob)
640 unsigned num_alloc_strings = list->num_alloc_strings;
642 if (list->num_strings == num_alloc_strings) {
645 num_alloc_strings += 4;
646 new_strings = realloc(list->strings,
647 sizeof(list->strings[0]) * num_alloc_strings);
649 imagex_error(T("Out of memory!"));
652 list->strings = new_strings;
653 list->num_alloc_strings = num_alloc_strings;
655 list->strings[list->num_strings++] = glob;
660 string_list_destroy(struct string_list *list)
666 wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags)
668 return wimlib_reference_resource_files(wim, (const tchar **)list->strings,
670 WIMLIB_REF_FLAG_GLOB_ENABLE,
675 append_image_property_argument(struct string_list *image_properties)
677 if (!tstrchr(optarg, '=')) {
678 imagex_error(T("'--image-property' argument "
679 "must be in the form NAME=VALUE"));
682 return string_list_append(image_properties, optarg);
686 apply_image_properties(struct string_list *image_properties,
687 WIMStruct *wim, int image, bool *any_changes_ret)
689 bool any_changes = false;
690 for (unsigned i = 0; i < image_properties->num_strings; i++) {
692 const tchar *current_value;
695 name = image_properties->strings[i];
696 value = tstrchr(name, '=');
699 current_value = wimlib_get_image_property(wim, image, name);
700 if (current_value && !tstrcmp(current_value, value)) {
701 imagex_printf(T("The %"TS" property of image %d "
702 "already has value \"%"TS"\".\n"),
705 imagex_printf(T("Setting the %"TS" property of image "
706 "%d to \"%"TS"\".\n"),
708 ret = wimlib_set_image_property(wim, image, name, value);
715 *any_changes_ret = any_changes;
720 do_resource_not_found_warning(const tchar *wimfile,
721 const struct wimlib_wim_info *info,
722 const struct string_list *refglobs)
724 if (info->total_parts > 1) {
725 if (refglobs->num_strings == 0) {
726 imagex_error(T("\"%"TS"\" is part of a split WIM. "
727 "Use --ref to specify the other parts."),
730 imagex_error(T("Perhaps the '--ref' argument did not "
731 "specify all other parts of the split "
735 imagex_error(T("If this is a delta WIM, use the --ref argument "
736 "to specify the WIM(s) on which it is based."));
741 do_metadata_not_found_warning(const tchar *wimfile,
742 const struct wimlib_wim_info *info)
744 if (info->part_number != 1) {
745 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
746 " You must specify the first part."),
751 /* Returns the size of a file given its name, or -1 if the file does not exist
752 * or its size cannot be determined. */
754 file_get_size(const tchar *filename)
757 if (tstat(filename, &st) == 0)
764 PARSE_STRING_SUCCESS = 0,
765 PARSE_STRING_FAILURE = 1,
766 PARSE_STRING_NONE = 2,
770 * Parses a string token from an array of characters.
772 * Tokens are either whitespace-delimited, or double or single-quoted.
774 * @line_p: Pointer to the pointer to the line of data. Will be updated
775 * to point past the string token iff the return value is
776 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
779 * @len_p: @len_p initially stores the length of the line of data, which may
780 * be 0, and it will be updated to the number of bytes remaining in
781 * the line iff the return value is PARSE_STRING_SUCCESS.
783 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
784 * parsed string token will be returned here.
786 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
787 * PARSE_STRING_FAILURE if the data was invalid due to a missing
788 * closing quote; or PARSE_STRING_NONE if the line ended before the
789 * beginning of a string token was found.
792 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
795 tchar *line = *line_p;
799 /* Skip leading whitespace */
802 return PARSE_STRING_NONE;
803 if (!istspace(*line) && *line != T('\0'))
809 if (quote_char == T('"') || quote_char == T('\'')) {
814 line = tmemchr(line, quote_char, len);
816 imagex_error(T("Missing closing quote: %"TS), fn - 1);
817 return PARSE_STRING_FAILURE;
820 /* Unquoted string. Go until whitespace. Line is terminated
821 * by '\0', so no need to check 'len'. */
825 } while (!istspace(*line) && *line != T('\0'));
832 return PARSE_STRING_SUCCESS;
835 /* Parses a line of data (not an empty line or comment) in the source list file
836 * format. (See the man page for 'wimlib-imagex capture' for details on this
837 * format and the meaning.)
839 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
840 * len == 0. The data in @line will be modified by this function call.
842 * @len: Length of the line of data.
844 * @source: On success, the capture source and target described by the line is
845 * written into this destination. Note that it will contain pointers
846 * to data in the @line array.
848 * Returns true if the line was valid; false otherwise. */
850 parse_source_list_line(tchar *line, size_t len,
851 struct wimlib_capture_source *source)
855 ret = parse_string(&line, &len, &source->fs_source_path);
856 if (ret != PARSE_STRING_SUCCESS)
858 ret = parse_string(&line, &len, &source->wim_target_path);
859 if (ret == PARSE_STRING_NONE)
860 source->wim_target_path = source->fs_source_path;
861 return ret != PARSE_STRING_FAILURE;
864 /* Returns %true if the given line of length @len > 0 is a comment or empty line
865 * in the source list file format. */
867 is_comment_line(const tchar *line, size_t len)
870 if (*line == T('#') || *line == T(';'))
872 if (!istspace(*line) && *line != T('\0'))
882 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
885 tchar *contents = *contents_p;
886 size_t nchars = *nchars_p;
889 for (i = 0; i < nchars; i++)
890 if (contents[i] == T('\n'))
893 /* Handle last line not terminated by a newline */
894 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
895 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
897 imagex_error(T("Out of memory!"));
900 contents[nchars] = T('\n');
901 *contents_p = contents;
909 /* Parses a file in the source list format. (See the man page for
910 * 'wimlib-imagex capture' for details on this format and the meaning.)
912 * @source_list_contents: Contents of the source list file. Note that this
913 * buffer will be modified to save memory allocations,
914 * and cannot be freed until the returned array of
915 * wimlib_capture_source's has also been freed.
917 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
920 * @nsources_ret: On success, the length of the returned array is
923 * Returns: An array of `struct wimlib_capture_source's that can be passed to
924 * the wimlib_add_image_multisource() function to specify how a WIM image is to
926 static struct wimlib_capture_source *
927 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
928 size_t *nsources_ret)
932 struct wimlib_capture_source *sources;
935 nlines = text_file_count_lines(source_list_contents_p,
936 &source_list_nchars);
940 /* Always allocate at least 1 slot, just in case the implementation of
941 * calloc() returns NULL if 0 bytes are requested. */
942 sources = calloc(nlines ?: 1, sizeof(*sources));
944 imagex_error(T("out of memory"));
947 p = *source_list_contents_p;
949 for (i = 0; i < nlines; i++) {
950 /* XXX: Could use rawmemchr() here instead, but it may not be
951 * available on all platforms. */
952 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
953 size_t len = endp - p + 1;
955 if (!is_comment_line(p, len)) {
956 if (!parse_source_list_line(p, len, &sources[j++])) {
968 /* Reads the contents of a file into memory. */
970 file_get_contents(const tchar *filename, size_t *len_ret)
977 if (tstat(filename, &stbuf) != 0) {
978 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
983 fp = tfopen(filename, T("rb"));
985 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
989 buf = malloc(len ? len : 1);
991 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
992 "contents of file \"%"TS"\""), len, filename);
995 if (fread(buf, 1, len, fp) != len) {
996 imagex_error_with_errno(T("Failed to read %zu bytes from the "
997 "file \"%"TS"\""), len, filename);
1011 /* Read standard input until EOF and return the full contents in a malloc()ed
1012 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1015 stdin_get_contents(size_t *len_ret)
1017 /* stdin can, of course, be a pipe or other non-seekable file, so the
1018 * total length of the data cannot be pre-determined */
1020 size_t newlen = 1024;
1024 char *p = realloc(buf, newlen);
1025 size_t bytes_read, bytes_to_read;
1027 imagex_error(T("out of memory while reading stdin"));
1031 bytes_to_read = newlen - pos;
1032 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1034 if (bytes_read != bytes_to_read) {
1039 imagex_error_with_errno(T("error reading stdin"));
1053 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1056 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1058 *num_tchars_ret = num_bytes;
1060 #else /* !__WIN32__ */
1061 /* On Windows, translate the text to UTF-16LE */
1065 if (num_bytes >= 2 &&
1066 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1067 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1069 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1070 * with something that looks like an ASCII character encoded as
1071 * a UTF-16LE code unit. Assume the file is encoded as
1072 * UTF-16LE. This is not a 100% reliable check. */
1073 num_wchars = num_bytes / 2;
1074 text_wstr = (wchar_t*)text;
1076 /* File does not look like UTF-16LE. Assume it is encoded in
1077 * the current Windows code page. I think these are always
1078 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1079 * should work as expected. */
1080 text_wstr = win32_mbs_to_wcs(text,
1085 *num_tchars_ret = num_wchars;
1087 #endif /* __WIN32__ */
1091 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1096 contents = file_get_contents(filename, &num_bytes);
1099 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1103 stdin_get_text_contents(size_t *num_tchars_ret)
1108 contents = stdin_get_contents(&num_bytes);
1111 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1114 #define TO_PERCENT(numerator, denominator) \
1115 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1117 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1118 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1119 #define KIBIBYTE_MIN_NBYTES 10000ULL
1122 get_unit(uint64_t total_bytes, const tchar **name_ret)
1124 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1125 *name_ret = T("GiB");
1127 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1128 *name_ret = T("MiB");
1130 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1131 *name_ret = T("KiB");
1134 *name_ret = T("bytes");
1139 static struct wimlib_progress_info_scan last_scan_progress;
1142 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1144 uint64_t prev_count, cur_count;
1146 prev_count = last_scan_progress.num_nondirs_scanned +
1147 last_scan_progress.num_dirs_scanned;
1148 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1150 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1151 cur_count % 128 == 0)
1153 unsigned unit_shift;
1154 const tchar *unit_name;
1156 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1157 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1158 "%"PRIu64" directories) "),
1159 scan->num_bytes_scanned >> unit_shift,
1161 scan->num_nondirs_scanned,
1162 scan->num_dirs_scanned);
1163 last_scan_progress = *scan;
1167 static struct wimlib_progress_info_split last_split_progress;
1170 report_split_progress(uint64_t bytes_completed_in_part)
1172 uint64_t completed_bytes = last_split_progress.completed_bytes +
1173 bytes_completed_in_part;
1174 unsigned percent_done = TO_PERCENT(completed_bytes,
1175 last_split_progress.total_bytes);
1176 unsigned unit_shift;
1177 const tchar *unit_name;
1179 unit_shift = get_unit(last_split_progress.total_bytes, &unit_name);
1180 imagex_printf(T("\rSplitting WIM: %"PRIu64" %"TS" of "
1181 "%"PRIu64" %"TS" (%u%%) written, part %u of %u"),
1182 completed_bytes >> unit_shift,
1184 last_split_progress.total_bytes >> unit_shift,
1187 last_split_progress.cur_part_number,
1188 last_split_progress.total_parts);
1191 /* Progress callback function passed to various wimlib functions. */
1192 static enum wimlib_progress_status
1193 imagex_progress_func(enum wimlib_progress_msg msg,
1194 union wimlib_progress_info *info,
1195 void *_ignored_context)
1197 unsigned percent_done;
1198 unsigned unit_shift;
1199 const tchar *unit_name;
1202 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1203 if (last_split_progress.total_bytes != 0) {
1204 /* wimlib_split() in progress; use the split-specific
1205 * progress message. */
1206 report_split_progress(info->write_streams.completed_compressed_bytes);
1210 static bool started;
1212 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1213 imagex_printf(T("Using %"TS" compression "
1214 "with %u thread%"TS"\n"),
1215 wimlib_get_compression_type_string(
1216 info->write_streams.compression_type),
1217 info->write_streams.num_threads,
1218 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1223 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1224 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1225 info->write_streams.total_bytes);
1227 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1228 info->write_streams.completed_bytes >> unit_shift,
1230 info->write_streams.total_bytes >> unit_shift,
1233 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1234 imagex_printf(T("\n"));
1236 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1237 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1238 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1239 imagex_printf(T("\n"));
1241 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1242 info->scan.wim_target_path);
1244 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1246 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1247 switch (info->scan.status) {
1248 case WIMLIB_SCAN_DENTRY_OK:
1249 report_scan_progress(&info->scan, false);
1251 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1252 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1254 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1255 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1256 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1258 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1259 /* Symlink fixups are enabled by default. This is
1260 * mainly intended for Windows, which for some reason
1261 * uses absolute junctions (with drive letters!) in the
1262 * default installation. On UNIX-like systems, warn the
1263 * user when fixing the target of an absolute symbolic
1264 * link, so they know to disable this if they want. */
1266 imagex_printf(T("\nWARNING: Adjusted target of "
1267 "absolute symbolic link \"%"TS"\"\n"
1268 " (Use --norpfix to capture "
1269 "absolute symbolic links as-is)\n"),
1270 info->scan.cur_path);
1277 case WIMLIB_PROGRESS_MSG_SCAN_END:
1278 report_scan_progress(&info->scan, true);
1279 imagex_printf(T("\n"));
1281 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1282 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1283 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1284 info->integrity.total_bytes);
1285 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1286 "of %"PRIu64" %"TS" (%u%%) done"),
1287 info->integrity.filename,
1288 info->integrity.completed_bytes >> unit_shift,
1290 info->integrity.total_bytes >> unit_shift,
1293 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1294 imagex_printf(T("\n"));
1296 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1297 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1298 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1299 info->integrity.total_bytes);
1300 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1301 "of %"PRIu64" %"TS" (%u%%) done"),
1302 info->integrity.completed_bytes >> unit_shift,
1304 info->integrity.total_bytes >> unit_shift,
1307 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1308 imagex_printf(T("\n"));
1310 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1311 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1312 "to %"TS" \"%"TS"\"\n"),
1313 info->extract.image,
1314 info->extract.image_name,
1315 info->extract.wimfile_name,
1316 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1317 T("NTFS volume") : T("directory")),
1318 info->extract.target);
1320 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1321 if (info->extract.end_file_count >= 2000) {
1322 percent_done = TO_PERCENT(info->extract.current_file_count,
1323 info->extract.end_file_count);
1324 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1325 info->extract.current_file_count,
1326 info->extract.end_file_count, percent_done);
1327 if (info->extract.current_file_count == info->extract.end_file_count)
1328 imagex_printf(T("\n"));
1331 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1332 percent_done = TO_PERCENT(info->extract.completed_bytes,
1333 info->extract.total_bytes);
1334 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1335 imagex_printf(T("\rExtracting file data: "
1336 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1337 info->extract.completed_bytes >> unit_shift,
1339 info->extract.total_bytes >> unit_shift,
1342 if (info->extract.completed_bytes >= info->extract.total_bytes)
1343 imagex_printf(T("\n"));
1345 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1346 if (info->extract.end_file_count >= 2000) {
1347 percent_done = TO_PERCENT(info->extract.current_file_count,
1348 info->extract.end_file_count);
1349 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1350 info->extract.current_file_count,
1351 info->extract.end_file_count, percent_done);
1352 if (info->extract.current_file_count == info->extract.end_file_count)
1353 imagex_printf(T("\n"));
1356 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1357 if (info->extract.total_parts != 1) {
1358 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1359 info->extract.part_number,
1360 info->extract.total_parts);
1363 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1364 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1365 last_split_progress = info->split;
1366 report_split_progress(0);
1368 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1369 switch (info->update.command->op) {
1370 case WIMLIB_UPDATE_OP_DELETE:
1371 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1372 info->update.command->delete_.wim_path);
1374 case WIMLIB_UPDATE_OP_RENAME:
1375 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1376 info->update.command->rename.wim_source_path,
1377 info->update.command->rename.wim_target_path);
1379 case WIMLIB_UPDATE_OP_ADD:
1384 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1385 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1386 info->replace.path_in_wim);
1388 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1389 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1390 info->wimboot_exclude.path_in_wim);
1392 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1393 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1394 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1395 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1396 info->unmount.mounted_wim,
1397 info->unmount.mounted_image);
1399 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1400 info->unmount.mounted_wim,
1401 info->unmount.mounted_image);
1402 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1406 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1407 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1408 info->verify_image.current_image,
1409 info->verify_image.total_images);
1411 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1412 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1413 info->verify_streams.total_bytes);
1414 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1415 imagex_printf(T("\rVerifying file data: "
1416 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1417 info->verify_streams.completed_bytes >> unit_shift,
1419 info->verify_streams.total_bytes >> unit_shift,
1422 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1423 imagex_printf(T("\n"));
1428 imagex_flush_output();
1429 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1433 parse_num_threads(const tchar *optarg)
1436 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1437 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1438 imagex_error(T("Number of threads must be a non-negative integer!"));
1446 parse_chunk_size(const tchar *optarg)
1449 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1450 if (chunk_size == 0) {
1451 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1452 " with optional K, M, or G suffix"));
1456 if (*tmp == T('k') || *tmp == T('K')) {
1459 } else if (*tmp == T('m') || *tmp == T('M')) {
1462 } else if (*tmp == T('g') || *tmp == T('G')) {
1466 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1467 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1471 if (chunk_size >= UINT32_MAX) {
1472 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1480 * Parse an option passed to an update command.
1482 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1485 * @option: Text string for the option (beginning with --)
1487 * @cmd: `struct wimlib_update_command' that is being constructed for
1490 * Returns true if the option was recognized; false if not.
1493 update_command_add_option(int op, const tchar *option,
1494 struct wimlib_update_command *cmd)
1496 bool recognized = true;
1498 case WIMLIB_UPDATE_OP_ADD:
1499 if (!tstrcmp(option, T("--verbose")))
1500 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1501 else if (!tstrcmp(option, T("--unix-data")))
1502 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1503 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1504 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1505 else if (!tstrcmp(option, T("--strict-acls")))
1506 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1507 else if (!tstrcmp(option, T("--dereference")))
1508 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1509 else if (!tstrcmp(option, T("--no-replace")))
1510 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1514 case WIMLIB_UPDATE_OP_DELETE:
1515 if (!tstrcmp(option, T("--force")))
1516 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1517 else if (!tstrcmp(option, T("--recursive")))
1518 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1529 /* How many nonoption arguments each `imagex update' command expects */
1530 static const unsigned update_command_num_nonoptions[] = {
1531 [WIMLIB_UPDATE_OP_ADD] = 2,
1532 [WIMLIB_UPDATE_OP_DELETE] = 1,
1533 [WIMLIB_UPDATE_OP_RENAME] = 2,
1537 update_command_add_nonoption(int op, const tchar *nonoption,
1538 struct wimlib_update_command *cmd,
1539 unsigned num_nonoptions)
1542 case WIMLIB_UPDATE_OP_ADD:
1543 if (num_nonoptions == 0)
1544 cmd->add.fs_source_path = (tchar*)nonoption;
1546 cmd->add.wim_target_path = (tchar*)nonoption;
1548 case WIMLIB_UPDATE_OP_DELETE:
1549 cmd->delete_.wim_path = (tchar*)nonoption;
1551 case WIMLIB_UPDATE_OP_RENAME:
1552 if (num_nonoptions == 0)
1553 cmd->rename.wim_source_path = (tchar*)nonoption;
1555 cmd->rename.wim_target_path = (tchar*)nonoption;
1561 * Parse a command passed on stdin to `imagex update'.
1563 * @line: Text of the command.
1564 * @len: Length of the line, including a null terminator
1567 * @command: A `struct wimlib_update_command' to fill in from the parsed
1570 * @line_number: Line number of the command, for diagnostics.
1572 * Returns true on success; returns false on parse error.
1575 parse_update_command(tchar *line, size_t len,
1576 struct wimlib_update_command *command,
1580 tchar *command_name;
1582 size_t num_nonoptions;
1584 /* Get the command name ("add", "delete", "rename") */
1585 ret = parse_string(&line, &len, &command_name);
1586 if (ret != PARSE_STRING_SUCCESS)
1589 if (!tstrcasecmp(command_name, T("add"))) {
1590 op = WIMLIB_UPDATE_OP_ADD;
1591 } else if (!tstrcasecmp(command_name, T("delete"))) {
1592 op = WIMLIB_UPDATE_OP_DELETE;
1593 } else if (!tstrcasecmp(command_name, T("rename"))) {
1594 op = WIMLIB_UPDATE_OP_RENAME;
1596 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1597 command_name, line_number);
1602 /* Parse additional options and non-options as needed */
1607 ret = parse_string(&line, &len, &next_string);
1608 if (ret == PARSE_STRING_NONE) /* End of line */
1610 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1612 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1614 if (!update_command_add_option(op, next_string, command))
1616 imagex_error(T("Unrecognized option \"%"TS"\" to "
1617 "update command \"%"TS"\" on line %zu"),
1618 next_string, command_name, line_number);
1624 if (num_nonoptions == update_command_num_nonoptions[op])
1626 imagex_error(T("Unexpected argument \"%"TS"\" in "
1627 "update command on line %zu\n"
1628 " (The \"%"TS"\" command only "
1629 "takes %zu nonoption arguments!)\n"),
1630 next_string, line_number,
1631 command_name, num_nonoptions);
1634 update_command_add_nonoption(op, next_string,
1635 command, num_nonoptions);
1640 if (num_nonoptions != update_command_num_nonoptions[op]) {
1641 imagex_error(T("Not enough arguments to update command "
1642 "\"%"TS"\" on line %zu"), command_name, line_number);
1648 static struct wimlib_update_command *
1649 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1650 size_t *num_cmds_ret)
1654 struct wimlib_update_command *cmds;
1657 nlines = text_file_count_lines(cmd_file_contents_p,
1662 /* Always allocate at least 1 slot, just in case the implementation of
1663 * calloc() returns NULL if 0 bytes are requested. */
1664 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1666 imagex_error(T("out of memory"));
1669 p = *cmd_file_contents_p;
1671 for (i = 0; i < nlines; i++) {
1672 /* XXX: Could use rawmemchr() here instead, but it may not be
1673 * available on all platforms. */
1674 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1675 size_t len = endp - p + 1;
1677 if (!is_comment_line(p, len)) {
1678 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1689 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1690 * one image from a WIM file to an NTFS volume. */
1692 imagex_apply(int argc, tchar **argv, int cmd)
1696 int image = WIMLIB_NO_IMAGE;
1698 struct wimlib_wim_info info;
1700 const tchar *wimfile;
1701 const tchar *target;
1702 const tchar *image_num_or_name = NULL;
1703 int extract_flags = 0;
1705 STRING_LIST(refglobs);
1707 for_opt(c, apply_options) {
1709 case IMAGEX_CHECK_OPTION:
1710 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1712 case IMAGEX_VERBOSE_OPTION:
1713 /* No longer does anything. */
1715 case IMAGEX_REF_OPTION:
1716 ret = string_list_append(&refglobs, optarg);
1718 goto out_free_refglobs;
1720 case IMAGEX_UNIX_DATA_OPTION:
1721 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1723 case IMAGEX_NO_ACLS_OPTION:
1724 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1726 case IMAGEX_STRICT_ACLS_OPTION:
1727 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1729 case IMAGEX_NO_ATTRIBUTES_OPTION:
1730 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1732 case IMAGEX_NORPFIX_OPTION:
1733 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1735 case IMAGEX_RPFIX_OPTION:
1736 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1738 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1739 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1740 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1742 case IMAGEX_WIMBOOT_OPTION:
1743 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1745 case IMAGEX_COMPACT_OPTION:
1746 ret = set_compact_mode(optarg, &extract_flags);
1748 goto out_free_refglobs;
1756 if (argc != 2 && argc != 3)
1761 if (!tstrcmp(wimfile, T("-"))) {
1762 /* Attempt to apply pipable WIM from standard input. */
1764 image_num_or_name = NULL;
1767 image_num_or_name = argv[1];
1772 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1773 imagex_progress_func, NULL);
1775 goto out_free_refglobs;
1777 wimlib_get_wim_info(wim, &info);
1780 /* Image explicitly specified. */
1781 image_num_or_name = argv[1];
1782 image = wimlib_resolve_image(wim, image_num_or_name);
1783 ret = verify_image_exists(image, image_num_or_name, wimfile);
1785 goto out_wimlib_free;
1788 /* No image specified; default to image 1, but only if the WIM
1789 * contains exactly one image. */
1791 if (info.image_count != 1) {
1792 imagex_error(T("\"%"TS"\" contains %d images; "
1793 "Please select one (or all)."),
1794 wimfile, info.image_count);
1803 if (refglobs.num_strings) {
1805 imagex_error(T("Can't specify --ref when applying from stdin!"));
1807 goto out_wimlib_free;
1809 ret = wim_reference_globs(wim, &refglobs, open_flags);
1811 goto out_wimlib_free;
1816 /* Interpret a regular file or block device target as an NTFS
1820 if (tstat(target, &stbuf)) {
1821 if (errno != ENOENT) {
1822 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1825 goto out_wimlib_free;
1828 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1829 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1835 ret = wimlib_extract_image(wim, image, target, extract_flags);
1837 set_fd_to_binary_mode(STDIN_FILENO);
1838 ret = wimlib_extract_image_from_pipe_with_progress(
1843 imagex_progress_func,
1847 imagex_printf(T("Done applying WIM image.\n"));
1848 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1850 do_resource_not_found_warning(wimfile, &info, &refglobs);
1852 imagex_error(T( "If you are applying an image "
1853 "from a split pipable WIM,\n"
1854 " make sure you have "
1855 "concatenated together all parts."));
1857 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1858 do_metadata_not_found_warning(wimfile, &info);
1863 string_list_destroy(&refglobs);
1867 usage(CMD_APPLY, stderr);
1869 goto out_free_refglobs;
1873 * Create a WIM image from a directory tree, NTFS volume, or multiple files or
1874 * directory trees. 'wimcapture': create a new WIM file containing the desired
1875 * image. 'wimappend': add a new image to an existing WIM file; or, with
1876 * '--create' behave like 'wimcapture' if the WIM file doesn't exist.
1879 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1882 bool create = false;
1883 bool appending = (cmd == CMD_APPEND);
1885 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1886 WIMLIB_ADD_FLAG_WINCONFIG |
1887 WIMLIB_ADD_FLAG_VERBOSE |
1888 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1889 int write_flags = 0;
1890 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1891 uint32_t chunk_size = UINT32_MAX;
1892 uint32_t solid_chunk_size = UINT32_MAX;
1893 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1894 const tchar *wimfile;
1897 STRING_LIST(image_properties);
1900 STRING_LIST(base_wimfiles);
1901 WIMStruct **base_wims;
1903 WIMStruct *template_wim = NULL;
1904 const tchar *template_wimfile = NULL;
1905 const tchar *template_image_name_or_num = NULL;
1906 int template_image = WIMLIB_NO_IMAGE;
1909 unsigned num_threads = 0;
1914 tchar *config_file = NULL;
1916 bool source_list = false;
1917 size_t source_list_nchars = 0;
1918 tchar *source_list_contents;
1919 bool capture_sources_malloced;
1920 struct wimlib_capture_source *capture_sources;
1922 bool name_defaulted;
1924 for_opt(c, capture_or_append_options) {
1926 case IMAGEX_BOOT_OPTION:
1927 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1929 case IMAGEX_CHECK_OPTION:
1930 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1932 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
1933 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1935 case IMAGEX_NOCHECK_OPTION:
1936 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1938 case IMAGEX_CONFIG_OPTION:
1939 config_file = optarg;
1940 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1942 case IMAGEX_COMPRESS_OPTION:
1943 compression_type = get_compression_type(optarg, false);
1944 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1947 case IMAGEX_CHUNK_SIZE_OPTION:
1948 chunk_size = parse_chunk_size(optarg);
1949 if (chunk_size == UINT32_MAX)
1952 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1953 solid_chunk_size = parse_chunk_size(optarg);
1954 if (solid_chunk_size == UINT32_MAX)
1957 case IMAGEX_SOLID_COMPRESS_OPTION:
1958 solid_ctype = get_compression_type(optarg, true);
1959 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1962 case IMAGEX_SOLID_OPTION:
1963 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1965 case IMAGEX_NO_SOLID_SORT_OPTION:
1966 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1968 case IMAGEX_FLAGS_OPTION: {
1969 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1970 tsprintf(p, T("FLAGS=%"TS), optarg);
1971 ret = string_list_append(&image_properties, p);
1976 case IMAGEX_IMAGE_PROPERTY_OPTION:
1977 ret = append_image_property_argument(&image_properties);
1981 case IMAGEX_DEREFERENCE_OPTION:
1982 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1984 case IMAGEX_VERBOSE_OPTION:
1985 /* No longer does anything. */
1987 case IMAGEX_THREADS_OPTION:
1988 num_threads = parse_num_threads(optarg);
1989 if (num_threads == UINT_MAX)
1992 case IMAGEX_REBUILD_OPTION:
1993 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1995 case IMAGEX_UNIX_DATA_OPTION:
1996 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1998 case IMAGEX_SOURCE_LIST_OPTION:
2001 case IMAGEX_NO_ACLS_OPTION:
2002 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
2004 case IMAGEX_STRICT_ACLS_OPTION:
2005 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
2007 case IMAGEX_RPFIX_OPTION:
2008 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
2010 case IMAGEX_NORPFIX_OPTION:
2011 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
2013 case IMAGEX_PIPABLE_OPTION:
2014 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2016 case IMAGEX_NOT_PIPABLE_OPTION:
2017 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2019 case IMAGEX_UPDATE_OF_OPTION:
2020 if (template_image_name_or_num) {
2021 imagex_error(T("'--update-of' can only be "
2022 "specified one time!"));
2026 colon = tstrrchr(optarg, T(':'));
2029 template_wimfile = optarg;
2031 template_image_name_or_num = colon + 1;
2033 template_wimfile = NULL;
2034 template_image_name_or_num = optarg;
2038 imagex_printf(T("[WARNING] '--update-of' is unreliable on Windows!\n"));
2041 case IMAGEX_DELTA_FROM_OPTION:
2042 ret = string_list_append(&base_wimfiles, optarg);
2045 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2047 case IMAGEX_WIMBOOT_OPTION:
2048 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2050 case IMAGEX_UNSAFE_COMPACT_OPTION:
2051 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2053 case IMAGEX_SNAPSHOT_OPTION:
2054 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2056 case IMAGEX_CREATE_OPTION:
2057 if (cmd == CMD_CAPTURE) {
2058 imagex_error(T("'--create' is only valid for 'wimappend', not 'wimcapture'"));
2070 if (argc < 2 || argc > 4)
2076 /* Set default compression type and parameters. */
2079 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2080 /* No compression type specified. Use the default. */
2082 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2083 /* With --wimboot, default to XPRESS compression. */
2084 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2085 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2086 /* With --solid, default to LZMS compression. (However,
2087 * this will not affect solid resources!) */
2088 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2090 /* Otherwise, default to LZX compression. */
2091 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2095 if (!tstrcmp(wimfile, T("-"))) {
2096 /* Writing captured WIM to standard output. */
2100 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2101 imagex_error("Can't write a non-pipable WIM to "
2102 "standard output! Specify --pipable\n"
2103 " if you want to create a pipable WIM "
2104 "(but read the docs first).");
2108 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2111 imagex_error(T("Using standard output for append does "
2112 "not make sense."));
2115 wim_fd = STDOUT_FILENO;
2117 imagex_output_to_stderr();
2118 set_fd_to_binary_mode(wim_fd);
2122 /* Check for 'wimappend --create' acting as wimcapture */
2123 if (create && tstat(wimfile, &stbuf) != 0 && errno == ENOENT) {
2127 /* Ignore '--update-of' for the target WIMFILE */
2128 if (template_image_name_or_num &&
2129 (!template_wimfile ||
2130 !tstrcmp(template_wimfile, wimfile)))
2132 template_image_name_or_num = NULL;
2133 template_wimfile = NULL;
2138 if ((write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) && !appending) {
2139 imagex_error(T("'--unsafe-compact' is only valid for append!"));
2143 /* If template image was specified using --update-of=IMAGE rather
2144 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2145 if (template_image_name_or_num && !template_wimfile) {
2146 if (base_wimfiles.num_strings == 1) {
2147 /* Capturing delta WIM based on single WIM: default to
2149 template_wimfile = base_wimfiles.strings[0];
2150 } else if (appending) {
2151 /* Appending to WIM: default to WIM being appended to.
2153 template_wimfile = wimfile;
2155 /* Capturing a normal (non-delta) WIM, so the WIM file
2156 * *must* be explicitly specified. */
2157 if (base_wimfiles.num_strings > 1) {
2158 imagex_error(T("For capture of delta WIM "
2159 "based on multiple existing "
2161 " '--update-of' must "
2162 "specify WIMFILE:IMAGE!"));
2164 imagex_error(T("For capture of non-delta WIM, "
2165 "'--update-of' must specify "
2174 name_defaulted = false;
2176 /* Set default name to SOURCE argument, omitting any directory
2177 * prefixes and trailing slashes. This requires making a copy
2178 * of @source. Leave some free characters at the end in case we
2179 * append a number to keep the name unique. */
2180 size_t source_name_len;
2182 source_name_len = tstrlen(source);
2183 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2184 name = tbasename(tstrcpy(source_copy, source));
2185 name_defaulted = true;
2188 /* Image description (if given). */
2190 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2191 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2192 ret = string_list_append(&image_properties, p);
2198 /* Set up capture sources in source list mode */
2199 if (source[0] == T('-') && source[1] == T('\0')) {
2200 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2202 source_list_contents = file_get_text_contents(source,
2203 &source_list_nchars);
2205 if (!source_list_contents)
2208 capture_sources = parse_source_list(&source_list_contents,
2211 if (!capture_sources) {
2213 goto out_free_source_list_contents;
2215 capture_sources_malloced = true;
2217 /* Set up capture source in non-source-list mode. */
2218 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2219 capture_sources[0].fs_source_path = source;
2220 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2221 capture_sources[0].reserved = 0;
2223 capture_sources_malloced = false;
2224 source_list_contents = NULL;
2227 /* Open the existing WIM, or create a new one. */
2229 ret = wimlib_open_wim_with_progress(wimfile,
2230 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2232 imagex_progress_func,
2235 goto out_free_capture_sources;
2237 ret = wimlib_create_new_wim(compression_type, &wim);
2239 goto out_free_capture_sources;
2240 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2243 /* Set chunk size if non-default. */
2244 if (chunk_size != UINT32_MAX) {
2245 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2248 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2250 int ctype = compression_type;
2253 struct wimlib_wim_info info;
2254 wimlib_get_wim_info(wim, &info);
2255 ctype = info.compression_type;
2258 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2259 ret = wimlib_set_output_chunk_size(wim, 4096);
2264 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2265 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2269 if (solid_chunk_size != UINT32_MAX) {
2270 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2276 /* Detect if source is regular file or block device and set NTFS volume
2281 if (tstat(source, &stbuf) == 0) {
2282 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2283 imagex_printf(T("Capturing WIM image from NTFS "
2284 "filesystem on \"%"TS"\"\n"), source);
2285 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2288 if (errno != ENOENT) {
2289 imagex_error_with_errno(T("Failed to stat "
2290 "\"%"TS"\""), source);
2298 /* If the user did not specify an image name, and the basename of the
2299 * source already exists as an image name in the WIM file, append a
2300 * suffix to make it unique. */
2301 if (appending && name_defaulted) {
2302 unsigned long conflict_idx;
2303 tchar *name_end = tstrchr(name, T('\0'));
2304 for (conflict_idx = 1;
2305 wimlib_image_name_in_use(wim, name);
2308 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2312 /* If capturing a delta WIM, reference resources from the base WIMs
2313 * before adding the new image. */
2314 if (base_wimfiles.num_strings) {
2315 base_wims = calloc(base_wimfiles.num_strings,
2316 sizeof(base_wims[0]));
2317 if (base_wims == NULL) {
2318 imagex_error(T("Out of memory!"));
2323 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2324 ret = wimlib_open_wim_with_progress(
2325 base_wimfiles.strings[i], open_flags,
2326 &base_wims[i], imagex_progress_func, NULL);
2328 goto out_free_base_wims;
2332 ret = wimlib_reference_resources(wim, base_wims,
2333 base_wimfiles.num_strings, 0);
2335 goto out_free_base_wims;
2337 if (base_wimfiles.num_strings == 1) {
2338 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2339 base_wimfiles.strings[0]);
2341 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2342 base_wimfiles.num_strings);
2349 /* If capturing or appending as an update of an existing (template) image,
2350 * open the WIM if needed and parse the image index. */
2351 if (template_image_name_or_num) {
2353 if (appending && !tstrcmp(template_wimfile, wimfile)) {
2356 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2357 if (!tstrcmp(template_wimfile,
2358 base_wimfiles.strings[i])) {
2359 template_wim = base_wims[i];
2365 if (!template_wim) {
2366 ret = wimlib_open_wim_with_progress(template_wimfile,
2369 imagex_progress_func,
2372 goto out_free_base_wims;
2375 template_image = wimlib_resolve_image(template_wim,
2376 template_image_name_or_num);
2378 if (template_image_name_or_num[0] == T('-')) {
2381 struct wimlib_wim_info info;
2383 wimlib_get_wim_info(template_wim, &info);
2384 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2385 if (n >= 1 && n <= info.image_count &&
2387 tmp != template_image_name_or_num + 1)
2389 template_image = info.image_count - (n - 1);
2392 ret = verify_image_exists_and_is_single(template_image,
2393 template_image_name_or_num,
2396 goto out_free_template_wim;
2399 ret = wimlib_add_image_multisource(wim,
2406 goto out_free_template_wim;
2408 if (image_properties.num_strings || template_image_name_or_num) {
2409 /* User asked to set additional image properties, or an image on
2410 * which the added one is to be based has been specified with
2412 struct wimlib_wim_info info;
2414 wimlib_get_wim_info(wim, &info);
2416 ret = apply_image_properties(&image_properties, wim,
2417 info.image_count, NULL);
2419 goto out_free_template_wim;
2421 /* Reference template image if the user provided one. */
2422 if (template_image_name_or_num) {
2423 imagex_printf(T("Using image %d "
2424 "from \"%"TS"\" as template\n"),
2425 template_image, template_wimfile);
2426 ret = wimlib_reference_template_image(wim,
2432 goto out_free_template_wim;
2436 /* Write the new WIM or overwrite the existing WIM with the new image
2439 ret = wimlib_overwrite(wim, write_flags, num_threads);
2440 } else if (wimfile) {
2441 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2442 write_flags, num_threads);
2444 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2445 write_flags, num_threads);
2447 out_free_template_wim:
2448 /* 'template_wim' may alias 'wim' or any of the 'base_wims' */
2449 if (template_wim == wim)
2450 goto out_free_base_wims;
2451 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2452 if (template_wim == base_wims[i])
2453 goto out_free_base_wims;
2454 wimlib_free(template_wim);
2456 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2457 wimlib_free(base_wims[i]);
2461 out_free_capture_sources:
2462 if (capture_sources_malloced)
2463 free(capture_sources);
2464 out_free_source_list_contents:
2465 free(source_list_contents);
2467 string_list_destroy(&image_properties);
2468 string_list_destroy(&base_wimfiles);
2478 /* Remove image(s) from a WIM. */
2480 imagex_delete(int argc, tchar **argv, int cmd)
2483 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2484 int write_flags = 0;
2485 const tchar *wimfile;
2486 const tchar *image_num_or_name;
2491 for_opt(c, delete_options) {
2493 case IMAGEX_CHECK_OPTION:
2494 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2496 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2497 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2499 case IMAGEX_SOFT_OPTION:
2500 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2502 case IMAGEX_UNSAFE_COMPACT_OPTION:
2503 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2514 imagex_error(T("Must specify a WIM file"));
2516 imagex_error(T("Must specify an image"));
2520 image_num_or_name = argv[1];
2522 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2523 imagex_progress_func, NULL);
2527 image = wimlib_resolve_image(wim, image_num_or_name);
2529 ret = verify_image_exists(image, image_num_or_name, wimfile);
2531 goto out_wimlib_free;
2533 ret = wimlib_delete_image(wim, image);
2535 imagex_error(T("Failed to delete image from \"%"TS"\""),
2537 goto out_wimlib_free;
2540 ret = wimlib_overwrite(wim, write_flags, 0);
2542 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2543 "deleted"), wimfile);
2551 usage(CMD_DELETE, stderr);
2556 struct print_dentry_options {
2561 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2563 tprintf(T("%"TS"\n"), dentry->full_path);
2566 static const struct {
2569 } file_attr_flags[] = {
2570 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2571 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2572 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2573 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2574 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2575 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2576 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2577 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2578 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2579 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2580 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2581 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2582 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2583 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2584 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2587 #define TIMESTR_MAX 100
2590 print_time(const tchar *type, const struct wimlib_timespec *wts,
2593 tchar timestr[TIMESTR_MAX];
2597 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2598 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2603 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2604 timestr[TIMESTR_MAX - 1] = '\0';
2606 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2609 static void print_byte_field(const uint8_t field[], size_t len)
2612 tprintf(T("%02hhx"), *field++);
2616 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2618 tchar attr_string[256];
2621 tputs(T("WIM Information:"));
2622 tputs(T("----------------"));
2623 tprintf(T("Path: %"TS"\n"), wimfile);
2624 tprintf(T("GUID: 0x"));
2625 print_byte_field(info->guid, sizeof(info->guid));
2627 tprintf(T("Version: %u\n"), info->wim_version);
2628 tprintf(T("Image Count: %d\n"), info->image_count);
2629 tprintf(T("Compression: %"TS"\n"),
2630 wimlib_get_compression_type_string(info->compression_type));
2631 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2633 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2634 tprintf(T("Boot Index: %d\n"), info->boot_index);
2635 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2637 attr_string[0] = T('\0');
2640 tstrcat(attr_string, T("Pipable, "));
2642 if (info->has_integrity_table)
2643 tstrcat(attr_string, T("Integrity info, "));
2645 if (info->has_rpfix)
2646 tstrcat(attr_string, T("Relative path junction, "));
2648 if (info->resource_only)
2649 tstrcat(attr_string, T("Resource only, "));
2651 if (info->metadata_only)
2652 tstrcat(attr_string, T("Metadata only, "));
2654 if (info->is_marked_readonly)
2655 tstrcat(attr_string, T("Readonly, "));
2657 p = tstrchr(attr_string, T('\0'));
2658 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2661 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2665 print_resource(const struct wimlib_resource_entry *resource,
2668 tprintf(T("Hash = 0x"));
2669 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2672 if (!resource->is_missing) {
2673 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2674 resource->uncompressed_size);
2675 if (resource->packed) {
2676 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2677 "bytes @ offset %"PRIu64"\n"),
2678 resource->raw_resource_uncompressed_size,
2679 resource->raw_resource_compressed_size,
2680 resource->raw_resource_offset_in_wim);
2682 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2685 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2686 resource->compressed_size);
2688 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2692 tprintf(T("Part Number = %u\n"), resource->part_number);
2693 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2695 tprintf(T("Flags = "));
2696 if (resource->is_compressed)
2697 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2698 if (resource->is_metadata)
2699 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2700 if (resource->is_free)
2701 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2702 if (resource->is_spanned)
2703 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2704 if (resource->packed)
2705 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2713 print_blobs(WIMStruct *wim)
2715 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2720 default_print_security_descriptor(const uint8_t *sd, size_t size)
2722 tprintf(T("Security Descriptor = "));
2723 print_byte_field(sd, size);
2729 is_null_guid(const uint8_t *guid)
2731 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2733 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2737 print_guid(const tchar *label, const uint8_t *guid)
2739 if (is_null_guid(guid))
2741 tprintf(T("%-20"TS"= 0x"), label);
2742 print_byte_field(guid, WIMLIB_GUID_LEN);
2747 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2750 "----------------------------------------------------------------------------\n"));
2751 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2752 if (dentry->dos_name)
2753 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2754 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2755 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2756 if (file_attr_flags[i].flag & dentry->attributes)
2757 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2758 file_attr_flags[i].name);
2760 if (dentry->security_descriptor) {
2761 print_security_descriptor(dentry->security_descriptor,
2762 dentry->security_descriptor_size);
2765 print_time(T("Creation Time"),
2766 &dentry->creation_time, dentry->creation_time_high);
2767 print_time(T("Last Write Time"),
2768 &dentry->last_write_time, dentry->last_write_time_high);
2769 print_time(T("Last Access Time"),
2770 &dentry->last_access_time, dentry->last_access_time_high);
2773 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2774 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2776 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2777 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2779 if (dentry->unix_mode != 0) {
2780 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2781 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2782 dentry->unix_uid, dentry->unix_gid,
2783 dentry->unix_mode, dentry->unix_rdev);
2786 if (!is_null_guid(dentry->object_id.object_id)) {
2787 print_guid(T("Object ID"), dentry->object_id.object_id);
2788 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2789 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2790 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2793 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2794 if (dentry->streams[i].stream_name) {
2795 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2796 dentry->streams[i].stream_name);
2797 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2798 tprintf(T("\tRaw encrypted data stream:\n"));
2799 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2800 tprintf(T("\tReparse point stream:\n"));
2802 tprintf(T("\tUnnamed data stream:\n"));
2804 print_resource(&dentry->streams[i].resource, NULL);
2809 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2811 const struct print_dentry_options *options = _options;
2812 if (!options->detailed)
2813 print_dentry_full_path(dentry);
2815 print_dentry_detailed(dentry);
2819 /* Print the files contained in an image(s) in a WIM file. */
2821 imagex_dir(int argc, tchar **argv, int cmd)
2823 const tchar *wimfile;
2824 WIMStruct *wim = NULL;
2827 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2829 struct print_dentry_options options = {
2832 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2834 STRING_LIST(refglobs);
2836 for_opt(c, dir_options) {
2838 case IMAGEX_PATH_OPTION:
2841 case IMAGEX_DETAILED_OPTION:
2842 options.detailed = true;
2844 case IMAGEX_ONE_FILE_ONLY_OPTION:
2845 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2847 case IMAGEX_REF_OPTION:
2848 ret = string_list_append(&refglobs, optarg);
2850 goto out_free_refglobs;
2860 imagex_error(T("Must specify a WIM file"));
2864 imagex_error(T("Too many arguments"));
2869 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2870 imagex_progress_func, NULL);
2872 goto out_free_refglobs;
2875 image = wimlib_resolve_image(wim, argv[1]);
2876 ret = verify_image_exists(image, argv[1], wimfile);
2878 goto out_wimlib_free;
2880 /* No image specified; default to image 1, but only if the WIM
2881 * contains exactly one image. */
2883 struct wimlib_wim_info info;
2885 wimlib_get_wim_info(wim, &info);
2886 if (info.image_count != 1) {
2887 imagex_error(T("\"%"TS"\" contains %d images; Please "
2888 "select one (or all)."),
2889 wimfile, info.image_count);
2896 if (refglobs.num_strings) {
2897 ret = wim_reference_globs(wim, &refglobs, 0);
2899 goto out_wimlib_free;
2902 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2903 print_dentry, &options);
2904 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2905 struct wimlib_wim_info info;
2907 wimlib_get_wim_info(wim, &info);
2908 do_metadata_not_found_warning(wimfile, &info);
2913 string_list_destroy(&refglobs);
2917 usage(CMD_DIR, stderr);
2919 goto out_free_refglobs;
2922 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2925 imagex_export(int argc, tchar **argv, int cmd)
2929 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2930 int write_flags = 0;
2931 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2932 const tchar *src_wimfile;
2933 const tchar *src_image_num_or_name;
2934 const tchar *dest_wimfile;
2936 const tchar *dest_name;
2937 const tchar *dest_desc;
2939 struct wimlib_wim_info src_info;
2940 WIMStruct *dest_wim;
2945 STRING_LIST(refglobs);
2946 unsigned num_threads = 0;
2947 uint32_t chunk_size = UINT32_MAX;
2948 uint32_t solid_chunk_size = UINT32_MAX;
2949 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2951 for_opt(c, export_options) {
2953 case IMAGEX_BOOT_OPTION:
2954 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2956 case IMAGEX_CHECK_OPTION:
2957 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2959 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2960 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2962 case IMAGEX_NOCHECK_OPTION:
2963 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2965 case IMAGEX_COMPRESS_OPTION:
2966 compression_type = get_compression_type(optarg, false);
2967 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2970 case IMAGEX_RECOMPRESS_OPTION:
2971 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2973 case IMAGEX_SOLID_OPTION:
2974 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2976 case IMAGEX_NO_SOLID_SORT_OPTION:
2977 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2979 case IMAGEX_CHUNK_SIZE_OPTION:
2980 chunk_size = parse_chunk_size(optarg);
2981 if (chunk_size == UINT32_MAX)
2984 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2985 solid_chunk_size = parse_chunk_size(optarg);
2986 if (solid_chunk_size == UINT32_MAX)
2989 case IMAGEX_SOLID_COMPRESS_OPTION:
2990 solid_ctype = get_compression_type(optarg, true);
2991 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2994 case IMAGEX_REF_OPTION:
2995 ret = string_list_append(&refglobs, optarg);
2997 goto out_free_refglobs;
2999 case IMAGEX_THREADS_OPTION:
3000 num_threads = parse_num_threads(optarg);
3001 if (num_threads == UINT_MAX)
3004 case IMAGEX_REBUILD_OPTION:
3005 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
3007 case IMAGEX_PIPABLE_OPTION:
3008 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3010 case IMAGEX_NOT_PIPABLE_OPTION:
3011 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3013 case IMAGEX_WIMBOOT_OPTION:
3014 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
3016 case IMAGEX_UNSAFE_COMPACT_OPTION:
3017 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3025 if (argc < 3 || argc > 5)
3027 src_wimfile = argv[0];
3028 src_image_num_or_name = argv[1];
3029 dest_wimfile = argv[2];
3030 dest_name = (argc >= 4) ? argv[3] : NULL;
3031 dest_desc = (argc >= 5) ? argv[4] : NULL;
3032 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
3033 imagex_progress_func, NULL);
3035 goto out_free_refglobs;
3037 wimlib_get_wim_info(src_wim, &src_info);
3039 /* Determine if the destination is an existing file or not. If so, we
3040 * try to append the exported image(s) to it; otherwise, we create a new
3041 * WIM containing the exported image(s). Furthermore, determine if we
3042 * need to write a pipable WIM directly to standard output. */
3044 if (tstrcmp(dest_wimfile, T("-")) == 0) {
3046 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
3047 imagex_error("Can't write a non-pipable WIM to "
3048 "standard output! Specify --pipable\n"
3049 " if you want to create a pipable WIM "
3050 "(but read the docs first).");
3052 goto out_free_src_wim;
3055 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3057 dest_wimfile = NULL;
3058 dest_wim_fd = STDOUT_FILENO;
3059 imagex_output_to_stderr();
3060 set_fd_to_binary_mode(dest_wim_fd);
3063 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
3065 /* Destination file exists. */
3067 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
3068 imagex_error(T("\"%"TS"\" is not a regular file "
3069 "or block device"), dest_wimfile);
3071 goto out_free_src_wim;
3073 ret = wimlib_open_wim_with_progress(dest_wimfile,
3075 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3077 imagex_progress_func,
3080 goto out_free_src_wim;
3082 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3083 /* The user specified a compression type, but we're
3084 * exporting to an existing WIM. Make sure the
3085 * specified compression type is the same as the
3086 * compression type of the existing destination WIM. */
3087 struct wimlib_wim_info dest_info;
3089 wimlib_get_wim_info(dest_wim, &dest_info);
3090 if (compression_type != dest_info.compression_type) {
3091 imagex_error(T("Cannot specify a compression type that is "
3092 "not the same as that used in the "
3093 "destination WIM"));
3095 goto out_free_dest_wim;
3101 if (errno != ENOENT) {
3102 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3105 goto out_free_src_wim;
3108 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3109 imagex_error(T("'--unsafe-compact' is only valid when "
3110 "exporting to an existing WIM file!"));
3112 goto out_free_src_wim;
3115 /* dest_wimfile is not an existing file, so create a new WIM. */
3117 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3118 /* The user did not specify a compression type; default
3119 * to that of the source WIM, unless --solid or
3120 * --wimboot was specified. */
3122 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3123 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3124 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3125 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3127 compression_type = src_info.compression_type;
3129 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3131 goto out_free_src_wim;
3133 wimlib_register_progress_function(dest_wim,
3134 imagex_progress_func, NULL);
3136 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3137 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3139 /* For --wimboot export, use small XPRESS chunks. */
3140 wimlib_set_output_chunk_size(dest_wim, 4096);
3141 } else if (compression_type == src_info.compression_type &&
3142 chunk_size == UINT32_MAX)
3144 /* Use same chunk size if compression type is the same. */
3145 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3149 if (chunk_size != UINT32_MAX) {
3150 /* Set destination chunk size. */
3151 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3153 goto out_free_dest_wim;
3155 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3156 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3158 goto out_free_dest_wim;
3160 if (solid_chunk_size != UINT32_MAX) {
3161 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3163 goto out_free_dest_wim;
3166 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3167 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3169 goto out_free_dest_wim;
3171 if (refglobs.num_strings) {
3172 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3174 goto out_free_dest_wim;
3177 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3178 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3180 imagex_error(T("--boot specified for all-images export, but source WIM "
3181 "has no bootable image."));
3183 goto out_free_dest_wim;
3186 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3187 dest_desc, export_flags);
3189 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3190 do_resource_not_found_warning(src_wimfile,
3191 &src_info, &refglobs);
3192 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3193 do_metadata_not_found_warning(src_wimfile, &src_info);
3195 goto out_free_dest_wim;
3199 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3200 else if (dest_wimfile)
3201 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3202 write_flags, num_threads);
3204 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3205 WIMLIB_ALL_IMAGES, write_flags,
3208 wimlib_free(dest_wim);
3210 wimlib_free(src_wim);
3212 string_list_destroy(&refglobs);
3216 usage(CMD_EXPORT, stderr);
3219 goto out_free_refglobs;
3222 /* Extract files or directories from a WIM image */
3224 imagex_extract(int argc, tchar **argv, int cmd)
3231 const tchar *wimfile;
3232 const tchar *image_num_or_name;
3233 tchar *dest_dir = T(".");
3234 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3235 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3236 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3237 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3239 STRING_LIST(refglobs);
3241 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3243 for_opt(c, extract_options) {
3245 case IMAGEX_CHECK_OPTION:
3246 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3248 case IMAGEX_VERBOSE_OPTION:
3249 /* No longer does anything. */
3251 case IMAGEX_REF_OPTION:
3252 ret = string_list_append(&refglobs, optarg);
3254 goto out_free_refglobs;
3256 case IMAGEX_UNIX_DATA_OPTION:
3257 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3259 case IMAGEX_NO_ACLS_OPTION:
3260 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3262 case IMAGEX_STRICT_ACLS_OPTION:
3263 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3265 case IMAGEX_NO_ATTRIBUTES_OPTION:
3266 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3268 case IMAGEX_DEST_DIR_OPTION:
3271 case IMAGEX_TO_STDOUT_OPTION:
3272 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3273 imagex_suppress_output();
3274 set_fd_to_binary_mode(STDOUT_FILENO);
3276 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3277 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3278 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3280 case IMAGEX_NO_GLOBS_OPTION:
3281 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3283 case IMAGEX_NULLGLOB_OPTION:
3284 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3286 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3287 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3289 case IMAGEX_WIMBOOT_OPTION:
3290 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3292 case IMAGEX_COMPACT_OPTION:
3293 ret = set_compact_mode(optarg, &extract_flags);
3295 goto out_free_refglobs;
3307 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3308 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3310 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3315 image_num_or_name = argv[1];
3320 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3321 imagex_progress_func, NULL);
3323 goto out_free_refglobs;
3325 image = wimlib_resolve_image(wim, image_num_or_name);
3326 ret = verify_image_exists_and_is_single(image,
3330 goto out_wimlib_free;
3332 if (refglobs.num_strings) {
3333 ret = wim_reference_globs(wim, &refglobs, open_flags);
3335 goto out_wimlib_free;
3341 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3344 while (argc != 0 && ret == 0) {
3348 num_paths < argc && argv[num_paths][0] != T('@');
3353 ret = wimlib_extract_paths(wim, image, dest_dir,
3354 (const tchar **)argv,
3356 extract_flags | notlist_extract_flags);
3360 const tchar *listfile = argv[0] + 1;
3362 if (!tstrcmp(listfile, T("-"))) {
3363 tputs(T("Reading pathlist file from standard input..."));
3367 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3368 listfile, extract_flags);
3375 imagex_printf(T("Done extracting files.\n"));
3376 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3377 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3378 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3379 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3380 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3383 T("Note: You can use the '--nullglob' "
3384 "option to ignore missing files.\n"));
3386 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3387 "files and directories\n"
3388 " are in the WIM image.\n"),
3389 get_cmd_string(CMD_DIR, false));
3390 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3391 struct wimlib_wim_info info;
3393 wimlib_get_wim_info(wim, &info);
3394 do_resource_not_found_warning(wimfile, &info, &refglobs);
3395 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3396 struct wimlib_wim_info info;
3398 wimlib_get_wim_info(wim, &info);
3399 do_metadata_not_found_warning(wimfile, &info);
3404 string_list_destroy(&refglobs);
3408 usage(CMD_EXTRACT, stderr);
3411 goto out_free_refglobs;
3414 /* Prints information about a WIM file; also can mark an image as bootable,
3415 * change the name of an image, or change the description of an image. */
3417 imagex_info(int argc, tchar **argv, int cmd)
3421 bool header = false;
3424 bool short_header = true;
3425 const tchar *xml_out_file = NULL;
3426 const tchar *wimfile;
3427 const tchar *image_num_or_name;
3428 STRING_LIST(image_properties);
3433 int write_flags = 0;
3434 struct wimlib_wim_info info;
3436 for_opt(c, info_options) {
3438 case IMAGEX_BOOT_OPTION:
3441 case IMAGEX_CHECK_OPTION:
3442 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3444 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3445 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3447 case IMAGEX_NOCHECK_OPTION:
3448 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3450 case IMAGEX_HEADER_OPTION:
3452 short_header = false;
3454 case IMAGEX_BLOBS_OPTION:
3456 short_header = false;
3458 case IMAGEX_XML_OPTION:
3460 short_header = false;
3462 case IMAGEX_EXTRACT_XML_OPTION:
3463 xml_out_file = optarg;
3464 short_header = false;
3466 case IMAGEX_IMAGE_PROPERTY_OPTION:
3467 ret = append_image_property_argument(&image_properties);
3478 if (argc < 1 || argc > 4)
3482 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3486 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3487 tsprintf(p, T("NAME=%"TS), argv[2]);
3488 ret = string_list_append(&image_properties, p);
3495 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3496 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3497 ret = string_list_append(&image_properties, p);
3502 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3503 imagex_progress_func, NULL);
3507 wimlib_get_wim_info(wim, &info);
3509 image = wimlib_resolve_image(wim, image_num_or_name);
3510 ret = WIMLIB_ERR_INVALID_IMAGE;
3511 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3512 verify_image_exists(image, image_num_or_name, wimfile);
3514 imagex_error(T("If you would like to set the boot "
3515 "index to 0, specify image \"0\" with "
3516 "the --boot flag."));
3518 goto out_wimlib_free;
3521 if (boot && info.image_count == 0) {
3522 imagex_error(T("--boot is meaningless on a WIM with no images"));
3523 goto out_wimlib_free;
3526 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3528 imagex_error(T("Cannot specify the --boot flag "
3529 "without specifying a specific "
3530 "image in a multi-image WIM"));
3531 goto out_wimlib_free;
3533 if (image_properties.num_strings) {
3534 imagex_error(T("Can't change image properties without "
3535 "specifying a specific image in a "
3536 "multi-image WIM"));
3537 goto out_wimlib_free;
3541 /* Operations that print information are separated from operations that
3542 * recreate the WIM file. */
3543 if (!image_properties.num_strings && !boot) {
3545 /* Read-only operations */
3547 if (image == WIMLIB_NO_IMAGE) {
3548 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3549 image_num_or_name, wimfile);
3550 goto out_wimlib_free;
3553 if (image == WIMLIB_ALL_IMAGES && short_header)
3554 print_wim_information(wimfile, &info);
3557 wimlib_print_header(wim);
3560 if (info.total_parts != 1) {
3561 tfprintf(stderr, T("Warning: Only showing the blobs "
3562 "for part %d of a %d-part WIM.\n"),
3563 info.part_number, info.total_parts);
3569 ret = wimlib_extract_xml_data(wim, stdout);
3571 goto out_wimlib_free;
3577 fp = tfopen(xml_out_file, T("wb"));
3579 imagex_error_with_errno(T("Failed to open the "
3580 "file \"%"TS"\" for "
3584 goto out_wimlib_free;
3586 ret = wimlib_extract_xml_data(wim, fp);
3588 imagex_error(T("Failed to close the file "
3594 goto out_wimlib_free;
3598 wimlib_print_available_images(wim, image);
3602 /* Modification operations */
3603 bool any_property_changes;
3605 if (image == WIMLIB_ALL_IMAGES)
3608 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3609 imagex_error(T("Cannot change image properties "
3610 "when using image 0"));
3612 goto out_wimlib_free;
3616 if (image == info.boot_index) {
3617 imagex_printf(T("Image %d is already marked as "
3618 "bootable.\n"), image);
3621 imagex_printf(T("Marking image %d as bootable.\n"),
3623 info.boot_index = image;
3624 ret = wimlib_set_wim_info(wim, &info,
3625 WIMLIB_CHANGE_BOOT_INDEX);
3627 goto out_wimlib_free;
3631 ret = apply_image_properties(&image_properties, wim, image,
3632 &any_property_changes);
3634 goto out_wimlib_free;
3636 /* Only call wimlib_overwrite() if something actually needs to
3638 if (boot || any_property_changes ||
3639 ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) &&
3640 !info.has_integrity_table) ||
3641 ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) &&
3642 info.has_integrity_table))
3644 ret = wimlib_overwrite(wim, write_flags, 1);
3646 imagex_printf(T("The file \"%"TS"\" was not modified "
3647 "because nothing needed to be done.\n"),
3655 string_list_destroy(&image_properties);
3659 usage(CMD_INFO, stderr);
3664 /* Join split WIMs into one part WIM */
3666 imagex_join(int argc, tchar **argv, int cmd)
3669 int swm_open_flags = 0;
3670 int wim_write_flags = 0;
3671 const tchar *output_path;
3674 for_opt(c, join_options) {
3676 case IMAGEX_CHECK_OPTION:
3677 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3679 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3680 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3690 imagex_error(T("Must specify one or more split WIM (.swm) "
3694 output_path = argv[0];
3695 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3700 imagex_progress_func,
3706 usage(CMD_JOIN, stderr);
3711 #if WIM_MOUNTING_SUPPORTED
3713 /* Mounts a WIM image. */
3715 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3718 int mount_flags = 0;
3720 const tchar *staging_dir = NULL;
3721 const tchar *wimfile;
3724 struct wimlib_wim_info info;
3728 STRING_LIST(refglobs);
3730 if (cmd == CMD_MOUNTRW) {
3731 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3732 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3735 for_opt(c, mount_options) {
3737 case IMAGEX_ALLOW_OTHER_OPTION:
3738 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3740 case IMAGEX_CHECK_OPTION:
3741 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3743 case IMAGEX_DEBUG_OPTION:
3744 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3746 case IMAGEX_STREAMS_INTERFACE_OPTION:
3747 if (!tstrcasecmp(optarg, T("none")))
3748 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3749 else if (!tstrcasecmp(optarg, T("xattr")))
3750 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3751 else if (!tstrcasecmp(optarg, T("windows")))
3752 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3754 imagex_error(T("Unknown stream interface \"%"TS"\""),
3759 case IMAGEX_REF_OPTION:
3760 ret = string_list_append(&refglobs, optarg);
3762 goto out_free_refglobs;
3764 case IMAGEX_STAGING_DIR_OPTION:
3765 staging_dir = optarg;
3767 case IMAGEX_UNIX_DATA_OPTION:
3768 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3776 if (argc != 2 && argc != 3)
3781 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3782 imagex_progress_func, NULL);
3784 goto out_free_refglobs;
3786 wimlib_get_wim_info(wim, &info);
3789 /* Image explicitly specified. */
3790 image = wimlib_resolve_image(wim, argv[1]);
3792 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3796 /* No image specified; default to image 1, but only if the WIM
3797 * contains exactly one image. */
3799 if (info.image_count != 1) {
3800 imagex_error(T("\"%"TS"\" contains %d images; Please "
3801 "select one."), wimfile, info.image_count);
3809 if (refglobs.num_strings) {
3810 ret = wim_reference_globs(wim, &refglobs, open_flags);
3815 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3817 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3818 do_metadata_not_found_warning(wimfile, &info);
3820 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3822 image, wimfile, dir);
3828 string_list_destroy(&refglobs);
3834 goto out_free_refglobs;
3836 #endif /* WIM_MOUNTING_SUPPORTED */
3838 /* Rebuild a WIM file */
3840 imagex_optimize(int argc, tchar **argv, int cmd)
3843 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3844 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3845 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3846 uint32_t chunk_size = UINT32_MAX;
3847 uint32_t solid_chunk_size = UINT32_MAX;
3848 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3851 const tchar *wimfile;
3854 unsigned num_threads = 0;
3856 for_opt(c, optimize_options) {
3858 case IMAGEX_CHECK_OPTION:
3859 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3861 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3862 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3864 case IMAGEX_NOCHECK_OPTION:
3865 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3867 case IMAGEX_COMPRESS_OPTION:
3868 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3869 compression_type = get_compression_type(optarg, false);
3870 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3873 case IMAGEX_RECOMPRESS_OPTION:
3874 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3876 case IMAGEX_CHUNK_SIZE_OPTION:
3877 chunk_size = parse_chunk_size(optarg);
3878 if (chunk_size == UINT32_MAX)
3881 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3882 solid_chunk_size = parse_chunk_size(optarg);
3883 if (solid_chunk_size == UINT32_MAX)
3886 case IMAGEX_SOLID_COMPRESS_OPTION:
3887 solid_ctype = get_compression_type(optarg, true);
3888 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3891 case IMAGEX_SOLID_OPTION:
3892 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3893 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3895 case IMAGEX_NO_SOLID_SORT_OPTION:
3896 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3898 case IMAGEX_THREADS_OPTION:
3899 num_threads = parse_num_threads(optarg);
3900 if (num_threads == UINT_MAX)
3903 case IMAGEX_PIPABLE_OPTION:
3904 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3906 case IMAGEX_NOT_PIPABLE_OPTION:
3907 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3909 case IMAGEX_UNSAFE_COMPACT_OPTION:
3910 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3924 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3925 imagex_progress_func, NULL);
3929 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3930 /* Change compression type. */
3931 ret = wimlib_set_output_compression_type(wim, compression_type);
3933 goto out_wimlib_free;
3936 if (chunk_size != UINT32_MAX) {
3937 /* Change chunk size. */
3938 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3940 goto out_wimlib_free;
3942 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3943 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3945 goto out_wimlib_free;
3947 if (solid_chunk_size != UINT32_MAX) {
3948 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3950 goto out_wimlib_free;
3953 old_size = file_get_size(wimfile);
3954 tprintf(T("\"%"TS"\" original size: "), wimfile);
3956 tputs(T("Unknown"));
3958 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3960 ret = wimlib_overwrite(wim, write_flags, num_threads);
3962 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3963 goto out_wimlib_free;
3966 new_size = file_get_size(wimfile);
3967 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3969 tputs(T("Unknown"));
3971 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3973 tfputs(T("Space saved: "), stdout);
3974 if (new_size != -1 && old_size != -1) {
3975 tprintf(T("%lld KiB\n"),
3976 ((long long)old_size - (long long)new_size) >> 10);
3978 tputs(T("Unknown"));
3987 usage(CMD_OPTIMIZE, stderr);
3993 /* Split a WIM into a spanned set */
3995 imagex_split(int argc, tchar **argv, int cmd)
3999 int write_flags = 0;
4000 unsigned long part_size;
4005 for_opt(c, split_options) {
4007 case IMAGEX_CHECK_OPTION:
4008 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4010 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4011 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4023 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
4024 if (tmp == argv[2] || *tmp) {
4025 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
4026 imagex_error(T("The part size must be an integer or "
4027 "floating-point number of megabytes."));
4030 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
4031 imagex_progress_func, NULL);
4035 ret = wimlib_split(wim, argv[1], part_size, write_flags);
4036 tprintf(T("\nFinished splitting \"%"TS"\"\n"), argv[0]);
4042 usage(CMD_SPLIT, stderr);
4048 #if WIM_MOUNTING_SUPPORTED
4049 /* Unmounts a mounted WIM image. */
4051 imagex_unmount(int argc, tchar **argv, int cmd)
4054 int unmount_flags = 0;
4057 for_opt(c, unmount_options) {
4059 case IMAGEX_COMMIT_OPTION:
4060 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
4062 case IMAGEX_CHECK_OPTION:
4063 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
4065 case IMAGEX_REBUILD_OPTION:
4066 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
4068 case IMAGEX_LAZY_OPTION:
4069 case IMAGEX_FORCE_OPTION:
4070 /* Now, unmount is lazy by default. However, committing
4071 * the image will fail with
4072 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4073 * file descriptors on the WIM image. The
4074 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4075 * descriptors to be closed. */
4076 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4078 case IMAGEX_NEW_IMAGE_OPTION:
4079 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4090 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4091 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4092 imagex_error(T("--new-image is meaningless "
4093 "without --commit also specified!"));
4098 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4099 imagex_progress_func, NULL);
4101 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4102 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4104 "\tNote: Use --commit --force to force changes "
4105 "to be committed, regardless\n"
4106 "\t of open files.\n"));
4113 usage(CMD_UNMOUNT, stderr);
4118 #endif /* WIM_MOUNTING_SUPPORTED */
4121 * Add, delete, or rename files in a WIM image.
4124 imagex_update(int argc, tchar **argv, int cmd)
4126 const tchar *wimfile;
4130 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4131 int write_flags = 0;
4132 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4133 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4134 WIMLIB_ADD_FLAG_VERBOSE |
4135 WIMLIB_ADD_FLAG_WINCONFIG;
4136 int default_delete_flags = 0;
4137 unsigned num_threads = 0;
4139 tchar *cmd_file_contents;
4140 size_t cmd_file_nchars;
4141 struct wimlib_update_command *cmds;
4143 tchar *command_str = NULL;
4144 tchar *config_file = NULL;
4145 tchar *wimboot_config = NULL;
4147 for_opt(c, update_options) {
4149 /* Generic or write options */
4150 case IMAGEX_THREADS_OPTION:
4151 num_threads = parse_num_threads(optarg);
4152 if (num_threads == UINT_MAX)
4155 case IMAGEX_CHECK_OPTION:
4156 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4158 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4159 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4161 case IMAGEX_REBUILD_OPTION:
4162 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4164 case IMAGEX_COMMAND_OPTION:
4166 imagex_error(T("--command may only be specified "
4167 "one time. Please provide\n"
4168 " the update commands "
4169 "on standard input instead."));
4172 command_str = tstrdup(optarg);
4174 imagex_error(T("Out of memory!"));
4178 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4179 wimboot_config = optarg;
4181 /* Default delete options */
4182 case IMAGEX_FORCE_OPTION:
4183 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4185 case IMAGEX_RECURSIVE_OPTION:
4186 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4189 /* Global add option */
4190 case IMAGEX_CONFIG_OPTION:
4191 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4192 config_file = optarg;
4195 /* Default add options */
4196 case IMAGEX_VERBOSE_OPTION:
4197 /* No longer does anything. */
4199 case IMAGEX_DEREFERENCE_OPTION:
4200 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4202 case IMAGEX_UNIX_DATA_OPTION:
4203 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4205 case IMAGEX_NO_ACLS_OPTION:
4206 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4208 case IMAGEX_STRICT_ACLS_OPTION:
4209 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4211 case IMAGEX_NO_REPLACE_OPTION:
4212 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4214 case IMAGEX_UNSAFE_COMPACT_OPTION:
4215 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4224 if (argc != 1 && argc != 2)
4228 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4229 imagex_progress_func, NULL);
4231 goto out_free_command_str;
4234 /* Image explicitly specified. */
4235 image = wimlib_resolve_image(wim, argv[1]);
4236 ret = verify_image_exists_and_is_single(image, argv[1],
4239 goto out_wimlib_free;
4241 /* No image specified; default to image 1, but only if the WIM
4242 * contains exactly one image. */
4243 struct wimlib_wim_info info;
4245 wimlib_get_wim_info(wim, &info);
4246 if (info.image_count != 1) {
4247 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4248 wimfile, info.image_count);
4255 /* Read update commands from standard input, or the command string if
4258 cmd_file_contents = NULL;
4259 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4263 goto out_free_cmd_file_contents;
4265 } else if (!wimboot_config) {
4266 if (isatty(STDIN_FILENO)) {
4267 tputs(T("Reading update commands from standard input..."));
4268 recommend_man_page(CMD_UPDATE, stdout);
4270 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4271 if (!cmd_file_contents) {
4273 goto out_wimlib_free;
4276 /* Parse the update commands */
4277 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4281 goto out_free_cmd_file_contents;
4284 cmd_file_contents = NULL;
4289 /* Set default flags and capture config on the update commands */
4290 for (size_t i = 0; i < num_cmds; i++) {
4291 switch (cmds[i].op) {
4292 case WIMLIB_UPDATE_OP_ADD:
4293 cmds[i].add.add_flags |= default_add_flags;
4294 cmds[i].add.config_file = config_file;
4296 case WIMLIB_UPDATE_OP_DELETE:
4297 cmds[i].delete_.delete_flags |= default_delete_flags;
4304 /* Execute the update commands */
4305 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4309 if (wimboot_config) {
4310 /* --wimboot-config=FILE is short for an
4311 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4313 struct wimlib_update_command cmd;
4315 cmd.op = WIMLIB_UPDATE_OP_ADD;
4316 cmd.add.fs_source_path = wimboot_config;
4317 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4318 cmd.add.config_file = NULL;
4319 cmd.add.add_flags = 0;
4321 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4326 /* Overwrite the updated WIM */
4327 ret = wimlib_overwrite(wim, write_flags, num_threads);
4330 out_free_cmd_file_contents:
4331 free(cmd_file_contents);
4334 out_free_command_str:
4339 usage(CMD_UPDATE, stderr);
4342 goto out_free_command_str;
4345 /* Verify a WIM file. */
4347 imagex_verify(int argc, tchar **argv, int cmd)
4350 const tchar *wimfile;
4352 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4353 int verify_flags = 0;
4354 STRING_LIST(refglobs);
4357 for_opt(c, verify_options) {
4359 case IMAGEX_REF_OPTION:
4360 ret = string_list_append(&refglobs, optarg);
4362 goto out_free_refglobs;
4364 case IMAGEX_NOCHECK_OPTION:
4365 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4377 imagex_error(T("Must specify a WIM file!"));
4379 imagex_error(T("At most one WIM file can be specified!"));
4385 ret = wimlib_open_wim_with_progress(wimfile,
4388 imagex_progress_func,
4391 goto out_free_refglobs;
4393 ret = wim_reference_globs(wim, &refglobs, open_flags);
4395 goto out_wimlib_free;
4397 ret = wimlib_verify_wim(wim, verify_flags);
4399 tputc(T('\n'), stderr);
4400 imagex_error(T("\"%"TS"\" failed verification!"),
4402 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4403 refglobs.num_strings == 0)
4405 imagex_printf(T("Note: if this WIM file is not standalone, "
4406 "use the --ref option to specify the other parts.\n"));
4409 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4416 string_list_destroy(&refglobs);
4420 usage(CMD_VERIFY, stderr);
4422 goto out_free_refglobs;
4425 struct imagex_command {
4427 int (*func)(int argc, tchar **argv, int cmd);
4430 static const struct imagex_command imagex_commands[] = {
4431 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4432 [CMD_APPLY] = {T("apply"), imagex_apply},
4433 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4434 [CMD_DELETE] = {T("delete"), imagex_delete},
4435 [CMD_DIR ] = {T("dir"), imagex_dir},
4436 [CMD_EXPORT] = {T("export"), imagex_export},
4437 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4438 [CMD_INFO] = {T("info"), imagex_info},
4439 [CMD_JOIN] = {T("join"), imagex_join},
4440 #if WIM_MOUNTING_SUPPORTED
4441 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4442 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4444 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4445 [CMD_SPLIT] = {T("split"), imagex_split},
4446 #if WIM_MOUNTING_SUPPORTED
4447 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4449 [CMD_UPDATE] = {T("update"), imagex_update},
4450 [CMD_VERIFY] = {T("verify"), imagex_verify},
4455 /* Can be a directory or source list file. But source list file is probably
4456 * a rare use case, so just say directory. */
4457 # define SOURCE_STR T("DIRECTORY")
4459 /* Can only be a directory */
4460 # define TARGET_STR T("DIRECTORY")
4463 /* Can be a directory, NTFS volume, or source list file. */
4464 # define SOURCE_STR T("SOURCE")
4466 /* Can be a directory or NTFS volume. */
4467 # define TARGET_STR T("TARGET")
4471 static const tchar * const usage_strings[] = {
4474 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4475 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4476 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4477 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4478 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4479 " [--dereference] [--snapshot] [--create]\n"
4483 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4484 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4485 " [--no-attributes] [--rpfix] [--norpfix]\n"
4486 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4487 " [--compact=FORMAT]\n"
4491 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4492 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4493 " [--config=FILE] [--threads=NUM_THREADS]\n"
4494 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4495 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4496 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4501 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4505 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4509 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4510 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4511 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4512 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4513 " [--wimboot] [--solid]\n"
4517 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4518 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4519 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4520 " [--no-attributes] [--include-invalid-names]\n"
4521 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4525 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4526 " [--boot] [--check] [--nocheck] [--xml]\n"
4527 " [--extract-xml FILE] [--header] [--blobs]\n"
4528 " [--image-property NAME=VALUE]\n"
4532 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4534 #if WIM_MOUNTING_SUPPORTED
4537 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4538 " [--check] [--streams-interface=INTERFACE]\n"
4539 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4543 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4544 " [--check] [--streams-interface=INTERFACE]\n"
4545 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4551 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4552 " [--check] [--nocheck] [--solid]\n"
4557 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4559 #if WIM_MOUNTING_SUPPORTED
4562 " %"TS" DIRECTORY\n"
4563 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4568 " %"TS" WIMFILE [IMAGE]\n"
4569 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4570 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4571 " [--command=STRING] [--wimboot-config=FILE]\n"
4576 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4580 static const tchar *invocation_name;
4581 static int invocation_cmd = CMD_NONE;
4583 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4585 static tchar buf[50];
4587 if (cmd == CMD_NONE)
4588 return T("wimlib-imagex");
4590 if (only_short_form || invocation_cmd != CMD_NONE) {
4591 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4593 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4594 imagex_commands[cmd].name);
4602 static const tchar * const fmt =
4604 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
4605 "Copyright (C) 2012-2018 Eric Biggers\n"
4606 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4607 "This is free software: you are free to change and redistribute it.\n"
4608 "There is NO WARRANTY, to the extent permitted by law.\n"
4610 "Report bugs to "PACKAGE_BUGREPORT".\n"
4612 tfprintf(stdout, fmt, wimlib_get_version_string());
4616 do_common_options(int *argc_p, tchar **argv, int cmd)
4622 for (i = 1; i < argc; i++) {
4624 if (p[0] == T('-') && p[1] == T('-')) {
4626 if (!tstrcmp(p, T("help"))) {
4627 if (cmd == CMD_NONE)
4632 } else if (!tstrcmp(p, T("version"))) {
4635 } else if (!tstrcmp(p, T("quiet"))) {
4636 imagex_suppress_output();
4637 memmove(&argv[i], &argv[i + 1],
4638 (argc - i) * sizeof(argv[i]));
4641 } else if (!*p) /* reached "--", no more options */
4650 print_usage_string(int cmd, FILE *fp)
4652 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4656 recommend_man_page(int cmd, FILE *fp)
4658 const tchar *format_str;
4660 format_str = T("Some uncommon options are not listed;\n"
4661 "See %"TS".pdf in the doc directory for more details.\n");
4663 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4665 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4669 usage(int cmd, FILE *fp)
4671 tfprintf(fp, T("Usage:\n"));
4672 print_usage_string(cmd, fp);
4673 tfprintf(fp, T("\n"));
4674 recommend_man_page(cmd, fp);
4680 tfprintf(fp, T("Usage:\n"));
4681 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4682 print_usage_string(cmd, fp);
4683 tfprintf(fp, T("\n"));
4685 static const tchar * const extra =
4688 " %"TS" --version\n"
4691 tfprintf(fp, extra, invocation_name, invocation_name);
4693 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4694 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4695 "For some commands IMAGE may be \"all\".\n"
4697 recommend_man_page(CMD_NONE, fp);
4701 extern int wmain(int argc, wchar_t **argv);
4705 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4706 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4707 * something else), while on Windows the command arguments will be UTF-16LE
4708 * encoded 'wchar_t' strings. */
4710 main(int argc, tchar **argv)
4716 imagex_info_file = stdout;
4717 invocation_name = tbasename(argv[0]);
4720 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4721 if (igcase != NULL) {
4722 if (!tstrcmp(igcase, T("no")) ||
4723 !tstrcmp(igcase, T("0")))
4724 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4725 else if (!tstrcmp(igcase, T("yes")) ||
4726 !tstrcmp(igcase, T("1")))
4727 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4730 "WARNING: Ignoring unknown setting of "
4731 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4736 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4738 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4739 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4740 for (int i = 0; i < CMD_MAX; i++) {
4741 if (!tstrcmp(invocation_name + 3,
4742 imagex_commands[i].name))
4751 /* Unless already known from the invocation name, determine which
4752 * command was specified. */
4753 if (cmd == CMD_NONE) {
4755 imagex_error(T("No command specified!\n"));
4759 for (int i = 0; i < CMD_MAX; i++) {
4760 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4765 if (cmd != CMD_NONE) {
4771 /* Handle common options. May exit early (for --help or --version). */
4772 do_common_options(&argc, argv, cmd);
4774 /* Bail if a valid command was not specified. */
4775 if (cmd == CMD_NONE) {
4776 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4781 /* Enable warning and error messages in wimlib to be more user-friendly.
4783 wimlib_set_print_errors(true);
4785 /* Initialize wimlib. */
4786 ret = wimlib_global_init(init_flags);
4788 goto out_check_status;
4790 /* Call the command handler function. */
4791 ret = imagex_commands[cmd].func(argc, argv, cmd);
4793 /* Check for error writing to standard output, especially since for some
4794 * commands, writing to standard output is part of the program's actual
4795 * behavior and not just for informational purposes. */
4796 if (ferror(stdout) || fclose(stdout)) {
4797 imagex_error_with_errno(T("error writing to standard output"));
4802 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4803 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4804 * error code from which an error message can be printed. */
4806 imagex_error(T("Exiting with error code %d:\n"
4808 wimlib_get_error_string(ret));
4809 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4810 imagex_error_with_errno(T("errno"));
4812 /* Make wimlib free any resources it's holding (although this is not
4813 * strictly necessary because the process is ending anyway). */
4814 wimlib_global_cleanup();