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;
1166 /* Progress callback function passed to various wimlib functions. */
1167 static enum wimlib_progress_status
1168 imagex_progress_func(enum wimlib_progress_msg msg,
1169 union wimlib_progress_info *info,
1170 void *_ignored_context)
1172 unsigned percent_done;
1173 unsigned unit_shift;
1174 const tchar *unit_name;
1177 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1179 static bool started;
1181 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1182 imagex_printf(T("Using %"TS" compression "
1183 "with %u thread%"TS"\n"),
1184 wimlib_get_compression_type_string(
1185 info->write_streams.compression_type),
1186 info->write_streams.num_threads,
1187 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1192 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1193 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1194 info->write_streams.total_bytes);
1196 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1197 info->write_streams.completed_bytes >> unit_shift,
1199 info->write_streams.total_bytes >> unit_shift,
1202 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1203 imagex_printf(T("\n"));
1205 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1206 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1207 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1208 imagex_printf(T("\n"));
1210 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1211 info->scan.wim_target_path);
1213 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1215 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1216 switch (info->scan.status) {
1217 case WIMLIB_SCAN_DENTRY_OK:
1218 report_scan_progress(&info->scan, false);
1220 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1221 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1223 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1224 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1225 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1227 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1228 /* Symlink fixups are enabled by default. This is
1229 * mainly intended for Windows, which for some reason
1230 * uses absolute junctions (with drive letters!) in the
1231 * default installation. On UNIX-like systems, warn the
1232 * user when fixing the target of an absolute symbolic
1233 * link, so they know to disable this if they want. */
1235 imagex_printf(T("\nWARNING: Adjusted target of "
1236 "absolute symbolic link \"%"TS"\"\n"
1237 " (Use --norpfix to capture "
1238 "absolute symbolic links as-is)\n"),
1239 info->scan.cur_path);
1246 case WIMLIB_PROGRESS_MSG_SCAN_END:
1247 report_scan_progress(&info->scan, true);
1248 imagex_printf(T("\n"));
1250 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1251 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1252 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1253 info->integrity.total_bytes);
1254 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1255 "of %"PRIu64" %"TS" (%u%%) done"),
1256 info->integrity.filename,
1257 info->integrity.completed_bytes >> unit_shift,
1259 info->integrity.total_bytes >> unit_shift,
1262 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1263 imagex_printf(T("\n"));
1265 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1266 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1267 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1268 info->integrity.total_bytes);
1269 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1270 "of %"PRIu64" %"TS" (%u%%) done"),
1271 info->integrity.completed_bytes >> unit_shift,
1273 info->integrity.total_bytes >> unit_shift,
1276 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1277 imagex_printf(T("\n"));
1279 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1280 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1281 "to %"TS" \"%"TS"\"\n"),
1282 info->extract.image,
1283 info->extract.image_name,
1284 info->extract.wimfile_name,
1285 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1286 T("NTFS volume") : T("directory")),
1287 info->extract.target);
1289 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1290 if (info->extract.end_file_count >= 2000) {
1291 percent_done = TO_PERCENT(info->extract.current_file_count,
1292 info->extract.end_file_count);
1293 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1294 info->extract.current_file_count,
1295 info->extract.end_file_count, percent_done);
1296 if (info->extract.current_file_count == info->extract.end_file_count)
1297 imagex_printf(T("\n"));
1300 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1301 percent_done = TO_PERCENT(info->extract.completed_bytes,
1302 info->extract.total_bytes);
1303 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1304 imagex_printf(T("\rExtracting file data: "
1305 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1306 info->extract.completed_bytes >> unit_shift,
1308 info->extract.total_bytes >> unit_shift,
1311 if (info->extract.completed_bytes >= info->extract.total_bytes)
1312 imagex_printf(T("\n"));
1314 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1315 if (info->extract.end_file_count >= 2000) {
1316 percent_done = TO_PERCENT(info->extract.current_file_count,
1317 info->extract.end_file_count);
1318 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1319 info->extract.current_file_count,
1320 info->extract.end_file_count, percent_done);
1321 if (info->extract.current_file_count == info->extract.end_file_count)
1322 imagex_printf(T("\n"));
1325 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1326 if (info->extract.total_parts != 1) {
1327 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1328 info->extract.part_number,
1329 info->extract.total_parts);
1332 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1333 percent_done = TO_PERCENT(info->split.completed_bytes,
1334 info->split.total_bytes);
1335 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1336 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1337 "%"PRIu64" %"TS" (%u%%) written\n"),
1338 info->split.part_name,
1339 info->split.cur_part_number,
1340 info->split.total_parts,
1341 info->split.completed_bytes >> unit_shift,
1343 info->split.total_bytes >> unit_shift,
1347 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1348 if (info->split.completed_bytes == info->split.total_bytes) {
1349 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1350 info->split.cur_part_number,
1351 info->split.total_parts);
1354 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1355 switch (info->update.command->op) {
1356 case WIMLIB_UPDATE_OP_DELETE:
1357 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1358 info->update.command->delete_.wim_path);
1360 case WIMLIB_UPDATE_OP_RENAME:
1361 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1362 info->update.command->rename.wim_source_path,
1363 info->update.command->rename.wim_target_path);
1365 case WIMLIB_UPDATE_OP_ADD:
1370 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1371 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1372 info->replace.path_in_wim);
1374 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1375 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1376 info->wimboot_exclude.path_in_wim);
1378 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1379 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1380 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1381 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1382 info->unmount.mounted_wim,
1383 info->unmount.mounted_image);
1385 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1386 info->unmount.mounted_wim,
1387 info->unmount.mounted_image);
1388 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1392 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1393 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1394 info->verify_image.current_image,
1395 info->verify_image.total_images);
1397 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1398 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1399 info->verify_streams.total_bytes);
1400 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1401 imagex_printf(T("\rVerifying file data: "
1402 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1403 info->verify_streams.completed_bytes >> unit_shift,
1405 info->verify_streams.total_bytes >> unit_shift,
1408 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1409 imagex_printf(T("\n"));
1414 imagex_flush_output();
1415 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1419 parse_num_threads(const tchar *optarg)
1422 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1423 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1424 imagex_error(T("Number of threads must be a non-negative integer!"));
1432 parse_chunk_size(const tchar *optarg)
1435 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1436 if (chunk_size == 0) {
1437 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1438 " with optional K, M, or G suffix"));
1442 if (*tmp == T('k') || *tmp == T('K')) {
1445 } else if (*tmp == T('m') || *tmp == T('M')) {
1448 } else if (*tmp == T('g') || *tmp == T('G')) {
1452 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1453 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1457 if (chunk_size >= UINT32_MAX) {
1458 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1466 * Parse an option passed to an update command.
1468 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1471 * @option: Text string for the option (beginning with --)
1473 * @cmd: `struct wimlib_update_command' that is being constructed for
1476 * Returns true if the option was recognized; false if not.
1479 update_command_add_option(int op, const tchar *option,
1480 struct wimlib_update_command *cmd)
1482 bool recognized = true;
1484 case WIMLIB_UPDATE_OP_ADD:
1485 if (!tstrcmp(option, T("--verbose")))
1486 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1487 else if (!tstrcmp(option, T("--unix-data")))
1488 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1489 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1490 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1491 else if (!tstrcmp(option, T("--strict-acls")))
1492 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1493 else if (!tstrcmp(option, T("--dereference")))
1494 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1495 else if (!tstrcmp(option, T("--no-replace")))
1496 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1500 case WIMLIB_UPDATE_OP_DELETE:
1501 if (!tstrcmp(option, T("--force")))
1502 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1503 else if (!tstrcmp(option, T("--recursive")))
1504 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1515 /* How many nonoption arguments each `imagex update' command expects */
1516 static const unsigned update_command_num_nonoptions[] = {
1517 [WIMLIB_UPDATE_OP_ADD] = 2,
1518 [WIMLIB_UPDATE_OP_DELETE] = 1,
1519 [WIMLIB_UPDATE_OP_RENAME] = 2,
1523 update_command_add_nonoption(int op, const tchar *nonoption,
1524 struct wimlib_update_command *cmd,
1525 unsigned num_nonoptions)
1528 case WIMLIB_UPDATE_OP_ADD:
1529 if (num_nonoptions == 0)
1530 cmd->add.fs_source_path = (tchar*)nonoption;
1532 cmd->add.wim_target_path = (tchar*)nonoption;
1534 case WIMLIB_UPDATE_OP_DELETE:
1535 cmd->delete_.wim_path = (tchar*)nonoption;
1537 case WIMLIB_UPDATE_OP_RENAME:
1538 if (num_nonoptions == 0)
1539 cmd->rename.wim_source_path = (tchar*)nonoption;
1541 cmd->rename.wim_target_path = (tchar*)nonoption;
1547 * Parse a command passed on stdin to `imagex update'.
1549 * @line: Text of the command.
1550 * @len: Length of the line, including a null terminator
1553 * @command: A `struct wimlib_update_command' to fill in from the parsed
1556 * @line_number: Line number of the command, for diagnostics.
1558 * Returns true on success; returns false on parse error.
1561 parse_update_command(tchar *line, size_t len,
1562 struct wimlib_update_command *command,
1566 tchar *command_name;
1568 size_t num_nonoptions;
1570 /* Get the command name ("add", "delete", "rename") */
1571 ret = parse_string(&line, &len, &command_name);
1572 if (ret != PARSE_STRING_SUCCESS)
1575 if (!tstrcasecmp(command_name, T("add"))) {
1576 op = WIMLIB_UPDATE_OP_ADD;
1577 } else if (!tstrcasecmp(command_name, T("delete"))) {
1578 op = WIMLIB_UPDATE_OP_DELETE;
1579 } else if (!tstrcasecmp(command_name, T("rename"))) {
1580 op = WIMLIB_UPDATE_OP_RENAME;
1582 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1583 command_name, line_number);
1588 /* Parse additional options and non-options as needed */
1593 ret = parse_string(&line, &len, &next_string);
1594 if (ret == PARSE_STRING_NONE) /* End of line */
1596 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1598 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1600 if (!update_command_add_option(op, next_string, command))
1602 imagex_error(T("Unrecognized option \"%"TS"\" to "
1603 "update command \"%"TS"\" on line %zu"),
1604 next_string, command_name, line_number);
1610 if (num_nonoptions == update_command_num_nonoptions[op])
1612 imagex_error(T("Unexpected argument \"%"TS"\" in "
1613 "update command on line %zu\n"
1614 " (The \"%"TS"\" command only "
1615 "takes %zu nonoption arguments!)\n"),
1616 next_string, line_number,
1617 command_name, num_nonoptions);
1620 update_command_add_nonoption(op, next_string,
1621 command, num_nonoptions);
1626 if (num_nonoptions != update_command_num_nonoptions[op]) {
1627 imagex_error(T("Not enough arguments to update command "
1628 "\"%"TS"\" on line %zu"), command_name, line_number);
1634 static struct wimlib_update_command *
1635 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1636 size_t *num_cmds_ret)
1640 struct wimlib_update_command *cmds;
1643 nlines = text_file_count_lines(cmd_file_contents_p,
1648 /* Always allocate at least 1 slot, just in case the implementation of
1649 * calloc() returns NULL if 0 bytes are requested. */
1650 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1652 imagex_error(T("out of memory"));
1655 p = *cmd_file_contents_p;
1657 for (i = 0; i < nlines; i++) {
1658 /* XXX: Could use rawmemchr() here instead, but it may not be
1659 * available on all platforms. */
1660 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1661 size_t len = endp - p + 1;
1663 if (!is_comment_line(p, len)) {
1664 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1675 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1676 * one image from a WIM file to an NTFS volume. */
1678 imagex_apply(int argc, tchar **argv, int cmd)
1682 int image = WIMLIB_NO_IMAGE;
1684 struct wimlib_wim_info info;
1686 const tchar *wimfile;
1687 const tchar *target;
1688 const tchar *image_num_or_name = NULL;
1689 int extract_flags = 0;
1691 STRING_LIST(refglobs);
1693 for_opt(c, apply_options) {
1695 case IMAGEX_CHECK_OPTION:
1696 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1698 case IMAGEX_VERBOSE_OPTION:
1699 /* No longer does anything. */
1701 case IMAGEX_REF_OPTION:
1702 ret = string_list_append(&refglobs, optarg);
1704 goto out_free_refglobs;
1706 case IMAGEX_UNIX_DATA_OPTION:
1707 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1709 case IMAGEX_NO_ACLS_OPTION:
1710 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1712 case IMAGEX_STRICT_ACLS_OPTION:
1713 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1715 case IMAGEX_NO_ATTRIBUTES_OPTION:
1716 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1718 case IMAGEX_NORPFIX_OPTION:
1719 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1721 case IMAGEX_RPFIX_OPTION:
1722 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1724 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1725 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1726 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1728 case IMAGEX_WIMBOOT_OPTION:
1729 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1731 case IMAGEX_COMPACT_OPTION:
1732 ret = set_compact_mode(optarg, &extract_flags);
1734 goto out_free_refglobs;
1742 if (argc != 2 && argc != 3)
1747 if (!tstrcmp(wimfile, T("-"))) {
1748 /* Attempt to apply pipable WIM from standard input. */
1750 image_num_or_name = NULL;
1753 image_num_or_name = argv[1];
1758 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1759 imagex_progress_func, NULL);
1761 goto out_free_refglobs;
1763 wimlib_get_wim_info(wim, &info);
1766 /* Image explicitly specified. */
1767 image_num_or_name = argv[1];
1768 image = wimlib_resolve_image(wim, image_num_or_name);
1769 ret = verify_image_exists(image, image_num_or_name, wimfile);
1771 goto out_wimlib_free;
1774 /* No image specified; default to image 1, but only if the WIM
1775 * contains exactly one image. */
1777 if (info.image_count != 1) {
1778 imagex_error(T("\"%"TS"\" contains %d images; "
1779 "Please select one (or all)."),
1780 wimfile, info.image_count);
1789 if (refglobs.num_strings) {
1791 imagex_error(T("Can't specify --ref when applying from stdin!"));
1793 goto out_wimlib_free;
1795 ret = wim_reference_globs(wim, &refglobs, open_flags);
1797 goto out_wimlib_free;
1802 /* Interpret a regular file or block device target as an NTFS
1806 if (tstat(target, &stbuf)) {
1807 if (errno != ENOENT) {
1808 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1811 goto out_wimlib_free;
1814 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1815 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1821 ret = wimlib_extract_image(wim, image, target, extract_flags);
1823 set_fd_to_binary_mode(STDIN_FILENO);
1824 ret = wimlib_extract_image_from_pipe_with_progress(
1829 imagex_progress_func,
1833 imagex_printf(T("Done applying WIM image.\n"));
1834 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1836 do_resource_not_found_warning(wimfile, &info, &refglobs);
1838 imagex_error(T( "If you are applying an image "
1839 "from a split pipable WIM,\n"
1840 " make sure you have "
1841 "concatenated together all parts."));
1843 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1844 do_metadata_not_found_warning(wimfile, &info);
1849 string_list_destroy(&refglobs);
1853 usage(CMD_APPLY, stderr);
1855 goto out_free_refglobs;
1859 * Create a WIM image from a directory tree, NTFS volume, or multiple files or
1860 * directory trees. 'wimcapture': create a new WIM file containing the desired
1861 * image. 'wimappend': add a new image to an existing WIM file; or, with
1862 * '--create' behave like 'wimcapture' if the WIM file doesn't exist.
1865 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1868 bool create = false;
1869 bool appending = (cmd == CMD_APPEND);
1871 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1872 WIMLIB_ADD_FLAG_WINCONFIG |
1873 WIMLIB_ADD_FLAG_VERBOSE |
1874 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1875 int write_flags = 0;
1876 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1877 uint32_t chunk_size = UINT32_MAX;
1878 uint32_t solid_chunk_size = UINT32_MAX;
1879 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1880 const tchar *wimfile;
1883 STRING_LIST(image_properties);
1886 STRING_LIST(base_wimfiles);
1887 WIMStruct **base_wims;
1889 WIMStruct *template_wim = NULL;
1890 const tchar *template_wimfile = NULL;
1891 const tchar *template_image_name_or_num = NULL;
1892 int template_image = WIMLIB_NO_IMAGE;
1895 unsigned num_threads = 0;
1900 tchar *config_file = NULL;
1902 bool source_list = false;
1903 size_t source_list_nchars = 0;
1904 tchar *source_list_contents;
1905 bool capture_sources_malloced;
1906 struct wimlib_capture_source *capture_sources;
1908 bool name_defaulted;
1910 for_opt(c, capture_or_append_options) {
1912 case IMAGEX_BOOT_OPTION:
1913 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1915 case IMAGEX_CHECK_OPTION:
1916 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1918 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
1919 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1921 case IMAGEX_NOCHECK_OPTION:
1922 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1924 case IMAGEX_CONFIG_OPTION:
1925 config_file = optarg;
1926 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1928 case IMAGEX_COMPRESS_OPTION:
1929 compression_type = get_compression_type(optarg, false);
1930 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1933 case IMAGEX_CHUNK_SIZE_OPTION:
1934 chunk_size = parse_chunk_size(optarg);
1935 if (chunk_size == UINT32_MAX)
1938 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1939 solid_chunk_size = parse_chunk_size(optarg);
1940 if (solid_chunk_size == UINT32_MAX)
1943 case IMAGEX_SOLID_COMPRESS_OPTION:
1944 solid_ctype = get_compression_type(optarg, true);
1945 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1948 case IMAGEX_SOLID_OPTION:
1949 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1951 case IMAGEX_NO_SOLID_SORT_OPTION:
1952 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1954 case IMAGEX_FLAGS_OPTION: {
1955 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1956 tsprintf(p, T("FLAGS=%"TS), optarg);
1957 ret = string_list_append(&image_properties, p);
1962 case IMAGEX_IMAGE_PROPERTY_OPTION:
1963 ret = append_image_property_argument(&image_properties);
1967 case IMAGEX_DEREFERENCE_OPTION:
1968 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1970 case IMAGEX_VERBOSE_OPTION:
1971 /* No longer does anything. */
1973 case IMAGEX_THREADS_OPTION:
1974 num_threads = parse_num_threads(optarg);
1975 if (num_threads == UINT_MAX)
1978 case IMAGEX_REBUILD_OPTION:
1979 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1981 case IMAGEX_UNIX_DATA_OPTION:
1982 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1984 case IMAGEX_SOURCE_LIST_OPTION:
1987 case IMAGEX_NO_ACLS_OPTION:
1988 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1990 case IMAGEX_STRICT_ACLS_OPTION:
1991 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1993 case IMAGEX_RPFIX_OPTION:
1994 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1996 case IMAGEX_NORPFIX_OPTION:
1997 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1999 case IMAGEX_PIPABLE_OPTION:
2000 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2002 case IMAGEX_NOT_PIPABLE_OPTION:
2003 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2005 case IMAGEX_UPDATE_OF_OPTION:
2006 if (template_image_name_or_num) {
2007 imagex_error(T("'--update-of' can only be "
2008 "specified one time!"));
2012 colon = tstrrchr(optarg, T(':'));
2015 template_wimfile = optarg;
2017 template_image_name_or_num = colon + 1;
2019 template_wimfile = NULL;
2020 template_image_name_or_num = optarg;
2024 case IMAGEX_DELTA_FROM_OPTION:
2025 ret = string_list_append(&base_wimfiles, optarg);
2028 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2030 case IMAGEX_WIMBOOT_OPTION:
2031 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2033 case IMAGEX_UNSAFE_COMPACT_OPTION:
2034 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2036 case IMAGEX_SNAPSHOT_OPTION:
2037 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2039 case IMAGEX_CREATE_OPTION:
2040 if (cmd == CMD_CAPTURE) {
2041 imagex_error(T("'--create' is only valid for 'wimappend', not 'wimcapture'"));
2053 if (argc < 2 || argc > 4)
2059 /* Set default compression type and parameters. */
2062 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2063 /* No compression type specified. Use the default. */
2065 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2066 /* With --wimboot, default to XPRESS compression. */
2067 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2068 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2069 /* With --solid, default to LZMS compression. (However,
2070 * this will not affect solid resources!) */
2071 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2073 /* Otherwise, default to LZX compression. */
2074 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2078 if (!tstrcmp(wimfile, T("-"))) {
2079 /* Writing captured WIM to standard output. */
2083 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2084 imagex_error("Can't write a non-pipable WIM to "
2085 "standard output! Specify --pipable\n"
2086 " if you want to create a pipable WIM "
2087 "(but read the docs first).");
2091 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2094 imagex_error(T("Using standard output for append does "
2095 "not make sense."));
2098 wim_fd = STDOUT_FILENO;
2100 imagex_output_to_stderr();
2101 set_fd_to_binary_mode(wim_fd);
2105 if (create && tstat(wimfile, &stbuf) != 0 && errno == ENOENT)
2109 if ((write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) && !appending) {
2110 imagex_error(T("'--unsafe-compact' is only valid for append!"));
2114 /* If template image was specified using --update-of=IMAGE rather
2115 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2116 if (template_image_name_or_num && !template_wimfile) {
2117 if (base_wimfiles.num_strings == 1) {
2118 /* Capturing delta WIM based on single WIM: default to
2120 template_wimfile = base_wimfiles.strings[0];
2121 } else if (appending) {
2122 /* Appending to WIM: default to WIM being appended to.
2124 template_wimfile = wimfile;
2126 /* Capturing a normal (non-delta) WIM, so the WIM file
2127 * *must* be explicitly specified. */
2128 if (base_wimfiles.num_strings > 1) {
2129 imagex_error(T("For capture of delta WIM "
2130 "based on multiple existing "
2132 " '--update-of' must "
2133 "specify WIMFILE:IMAGE!"));
2135 imagex_error(T("For capture of non-delta WIM, "
2136 "'--update-of' must specify "
2145 name_defaulted = false;
2147 /* Set default name to SOURCE argument, omitting any directory
2148 * prefixes and trailing slashes. This requires making a copy
2149 * of @source. Leave some free characters at the end in case we
2150 * append a number to keep the name unique. */
2151 size_t source_name_len;
2153 source_name_len = tstrlen(source);
2154 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2155 name = tbasename(tstrcpy(source_copy, source));
2156 name_defaulted = true;
2159 /* Image description (if given). */
2161 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2162 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2163 ret = string_list_append(&image_properties, p);
2169 /* Set up capture sources in source list mode */
2170 if (source[0] == T('-') && source[1] == T('\0')) {
2171 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2173 source_list_contents = file_get_text_contents(source,
2174 &source_list_nchars);
2176 if (!source_list_contents)
2179 capture_sources = parse_source_list(&source_list_contents,
2182 if (!capture_sources) {
2184 goto out_free_source_list_contents;
2186 capture_sources_malloced = true;
2188 /* Set up capture source in non-source-list mode. */
2189 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2190 capture_sources[0].fs_source_path = source;
2191 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2192 capture_sources[0].reserved = 0;
2194 capture_sources_malloced = false;
2195 source_list_contents = NULL;
2198 /* Open the existing WIM, or create a new one. */
2200 ret = wimlib_open_wim_with_progress(wimfile,
2201 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2203 imagex_progress_func,
2206 goto out_free_capture_sources;
2208 ret = wimlib_create_new_wim(compression_type, &wim);
2210 goto out_free_capture_sources;
2211 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2214 /* Set chunk size if non-default. */
2215 if (chunk_size != UINT32_MAX) {
2216 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2219 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2221 int ctype = compression_type;
2224 struct wimlib_wim_info info;
2225 wimlib_get_wim_info(wim, &info);
2226 ctype = info.compression_type;
2229 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2230 ret = wimlib_set_output_chunk_size(wim, 4096);
2235 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2236 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2240 if (solid_chunk_size != UINT32_MAX) {
2241 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2247 /* Detect if source is regular file or block device and set NTFS volume
2252 if (tstat(source, &stbuf) == 0) {
2253 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2254 imagex_printf(T("Capturing WIM image from NTFS "
2255 "filesystem on \"%"TS"\"\n"), source);
2256 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2259 if (errno != ENOENT) {
2260 imagex_error_with_errno(T("Failed to stat "
2261 "\"%"TS"\""), source);
2269 /* If the user did not specify an image name, and the basename of the
2270 * source already exists as an image name in the WIM file, append a
2271 * suffix to make it unique. */
2272 if (appending && name_defaulted) {
2273 unsigned long conflict_idx;
2274 tchar *name_end = tstrchr(name, T('\0'));
2275 for (conflict_idx = 1;
2276 wimlib_image_name_in_use(wim, name);
2279 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2283 /* If capturing a delta WIM, reference resources from the base WIMs
2284 * before adding the new image. */
2285 if (base_wimfiles.num_strings) {
2286 base_wims = calloc(base_wimfiles.num_strings,
2287 sizeof(base_wims[0]));
2288 if (base_wims == NULL) {
2289 imagex_error(T("Out of memory!"));
2294 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2295 ret = wimlib_open_wim_with_progress(
2296 base_wimfiles.strings[i], open_flags,
2297 &base_wims[i], imagex_progress_func, NULL);
2299 goto out_free_base_wims;
2303 ret = wimlib_reference_resources(wim, base_wims,
2304 base_wimfiles.num_strings, 0);
2306 goto out_free_base_wims;
2308 if (base_wimfiles.num_strings == 1) {
2309 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2310 base_wimfiles.strings[0]);
2312 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2313 base_wimfiles.num_strings);
2320 /* If capturing or appending as an update of an existing (template) image,
2321 * open the WIM if needed and parse the image index. */
2322 if (template_image_name_or_num) {
2324 if (appending && !tstrcmp(template_wimfile, wimfile)) {
2327 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2328 if (!tstrcmp(template_wimfile,
2329 base_wimfiles.strings[i])) {
2330 template_wim = base_wims[i];
2336 if (!template_wim) {
2337 ret = wimlib_open_wim_with_progress(template_wimfile,
2340 imagex_progress_func,
2343 goto out_free_base_wims;
2346 template_image = wimlib_resolve_image(template_wim,
2347 template_image_name_or_num);
2349 if (template_image_name_or_num[0] == T('-')) {
2352 struct wimlib_wim_info info;
2354 wimlib_get_wim_info(template_wim, &info);
2355 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2356 if (n >= 1 && n <= info.image_count &&
2358 tmp != template_image_name_or_num + 1)
2360 template_image = info.image_count - (n - 1);
2363 ret = verify_image_exists_and_is_single(template_image,
2364 template_image_name_or_num,
2367 goto out_free_template_wim;
2370 ret = wimlib_add_image_multisource(wim,
2377 goto out_free_template_wim;
2379 if (image_properties.num_strings || template_image_name_or_num) {
2380 /* User asked to set additional image properties, or an image on
2381 * which the added one is to be based has been specified with
2383 struct wimlib_wim_info info;
2385 wimlib_get_wim_info(wim, &info);
2387 ret = apply_image_properties(&image_properties, wim,
2388 info.image_count, NULL);
2390 goto out_free_template_wim;
2392 /* Reference template image if the user provided one. */
2393 if (template_image_name_or_num) {
2394 imagex_printf(T("Using image %d "
2395 "from \"%"TS"\" as template\n"),
2396 template_image, template_wimfile);
2397 ret = wimlib_reference_template_image(wim,
2403 goto out_free_template_wim;
2407 /* Write the new WIM or overwrite the existing WIM with the new image
2410 ret = wimlib_overwrite(wim, write_flags, num_threads);
2411 } else if (wimfile) {
2412 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2413 write_flags, num_threads);
2415 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2416 write_flags, num_threads);
2418 out_free_template_wim:
2419 /* 'template_wim' may alias 'wim' or any of the 'base_wims' */
2420 if (template_wim == wim)
2421 goto out_free_base_wims;
2422 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2423 if (template_wim == base_wims[i])
2424 goto out_free_base_wims;
2425 wimlib_free(template_wim);
2427 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2428 wimlib_free(base_wims[i]);
2432 out_free_capture_sources:
2433 if (capture_sources_malloced)
2434 free(capture_sources);
2435 out_free_source_list_contents:
2436 free(source_list_contents);
2438 string_list_destroy(&image_properties);
2439 string_list_destroy(&base_wimfiles);
2449 /* Remove image(s) from a WIM. */
2451 imagex_delete(int argc, tchar **argv, int cmd)
2454 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2455 int write_flags = 0;
2456 const tchar *wimfile;
2457 const tchar *image_num_or_name;
2462 for_opt(c, delete_options) {
2464 case IMAGEX_CHECK_OPTION:
2465 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2467 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2468 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2470 case IMAGEX_SOFT_OPTION:
2471 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2473 case IMAGEX_UNSAFE_COMPACT_OPTION:
2474 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2485 imagex_error(T("Must specify a WIM file"));
2487 imagex_error(T("Must specify an image"));
2491 image_num_or_name = argv[1];
2493 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2494 imagex_progress_func, NULL);
2498 image = wimlib_resolve_image(wim, image_num_or_name);
2500 ret = verify_image_exists(image, image_num_or_name, wimfile);
2502 goto out_wimlib_free;
2504 ret = wimlib_delete_image(wim, image);
2506 imagex_error(T("Failed to delete image from \"%"TS"\""),
2508 goto out_wimlib_free;
2511 ret = wimlib_overwrite(wim, write_flags, 0);
2513 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2514 "deleted"), wimfile);
2522 usage(CMD_DELETE, stderr);
2527 struct print_dentry_options {
2532 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2534 tprintf(T("%"TS"\n"), dentry->full_path);
2537 static const struct {
2540 } file_attr_flags[] = {
2541 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2542 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2543 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2544 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2545 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2546 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2547 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2548 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2549 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2550 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2551 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2552 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2553 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2554 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2555 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2558 #define TIMESTR_MAX 100
2561 print_time(const tchar *type, const struct wimlib_timespec *wts,
2564 tchar timestr[TIMESTR_MAX];
2568 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2569 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2574 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2575 timestr[TIMESTR_MAX - 1] = '\0';
2577 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2580 static void print_byte_field(const uint8_t field[], size_t len)
2583 tprintf(T("%02hhx"), *field++);
2587 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2589 tchar attr_string[256];
2592 tputs(T("WIM Information:"));
2593 tputs(T("----------------"));
2594 tprintf(T("Path: %"TS"\n"), wimfile);
2595 tprintf(T("GUID: 0x"));
2596 print_byte_field(info->guid, sizeof(info->guid));
2598 tprintf(T("Version: %u\n"), info->wim_version);
2599 tprintf(T("Image Count: %d\n"), info->image_count);
2600 tprintf(T("Compression: %"TS"\n"),
2601 wimlib_get_compression_type_string(info->compression_type));
2602 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2604 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2605 tprintf(T("Boot Index: %d\n"), info->boot_index);
2606 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2608 attr_string[0] = T('\0');
2611 tstrcat(attr_string, T("Pipable, "));
2613 if (info->has_integrity_table)
2614 tstrcat(attr_string, T("Integrity info, "));
2616 if (info->has_rpfix)
2617 tstrcat(attr_string, T("Relative path junction, "));
2619 if (info->resource_only)
2620 tstrcat(attr_string, T("Resource only, "));
2622 if (info->metadata_only)
2623 tstrcat(attr_string, T("Metadata only, "));
2625 if (info->is_marked_readonly)
2626 tstrcat(attr_string, T("Readonly, "));
2628 p = tstrchr(attr_string, T('\0'));
2629 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2632 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2636 print_resource(const struct wimlib_resource_entry *resource,
2639 tprintf(T("Hash = 0x"));
2640 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2643 if (!resource->is_missing) {
2644 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2645 resource->uncompressed_size);
2646 if (resource->packed) {
2647 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2648 "bytes @ offset %"PRIu64"\n"),
2649 resource->raw_resource_uncompressed_size,
2650 resource->raw_resource_compressed_size,
2651 resource->raw_resource_offset_in_wim);
2653 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2656 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2657 resource->compressed_size);
2659 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2663 tprintf(T("Part Number = %u\n"), resource->part_number);
2664 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2666 tprintf(T("Flags = "));
2667 if (resource->is_compressed)
2668 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2669 if (resource->is_metadata)
2670 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2671 if (resource->is_free)
2672 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2673 if (resource->is_spanned)
2674 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2675 if (resource->packed)
2676 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2684 print_blobs(WIMStruct *wim)
2686 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2691 default_print_security_descriptor(const uint8_t *sd, size_t size)
2693 tprintf(T("Security Descriptor = "));
2694 print_byte_field(sd, size);
2700 is_null_guid(const uint8_t *guid)
2702 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2704 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2708 print_guid(const tchar *label, const uint8_t *guid)
2710 if (is_null_guid(guid))
2712 tprintf(T("%-20"TS"= 0x"), label);
2713 print_byte_field(guid, WIMLIB_GUID_LEN);
2718 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2721 "----------------------------------------------------------------------------\n"));
2722 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2723 if (dentry->dos_name)
2724 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2725 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2726 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2727 if (file_attr_flags[i].flag & dentry->attributes)
2728 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2729 file_attr_flags[i].name);
2731 if (dentry->security_descriptor) {
2732 print_security_descriptor(dentry->security_descriptor,
2733 dentry->security_descriptor_size);
2736 print_time(T("Creation Time"),
2737 &dentry->creation_time, dentry->creation_time_high);
2738 print_time(T("Last Write Time"),
2739 &dentry->last_write_time, dentry->last_write_time_high);
2740 print_time(T("Last Access Time"),
2741 &dentry->last_access_time, dentry->last_access_time_high);
2744 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2745 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2747 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2748 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2750 if (dentry->unix_mode != 0) {
2751 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2752 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2753 dentry->unix_uid, dentry->unix_gid,
2754 dentry->unix_mode, dentry->unix_rdev);
2757 if (!is_null_guid(dentry->object_id.object_id)) {
2758 print_guid(T("Object ID"), dentry->object_id.object_id);
2759 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2760 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2761 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2764 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2765 if (dentry->streams[i].stream_name) {
2766 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2767 dentry->streams[i].stream_name);
2768 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2769 tprintf(T("\tRaw encrypted data stream:\n"));
2770 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2771 tprintf(T("\tReparse point stream:\n"));
2773 tprintf(T("\tUnnamed data stream:\n"));
2775 print_resource(&dentry->streams[i].resource, NULL);
2780 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2782 const struct print_dentry_options *options = _options;
2783 if (!options->detailed)
2784 print_dentry_full_path(dentry);
2786 print_dentry_detailed(dentry);
2790 /* Print the files contained in an image(s) in a WIM file. */
2792 imagex_dir(int argc, tchar **argv, int cmd)
2794 const tchar *wimfile;
2795 WIMStruct *wim = NULL;
2798 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2800 struct print_dentry_options options = {
2803 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2805 STRING_LIST(refglobs);
2807 for_opt(c, dir_options) {
2809 case IMAGEX_PATH_OPTION:
2812 case IMAGEX_DETAILED_OPTION:
2813 options.detailed = true;
2815 case IMAGEX_ONE_FILE_ONLY_OPTION:
2816 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2818 case IMAGEX_REF_OPTION:
2819 ret = string_list_append(&refglobs, optarg);
2821 goto out_free_refglobs;
2831 imagex_error(T("Must specify a WIM file"));
2835 imagex_error(T("Too many arguments"));
2840 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2841 imagex_progress_func, NULL);
2843 goto out_free_refglobs;
2846 image = wimlib_resolve_image(wim, argv[1]);
2847 ret = verify_image_exists(image, argv[1], wimfile);
2849 goto out_wimlib_free;
2851 /* No image specified; default to image 1, but only if the WIM
2852 * contains exactly one image. */
2854 struct wimlib_wim_info info;
2856 wimlib_get_wim_info(wim, &info);
2857 if (info.image_count != 1) {
2858 imagex_error(T("\"%"TS"\" contains %d images; Please "
2859 "select one (or all)."),
2860 wimfile, info.image_count);
2867 if (refglobs.num_strings) {
2868 ret = wim_reference_globs(wim, &refglobs, 0);
2870 goto out_wimlib_free;
2873 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2874 print_dentry, &options);
2875 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2876 struct wimlib_wim_info info;
2878 wimlib_get_wim_info(wim, &info);
2879 do_metadata_not_found_warning(wimfile, &info);
2884 string_list_destroy(&refglobs);
2888 usage(CMD_DIR, stderr);
2890 goto out_free_refglobs;
2893 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2896 imagex_export(int argc, tchar **argv, int cmd)
2900 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2901 int write_flags = 0;
2902 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2903 const tchar *src_wimfile;
2904 const tchar *src_image_num_or_name;
2905 const tchar *dest_wimfile;
2907 const tchar *dest_name;
2908 const tchar *dest_desc;
2910 struct wimlib_wim_info src_info;
2911 WIMStruct *dest_wim;
2916 STRING_LIST(refglobs);
2917 unsigned num_threads = 0;
2918 uint32_t chunk_size = UINT32_MAX;
2919 uint32_t solid_chunk_size = UINT32_MAX;
2920 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2922 for_opt(c, export_options) {
2924 case IMAGEX_BOOT_OPTION:
2925 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2927 case IMAGEX_CHECK_OPTION:
2928 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2930 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2931 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2933 case IMAGEX_NOCHECK_OPTION:
2934 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2936 case IMAGEX_COMPRESS_OPTION:
2937 compression_type = get_compression_type(optarg, false);
2938 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2941 case IMAGEX_RECOMPRESS_OPTION:
2942 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2944 case IMAGEX_SOLID_OPTION:
2945 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2947 case IMAGEX_NO_SOLID_SORT_OPTION:
2948 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2950 case IMAGEX_CHUNK_SIZE_OPTION:
2951 chunk_size = parse_chunk_size(optarg);
2952 if (chunk_size == UINT32_MAX)
2955 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2956 solid_chunk_size = parse_chunk_size(optarg);
2957 if (solid_chunk_size == UINT32_MAX)
2960 case IMAGEX_SOLID_COMPRESS_OPTION:
2961 solid_ctype = get_compression_type(optarg, true);
2962 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2965 case IMAGEX_REF_OPTION:
2966 ret = string_list_append(&refglobs, optarg);
2968 goto out_free_refglobs;
2970 case IMAGEX_THREADS_OPTION:
2971 num_threads = parse_num_threads(optarg);
2972 if (num_threads == UINT_MAX)
2975 case IMAGEX_REBUILD_OPTION:
2976 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2978 case IMAGEX_PIPABLE_OPTION:
2979 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2981 case IMAGEX_NOT_PIPABLE_OPTION:
2982 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2984 case IMAGEX_WIMBOOT_OPTION:
2985 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2987 case IMAGEX_UNSAFE_COMPACT_OPTION:
2988 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2996 if (argc < 3 || argc > 5)
2998 src_wimfile = argv[0];
2999 src_image_num_or_name = argv[1];
3000 dest_wimfile = argv[2];
3001 dest_name = (argc >= 4) ? argv[3] : NULL;
3002 dest_desc = (argc >= 5) ? argv[4] : NULL;
3003 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
3004 imagex_progress_func, NULL);
3006 goto out_free_refglobs;
3008 wimlib_get_wim_info(src_wim, &src_info);
3010 /* Determine if the destination is an existing file or not. If so, we
3011 * try to append the exported image(s) to it; otherwise, we create a new
3012 * WIM containing the exported image(s). Furthermore, determine if we
3013 * need to write a pipable WIM directly to standard output. */
3015 if (tstrcmp(dest_wimfile, T("-")) == 0) {
3017 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
3018 imagex_error("Can't write a non-pipable WIM to "
3019 "standard output! Specify --pipable\n"
3020 " if you want to create a pipable WIM "
3021 "(but read the docs first).");
3023 goto out_free_src_wim;
3026 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3028 dest_wimfile = NULL;
3029 dest_wim_fd = STDOUT_FILENO;
3030 imagex_output_to_stderr();
3031 set_fd_to_binary_mode(dest_wim_fd);
3034 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
3036 /* Destination file exists. */
3038 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
3039 imagex_error(T("\"%"TS"\" is not a regular file "
3040 "or block device"), dest_wimfile);
3042 goto out_free_src_wim;
3044 ret = wimlib_open_wim_with_progress(dest_wimfile,
3046 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3048 imagex_progress_func,
3051 goto out_free_src_wim;
3053 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3054 /* The user specified a compression type, but we're
3055 * exporting to an existing WIM. Make sure the
3056 * specified compression type is the same as the
3057 * compression type of the existing destination WIM. */
3058 struct wimlib_wim_info dest_info;
3060 wimlib_get_wim_info(dest_wim, &dest_info);
3061 if (compression_type != dest_info.compression_type) {
3062 imagex_error(T("Cannot specify a compression type that is "
3063 "not the same as that used in the "
3064 "destination WIM"));
3066 goto out_free_dest_wim;
3072 if (errno != ENOENT) {
3073 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3076 goto out_free_src_wim;
3079 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3080 imagex_error(T("'--unsafe-compact' is only valid when "
3081 "exporting to an existing WIM file!"));
3083 goto out_free_src_wim;
3086 /* dest_wimfile is not an existing file, so create a new WIM. */
3088 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3089 /* The user did not specify a compression type; default
3090 * to that of the source WIM, unless --solid or
3091 * --wimboot was specified. */
3093 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3094 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3095 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3096 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3098 compression_type = src_info.compression_type;
3100 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3102 goto out_free_src_wim;
3104 wimlib_register_progress_function(dest_wim,
3105 imagex_progress_func, NULL);
3107 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3108 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3110 /* For --wimboot export, use small XPRESS chunks. */
3111 wimlib_set_output_chunk_size(dest_wim, 4096);
3112 } else if (compression_type == src_info.compression_type &&
3113 chunk_size == UINT32_MAX)
3115 /* Use same chunk size if compression type is the same. */
3116 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3120 if (chunk_size != UINT32_MAX) {
3121 /* Set destination chunk size. */
3122 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3124 goto out_free_dest_wim;
3126 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3127 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3129 goto out_free_dest_wim;
3131 if (solid_chunk_size != UINT32_MAX) {
3132 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3134 goto out_free_dest_wim;
3137 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3138 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3140 goto out_free_dest_wim;
3142 if (refglobs.num_strings) {
3143 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3145 goto out_free_dest_wim;
3148 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3149 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3151 imagex_error(T("--boot specified for all-images export, but source WIM "
3152 "has no bootable image."));
3154 goto out_free_dest_wim;
3157 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3158 dest_desc, export_flags);
3160 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3161 do_resource_not_found_warning(src_wimfile,
3162 &src_info, &refglobs);
3163 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3164 do_metadata_not_found_warning(src_wimfile, &src_info);
3166 goto out_free_dest_wim;
3170 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3171 else if (dest_wimfile)
3172 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3173 write_flags, num_threads);
3175 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3176 WIMLIB_ALL_IMAGES, write_flags,
3179 wimlib_free(dest_wim);
3181 wimlib_free(src_wim);
3183 string_list_destroy(&refglobs);
3187 usage(CMD_EXPORT, stderr);
3190 goto out_free_refglobs;
3193 /* Extract files or directories from a WIM image */
3195 imagex_extract(int argc, tchar **argv, int cmd)
3202 const tchar *wimfile;
3203 const tchar *image_num_or_name;
3204 tchar *dest_dir = T(".");
3205 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3206 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3207 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3208 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3210 STRING_LIST(refglobs);
3212 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3214 for_opt(c, extract_options) {
3216 case IMAGEX_CHECK_OPTION:
3217 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3219 case IMAGEX_VERBOSE_OPTION:
3220 /* No longer does anything. */
3222 case IMAGEX_REF_OPTION:
3223 ret = string_list_append(&refglobs, optarg);
3225 goto out_free_refglobs;
3227 case IMAGEX_UNIX_DATA_OPTION:
3228 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3230 case IMAGEX_NO_ACLS_OPTION:
3231 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3233 case IMAGEX_STRICT_ACLS_OPTION:
3234 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3236 case IMAGEX_NO_ATTRIBUTES_OPTION:
3237 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3239 case IMAGEX_DEST_DIR_OPTION:
3242 case IMAGEX_TO_STDOUT_OPTION:
3243 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3244 imagex_suppress_output();
3245 set_fd_to_binary_mode(STDOUT_FILENO);
3247 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3248 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3249 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3251 case IMAGEX_NO_GLOBS_OPTION:
3252 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3254 case IMAGEX_NULLGLOB_OPTION:
3255 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3257 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3258 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3260 case IMAGEX_WIMBOOT_OPTION:
3261 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3263 case IMAGEX_COMPACT_OPTION:
3264 ret = set_compact_mode(optarg, &extract_flags);
3266 goto out_free_refglobs;
3278 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3279 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3281 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3286 image_num_or_name = argv[1];
3291 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3292 imagex_progress_func, NULL);
3294 goto out_free_refglobs;
3296 image = wimlib_resolve_image(wim, image_num_or_name);
3297 ret = verify_image_exists_and_is_single(image,
3301 goto out_wimlib_free;
3303 if (refglobs.num_strings) {
3304 ret = wim_reference_globs(wim, &refglobs, open_flags);
3306 goto out_wimlib_free;
3312 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3315 while (argc != 0 && ret == 0) {
3319 num_paths < argc && argv[num_paths][0] != T('@');
3324 ret = wimlib_extract_paths(wim, image, dest_dir,
3325 (const tchar **)argv,
3327 extract_flags | notlist_extract_flags);
3331 const tchar *listfile = argv[0] + 1;
3333 if (!tstrcmp(listfile, T("-"))) {
3334 tputs(T("Reading pathlist file from standard input..."));
3338 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3339 listfile, extract_flags);
3346 imagex_printf(T("Done extracting files.\n"));
3347 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3348 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3349 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3350 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3351 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3354 T("Note: You can use the '--nullglob' "
3355 "option to ignore missing files.\n"));
3357 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3358 "files and directories\n"
3359 " are in the WIM image.\n"),
3360 get_cmd_string(CMD_DIR, false));
3361 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3362 struct wimlib_wim_info info;
3364 wimlib_get_wim_info(wim, &info);
3365 do_resource_not_found_warning(wimfile, &info, &refglobs);
3366 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3367 struct wimlib_wim_info info;
3369 wimlib_get_wim_info(wim, &info);
3370 do_metadata_not_found_warning(wimfile, &info);
3375 string_list_destroy(&refglobs);
3379 usage(CMD_EXTRACT, stderr);
3382 goto out_free_refglobs;
3385 /* Prints information about a WIM file; also can mark an image as bootable,
3386 * change the name of an image, or change the description of an image. */
3388 imagex_info(int argc, tchar **argv, int cmd)
3392 bool header = false;
3395 bool short_header = true;
3396 const tchar *xml_out_file = NULL;
3397 const tchar *wimfile;
3398 const tchar *image_num_or_name;
3399 STRING_LIST(image_properties);
3404 int write_flags = 0;
3405 struct wimlib_wim_info info;
3407 for_opt(c, info_options) {
3409 case IMAGEX_BOOT_OPTION:
3412 case IMAGEX_CHECK_OPTION:
3413 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3415 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3416 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3418 case IMAGEX_NOCHECK_OPTION:
3419 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3421 case IMAGEX_HEADER_OPTION:
3423 short_header = false;
3425 case IMAGEX_BLOBS_OPTION:
3427 short_header = false;
3429 case IMAGEX_XML_OPTION:
3431 short_header = false;
3433 case IMAGEX_EXTRACT_XML_OPTION:
3434 xml_out_file = optarg;
3435 short_header = false;
3437 case IMAGEX_IMAGE_PROPERTY_OPTION:
3438 ret = append_image_property_argument(&image_properties);
3449 if (argc < 1 || argc > 4)
3453 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3457 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3458 tsprintf(p, T("NAME=%"TS), argv[2]);
3459 ret = string_list_append(&image_properties, p);
3466 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3467 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3468 ret = string_list_append(&image_properties, p);
3473 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3474 imagex_progress_func, NULL);
3478 wimlib_get_wim_info(wim, &info);
3480 image = wimlib_resolve_image(wim, image_num_or_name);
3481 ret = WIMLIB_ERR_INVALID_IMAGE;
3482 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3483 verify_image_exists(image, image_num_or_name, wimfile);
3485 imagex_error(T("If you would like to set the boot "
3486 "index to 0, specify image \"0\" with "
3487 "the --boot flag."));
3489 goto out_wimlib_free;
3492 if (boot && info.image_count == 0) {
3493 imagex_error(T("--boot is meaningless on a WIM with no images"));
3494 goto out_wimlib_free;
3497 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3499 imagex_error(T("Cannot specify the --boot flag "
3500 "without specifying a specific "
3501 "image in a multi-image WIM"));
3502 goto out_wimlib_free;
3504 if (image_properties.num_strings) {
3505 imagex_error(T("Can't change image properties without "
3506 "specifying a specific image in a "
3507 "multi-image WIM"));
3508 goto out_wimlib_free;
3512 /* Operations that print information are separated from operations that
3513 * recreate the WIM file. */
3514 if (!image_properties.num_strings && !boot) {
3516 /* Read-only operations */
3518 if (image == WIMLIB_NO_IMAGE) {
3519 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3520 image_num_or_name, wimfile);
3521 goto out_wimlib_free;
3524 if (image == WIMLIB_ALL_IMAGES && short_header)
3525 print_wim_information(wimfile, &info);
3528 wimlib_print_header(wim);
3531 if (info.total_parts != 1) {
3532 tfprintf(stderr, T("Warning: Only showing the blobs "
3533 "for part %d of a %d-part WIM.\n"),
3534 info.part_number, info.total_parts);
3540 ret = wimlib_extract_xml_data(wim, stdout);
3542 goto out_wimlib_free;
3548 fp = tfopen(xml_out_file, T("wb"));
3550 imagex_error_with_errno(T("Failed to open the "
3551 "file \"%"TS"\" for "
3555 goto out_wimlib_free;
3557 ret = wimlib_extract_xml_data(wim, fp);
3559 imagex_error(T("Failed to close the file "
3565 goto out_wimlib_free;
3569 wimlib_print_available_images(wim, image);
3573 /* Modification operations */
3574 bool any_property_changes;
3576 if (image == WIMLIB_ALL_IMAGES)
3579 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3580 imagex_error(T("Cannot change image properties "
3581 "when using image 0"));
3583 goto out_wimlib_free;
3587 if (image == info.boot_index) {
3588 imagex_printf(T("Image %d is already marked as "
3589 "bootable.\n"), image);
3592 imagex_printf(T("Marking image %d as bootable.\n"),
3594 info.boot_index = image;
3595 ret = wimlib_set_wim_info(wim, &info,
3596 WIMLIB_CHANGE_BOOT_INDEX);
3598 goto out_wimlib_free;
3602 ret = apply_image_properties(&image_properties, wim, image,
3603 &any_property_changes);
3605 goto out_wimlib_free;
3607 /* Only call wimlib_overwrite() if something actually needs to
3609 if (boot || any_property_changes ||
3610 ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) &&
3611 !info.has_integrity_table) ||
3612 ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) &&
3613 info.has_integrity_table))
3615 ret = wimlib_overwrite(wim, write_flags, 1);
3617 imagex_printf(T("The file \"%"TS"\" was not modified "
3618 "because nothing needed to be done.\n"),
3626 string_list_destroy(&image_properties);
3630 usage(CMD_INFO, stderr);
3635 /* Join split WIMs into one part WIM */
3637 imagex_join(int argc, tchar **argv, int cmd)
3640 int swm_open_flags = 0;
3641 int wim_write_flags = 0;
3642 const tchar *output_path;
3645 for_opt(c, join_options) {
3647 case IMAGEX_CHECK_OPTION:
3648 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3650 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3651 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3661 imagex_error(T("Must specify one or more split WIM (.swm) "
3665 output_path = argv[0];
3666 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3671 imagex_progress_func,
3677 usage(CMD_JOIN, stderr);
3682 #if WIM_MOUNTING_SUPPORTED
3684 /* Mounts a WIM image. */
3686 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3689 int mount_flags = 0;
3691 const tchar *staging_dir = NULL;
3692 const tchar *wimfile;
3695 struct wimlib_wim_info info;
3699 STRING_LIST(refglobs);
3701 if (cmd == CMD_MOUNTRW) {
3702 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3703 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3706 for_opt(c, mount_options) {
3708 case IMAGEX_ALLOW_OTHER_OPTION:
3709 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3711 case IMAGEX_CHECK_OPTION:
3712 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3714 case IMAGEX_DEBUG_OPTION:
3715 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3717 case IMAGEX_STREAMS_INTERFACE_OPTION:
3718 if (!tstrcasecmp(optarg, T("none")))
3719 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3720 else if (!tstrcasecmp(optarg, T("xattr")))
3721 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3722 else if (!tstrcasecmp(optarg, T("windows")))
3723 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3725 imagex_error(T("Unknown stream interface \"%"TS"\""),
3730 case IMAGEX_REF_OPTION:
3731 ret = string_list_append(&refglobs, optarg);
3733 goto out_free_refglobs;
3735 case IMAGEX_STAGING_DIR_OPTION:
3736 staging_dir = optarg;
3738 case IMAGEX_UNIX_DATA_OPTION:
3739 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3747 if (argc != 2 && argc != 3)
3752 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3753 imagex_progress_func, NULL);
3755 goto out_free_refglobs;
3757 wimlib_get_wim_info(wim, &info);
3760 /* Image explicitly specified. */
3761 image = wimlib_resolve_image(wim, argv[1]);
3763 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3767 /* No image specified; default to image 1, but only if the WIM
3768 * contains exactly one image. */
3770 if (info.image_count != 1) {
3771 imagex_error(T("\"%"TS"\" contains %d images; Please "
3772 "select one."), wimfile, info.image_count);
3780 if (refglobs.num_strings) {
3781 ret = wim_reference_globs(wim, &refglobs, open_flags);
3786 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3788 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3789 do_metadata_not_found_warning(wimfile, &info);
3791 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3793 image, wimfile, dir);
3799 string_list_destroy(&refglobs);
3805 goto out_free_refglobs;
3807 #endif /* WIM_MOUNTING_SUPPORTED */
3809 /* Rebuild a WIM file */
3811 imagex_optimize(int argc, tchar **argv, int cmd)
3814 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3815 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3816 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3817 uint32_t chunk_size = UINT32_MAX;
3818 uint32_t solid_chunk_size = UINT32_MAX;
3819 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3822 const tchar *wimfile;
3825 unsigned num_threads = 0;
3827 for_opt(c, optimize_options) {
3829 case IMAGEX_CHECK_OPTION:
3830 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3832 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3833 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3835 case IMAGEX_NOCHECK_OPTION:
3836 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3838 case IMAGEX_COMPRESS_OPTION:
3839 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3840 compression_type = get_compression_type(optarg, false);
3841 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3844 case IMAGEX_RECOMPRESS_OPTION:
3845 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3847 case IMAGEX_CHUNK_SIZE_OPTION:
3848 chunk_size = parse_chunk_size(optarg);
3849 if (chunk_size == UINT32_MAX)
3852 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3853 solid_chunk_size = parse_chunk_size(optarg);
3854 if (solid_chunk_size == UINT32_MAX)
3857 case IMAGEX_SOLID_COMPRESS_OPTION:
3858 solid_ctype = get_compression_type(optarg, true);
3859 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3862 case IMAGEX_SOLID_OPTION:
3863 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3864 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3866 case IMAGEX_NO_SOLID_SORT_OPTION:
3867 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3869 case IMAGEX_THREADS_OPTION:
3870 num_threads = parse_num_threads(optarg);
3871 if (num_threads == UINT_MAX)
3874 case IMAGEX_PIPABLE_OPTION:
3875 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3877 case IMAGEX_NOT_PIPABLE_OPTION:
3878 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3880 case IMAGEX_UNSAFE_COMPACT_OPTION:
3881 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3895 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3896 imagex_progress_func, NULL);
3900 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3901 /* Change compression type. */
3902 ret = wimlib_set_output_compression_type(wim, compression_type);
3904 goto out_wimlib_free;
3907 if (chunk_size != UINT32_MAX) {
3908 /* Change chunk size. */
3909 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3911 goto out_wimlib_free;
3913 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3914 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3916 goto out_wimlib_free;
3918 if (solid_chunk_size != UINT32_MAX) {
3919 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3921 goto out_wimlib_free;
3924 old_size = file_get_size(wimfile);
3925 tprintf(T("\"%"TS"\" original size: "), wimfile);
3927 tputs(T("Unknown"));
3929 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3931 ret = wimlib_overwrite(wim, write_flags, num_threads);
3933 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3934 goto out_wimlib_free;
3937 new_size = file_get_size(wimfile);
3938 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3940 tputs(T("Unknown"));
3942 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3944 tfputs(T("Space saved: "), stdout);
3945 if (new_size != -1 && old_size != -1) {
3946 tprintf(T("%lld KiB\n"),
3947 ((long long)old_size - (long long)new_size) >> 10);
3949 tputs(T("Unknown"));
3958 usage(CMD_OPTIMIZE, stderr);
3964 /* Split a WIM into a spanned set */
3966 imagex_split(int argc, tchar **argv, int cmd)
3970 int write_flags = 0;
3971 unsigned long part_size;
3976 for_opt(c, split_options) {
3978 case IMAGEX_CHECK_OPTION:
3979 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3981 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3982 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3994 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3995 if (tmp == argv[2] || *tmp) {
3996 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3997 imagex_error(T("The part size must be an integer or "
3998 "floating-point number of megabytes."));
4001 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
4002 imagex_progress_func, NULL);
4006 ret = wimlib_split(wim, argv[1], part_size, write_flags);
4012 usage(CMD_SPLIT, stderr);
4018 #if WIM_MOUNTING_SUPPORTED
4019 /* Unmounts a mounted WIM image. */
4021 imagex_unmount(int argc, tchar **argv, int cmd)
4024 int unmount_flags = 0;
4027 for_opt(c, unmount_options) {
4029 case IMAGEX_COMMIT_OPTION:
4030 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
4032 case IMAGEX_CHECK_OPTION:
4033 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
4035 case IMAGEX_REBUILD_OPTION:
4036 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
4038 case IMAGEX_LAZY_OPTION:
4039 case IMAGEX_FORCE_OPTION:
4040 /* Now, unmount is lazy by default. However, committing
4041 * the image will fail with
4042 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4043 * file descriptors on the WIM image. The
4044 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4045 * descriptors to be closed. */
4046 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4048 case IMAGEX_NEW_IMAGE_OPTION:
4049 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4060 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4061 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4062 imagex_error(T("--new-image is meaningless "
4063 "without --commit also specified!"));
4068 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4069 imagex_progress_func, NULL);
4071 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4072 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4074 "\tNote: Use --commit --force to force changes "
4075 "to be committed, regardless\n"
4076 "\t of open files.\n"));
4083 usage(CMD_UNMOUNT, stderr);
4088 #endif /* WIM_MOUNTING_SUPPORTED */
4091 * Add, delete, or rename files in a WIM image.
4094 imagex_update(int argc, tchar **argv, int cmd)
4096 const tchar *wimfile;
4100 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4101 int write_flags = 0;
4102 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4103 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4104 WIMLIB_ADD_FLAG_VERBOSE |
4105 WIMLIB_ADD_FLAG_WINCONFIG;
4106 int default_delete_flags = 0;
4107 unsigned num_threads = 0;
4109 tchar *cmd_file_contents;
4110 size_t cmd_file_nchars;
4111 struct wimlib_update_command *cmds;
4113 tchar *command_str = NULL;
4114 tchar *config_file = NULL;
4115 tchar *wimboot_config = NULL;
4117 for_opt(c, update_options) {
4119 /* Generic or write options */
4120 case IMAGEX_THREADS_OPTION:
4121 num_threads = parse_num_threads(optarg);
4122 if (num_threads == UINT_MAX)
4125 case IMAGEX_CHECK_OPTION:
4126 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4128 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4129 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4131 case IMAGEX_REBUILD_OPTION:
4132 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4134 case IMAGEX_COMMAND_OPTION:
4136 imagex_error(T("--command may only be specified "
4137 "one time. Please provide\n"
4138 " the update commands "
4139 "on standard input instead."));
4142 command_str = tstrdup(optarg);
4144 imagex_error(T("Out of memory!"));
4148 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4149 wimboot_config = optarg;
4151 /* Default delete options */
4152 case IMAGEX_FORCE_OPTION:
4153 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4155 case IMAGEX_RECURSIVE_OPTION:
4156 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4159 /* Global add option */
4160 case IMAGEX_CONFIG_OPTION:
4161 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4162 config_file = optarg;
4165 /* Default add options */
4166 case IMAGEX_VERBOSE_OPTION:
4167 /* No longer does anything. */
4169 case IMAGEX_DEREFERENCE_OPTION:
4170 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4172 case IMAGEX_UNIX_DATA_OPTION:
4173 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4175 case IMAGEX_NO_ACLS_OPTION:
4176 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4178 case IMAGEX_STRICT_ACLS_OPTION:
4179 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4181 case IMAGEX_NO_REPLACE_OPTION:
4182 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4184 case IMAGEX_UNSAFE_COMPACT_OPTION:
4185 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4194 if (argc != 1 && argc != 2)
4198 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4199 imagex_progress_func, NULL);
4201 goto out_free_command_str;
4204 /* Image explicitly specified. */
4205 image = wimlib_resolve_image(wim, argv[1]);
4206 ret = verify_image_exists_and_is_single(image, argv[1],
4209 goto out_wimlib_free;
4211 /* No image specified; default to image 1, but only if the WIM
4212 * contains exactly one image. */
4213 struct wimlib_wim_info info;
4215 wimlib_get_wim_info(wim, &info);
4216 if (info.image_count != 1) {
4217 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4218 wimfile, info.image_count);
4225 /* Read update commands from standard input, or the command string if
4228 cmd_file_contents = NULL;
4229 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4233 goto out_free_cmd_file_contents;
4235 } else if (!wimboot_config) {
4236 if (isatty(STDIN_FILENO)) {
4237 tputs(T("Reading update commands from standard input..."));
4238 recommend_man_page(CMD_UPDATE, stdout);
4240 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4241 if (!cmd_file_contents) {
4243 goto out_wimlib_free;
4246 /* Parse the update commands */
4247 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4251 goto out_free_cmd_file_contents;
4254 cmd_file_contents = NULL;
4259 /* Set default flags and capture config on the update commands */
4260 for (size_t i = 0; i < num_cmds; i++) {
4261 switch (cmds[i].op) {
4262 case WIMLIB_UPDATE_OP_ADD:
4263 cmds[i].add.add_flags |= default_add_flags;
4264 cmds[i].add.config_file = config_file;
4266 case WIMLIB_UPDATE_OP_DELETE:
4267 cmds[i].delete_.delete_flags |= default_delete_flags;
4274 /* Execute the update commands */
4275 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4279 if (wimboot_config) {
4280 /* --wimboot-config=FILE is short for an
4281 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4283 struct wimlib_update_command cmd;
4285 cmd.op = WIMLIB_UPDATE_OP_ADD;
4286 cmd.add.fs_source_path = wimboot_config;
4287 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4288 cmd.add.config_file = NULL;
4289 cmd.add.add_flags = 0;
4291 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4296 /* Overwrite the updated WIM */
4297 ret = wimlib_overwrite(wim, write_flags, num_threads);
4300 out_free_cmd_file_contents:
4301 free(cmd_file_contents);
4304 out_free_command_str:
4309 usage(CMD_UPDATE, stderr);
4312 goto out_free_command_str;
4315 /* Verify a WIM file. */
4317 imagex_verify(int argc, tchar **argv, int cmd)
4320 const tchar *wimfile;
4322 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4323 int verify_flags = 0;
4324 STRING_LIST(refglobs);
4327 for_opt(c, verify_options) {
4329 case IMAGEX_REF_OPTION:
4330 ret = string_list_append(&refglobs, optarg);
4332 goto out_free_refglobs;
4334 case IMAGEX_NOCHECK_OPTION:
4335 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4347 imagex_error(T("Must specify a WIM file!"));
4349 imagex_error(T("At most one WIM file can be specified!"));
4355 ret = wimlib_open_wim_with_progress(wimfile,
4358 imagex_progress_func,
4361 goto out_free_refglobs;
4363 ret = wim_reference_globs(wim, &refglobs, open_flags);
4365 goto out_wimlib_free;
4367 ret = wimlib_verify_wim(wim, verify_flags);
4369 tputc(T('\n'), stderr);
4370 imagex_error(T("\"%"TS"\" failed verification!"),
4372 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4373 refglobs.num_strings == 0)
4375 imagex_printf(T("Note: if this WIM file is not standalone, "
4376 "use the --ref option to specify the other parts.\n"));
4379 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4386 string_list_destroy(&refglobs);
4390 usage(CMD_VERIFY, stderr);
4392 goto out_free_refglobs;
4395 struct imagex_command {
4397 int (*func)(int argc, tchar **argv, int cmd);
4400 static const struct imagex_command imagex_commands[] = {
4401 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4402 [CMD_APPLY] = {T("apply"), imagex_apply},
4403 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4404 [CMD_DELETE] = {T("delete"), imagex_delete},
4405 [CMD_DIR ] = {T("dir"), imagex_dir},
4406 [CMD_EXPORT] = {T("export"), imagex_export},
4407 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4408 [CMD_INFO] = {T("info"), imagex_info},
4409 [CMD_JOIN] = {T("join"), imagex_join},
4410 #if WIM_MOUNTING_SUPPORTED
4411 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4412 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4414 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4415 [CMD_SPLIT] = {T("split"), imagex_split},
4416 #if WIM_MOUNTING_SUPPORTED
4417 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4419 [CMD_UPDATE] = {T("update"), imagex_update},
4420 [CMD_VERIFY] = {T("verify"), imagex_verify},
4425 /* Can be a directory or source list file. But source list file is probably
4426 * a rare use case, so just say directory. */
4427 # define SOURCE_STR T("DIRECTORY")
4429 /* Can only be a directory */
4430 # define TARGET_STR T("DIRECTORY")
4433 /* Can be a directory, NTFS volume, or source list file. */
4434 # define SOURCE_STR T("SOURCE")
4436 /* Can be a directory or NTFS volume. */
4437 # define TARGET_STR T("TARGET")
4441 static const tchar * const usage_strings[] = {
4444 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4445 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4446 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4447 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4448 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4449 " [--dereference] [--snapshot] [--create]\n"
4453 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4454 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4455 " [--no-attributes] [--rpfix] [--norpfix]\n"
4456 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4457 " [--compact=FORMAT]\n"
4461 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4462 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4463 " [--config=FILE] [--threads=NUM_THREADS]\n"
4464 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4465 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4466 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4471 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4475 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4479 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4480 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4481 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4482 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4483 " [--wimboot] [--solid]\n"
4487 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4488 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4489 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4490 " [--no-attributes] [--include-invalid-names]\n"
4491 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4495 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4496 " [--boot] [--check] [--nocheck] [--xml]\n"
4497 " [--extract-xml FILE] [--header] [--blobs]\n"
4498 " [--image-property NAME=VALUE]\n"
4502 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4504 #if WIM_MOUNTING_SUPPORTED
4507 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4508 " [--check] [--streams-interface=INTERFACE]\n"
4509 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4513 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4514 " [--check] [--streams-interface=INTERFACE]\n"
4515 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4521 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4522 " [--check] [--nocheck] [--solid]\n"
4527 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4529 #if WIM_MOUNTING_SUPPORTED
4532 " %"TS" DIRECTORY\n"
4533 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4538 " %"TS" WIMFILE [IMAGE]\n"
4539 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4540 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4541 " [--command=STRING] [--wimboot-config=FILE]\n"
4546 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4550 static const tchar *invocation_name;
4551 static int invocation_cmd = CMD_NONE;
4553 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4555 static tchar buf[50];
4557 if (cmd == CMD_NONE)
4558 return T("wimlib-imagex");
4560 if (only_short_form || invocation_cmd != CMD_NONE) {
4561 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4563 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4564 imagex_commands[cmd].name);
4572 static const tchar * const fmt =
4574 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
4575 "Copyright (C) 2012-2018 Eric Biggers\n"
4576 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4577 "This is free software: you are free to change and redistribute it.\n"
4578 "There is NO WARRANTY, to the extent permitted by law.\n"
4580 "Report bugs to "PACKAGE_BUGREPORT".\n"
4582 tfprintf(stdout, fmt, wimlib_get_version_string());
4586 do_common_options(int *argc_p, tchar **argv, int cmd)
4592 for (i = 1; i < argc; i++) {
4594 if (p[0] == T('-') && p[1] == T('-')) {
4596 if (!tstrcmp(p, T("help"))) {
4597 if (cmd == CMD_NONE)
4602 } else if (!tstrcmp(p, T("version"))) {
4605 } else if (!tstrcmp(p, T("quiet"))) {
4606 imagex_suppress_output();
4607 memmove(&argv[i], &argv[i + 1],
4608 (argc - i) * sizeof(argv[i]));
4611 } else if (!*p) /* reached "--", no more options */
4620 print_usage_string(int cmd, FILE *fp)
4622 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4626 recommend_man_page(int cmd, FILE *fp)
4628 const tchar *format_str;
4630 format_str = T("Some uncommon options are not listed;\n"
4631 "See %"TS".pdf in the doc directory for more details.\n");
4633 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4635 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4639 usage(int cmd, FILE *fp)
4641 tfprintf(fp, T("Usage:\n"));
4642 print_usage_string(cmd, fp);
4643 tfprintf(fp, T("\n"));
4644 recommend_man_page(cmd, fp);
4650 tfprintf(fp, T("Usage:\n"));
4651 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4652 print_usage_string(cmd, fp);
4653 tfprintf(fp, T("\n"));
4655 static const tchar * const extra =
4658 " %"TS" --version\n"
4661 tfprintf(fp, extra, invocation_name, invocation_name);
4663 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4664 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4665 "For some commands IMAGE may be \"all\".\n"
4667 recommend_man_page(CMD_NONE, fp);
4671 extern int wmain(int argc, wchar_t **argv);
4675 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4676 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4677 * something else), while on Windows the command arguments will be UTF-16LE
4678 * encoded 'wchar_t' strings. */
4680 main(int argc, tchar **argv)
4686 imagex_info_file = stdout;
4687 invocation_name = tbasename(argv[0]);
4690 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4691 if (igcase != NULL) {
4692 if (!tstrcmp(igcase, T("no")) ||
4693 !tstrcmp(igcase, T("0")))
4694 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4695 else if (!tstrcmp(igcase, T("yes")) ||
4696 !tstrcmp(igcase, T("1")))
4697 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4700 "WARNING: Ignoring unknown setting of "
4701 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4706 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4708 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4709 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4710 for (int i = 0; i < CMD_MAX; i++) {
4711 if (!tstrcmp(invocation_name + 3,
4712 imagex_commands[i].name))
4721 /* Unless already known from the invocation name, determine which
4722 * command was specified. */
4723 if (cmd == CMD_NONE) {
4725 imagex_error(T("No command specified!\n"));
4729 for (int i = 0; i < CMD_MAX; i++) {
4730 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4735 if (cmd != CMD_NONE) {
4741 /* Handle common options. May exit early (for --help or --version). */
4742 do_common_options(&argc, argv, cmd);
4744 /* Bail if a valid command was not specified. */
4745 if (cmd == CMD_NONE) {
4746 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4751 /* Enable warning and error messages in wimlib to be more user-friendly.
4753 wimlib_set_print_errors(true);
4755 /* Initialize wimlib. */
4756 ret = wimlib_global_init(init_flags);
4758 goto out_check_status;
4760 /* Call the command handler function. */
4761 ret = imagex_commands[cmd].func(argc, argv, cmd);
4763 /* Check for error writing to standard output, especially since for some
4764 * commands, writing to standard output is part of the program's actual
4765 * behavior and not just for informational purposes. */
4766 if (ferror(stdout) || fclose(stdout)) {
4767 imagex_error_with_errno(T("error writing to standard output"));
4772 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4773 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4774 * error code from which an error message can be printed. */
4776 imagex_error(T("Exiting with error code %d:\n"
4778 wimlib_get_error_string(ret));
4779 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4780 imagex_error_with_errno(T("errno"));
4782 /* Make wimlib free any resources it's holding (although this is not
4783 * strictly necessary because the process is ending anyway). */
4784 wimlib_global_cleanup();