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 imagex_printf(T("[WARNING] '--update-of' is unreliable on Windows!\n"));
2027 case IMAGEX_DELTA_FROM_OPTION:
2028 ret = string_list_append(&base_wimfiles, optarg);
2031 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2033 case IMAGEX_WIMBOOT_OPTION:
2034 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2036 case IMAGEX_UNSAFE_COMPACT_OPTION:
2037 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2039 case IMAGEX_SNAPSHOT_OPTION:
2040 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2042 case IMAGEX_CREATE_OPTION:
2043 if (cmd == CMD_CAPTURE) {
2044 imagex_error(T("'--create' is only valid for 'wimappend', not 'wimcapture'"));
2056 if (argc < 2 || argc > 4)
2062 /* Set default compression type and parameters. */
2065 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2066 /* No compression type specified. Use the default. */
2068 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2069 /* With --wimboot, default to XPRESS compression. */
2070 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2071 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2072 /* With --solid, default to LZMS compression. (However,
2073 * this will not affect solid resources!) */
2074 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2076 /* Otherwise, default to LZX compression. */
2077 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2081 if (!tstrcmp(wimfile, T("-"))) {
2082 /* Writing captured WIM to standard output. */
2086 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2087 imagex_error("Can't write a non-pipable WIM to "
2088 "standard output! Specify --pipable\n"
2089 " if you want to create a pipable WIM "
2090 "(but read the docs first).");
2094 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2097 imagex_error(T("Using standard output for append does "
2098 "not make sense."));
2101 wim_fd = STDOUT_FILENO;
2103 imagex_output_to_stderr();
2104 set_fd_to_binary_mode(wim_fd);
2108 /* Check for 'wimappend --create' acting as wimcapture */
2109 if (create && tstat(wimfile, &stbuf) != 0 && errno == ENOENT) {
2113 /* Ignore '--update-of' for the target WIMFILE */
2114 if (template_image_name_or_num &&
2115 (!template_wimfile ||
2116 !tstrcmp(template_wimfile, wimfile)))
2118 template_image_name_or_num = NULL;
2119 template_wimfile = NULL;
2124 if ((write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) && !appending) {
2125 imagex_error(T("'--unsafe-compact' is only valid for append!"));
2129 /* If template image was specified using --update-of=IMAGE rather
2130 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2131 if (template_image_name_or_num && !template_wimfile) {
2132 if (base_wimfiles.num_strings == 1) {
2133 /* Capturing delta WIM based on single WIM: default to
2135 template_wimfile = base_wimfiles.strings[0];
2136 } else if (appending) {
2137 /* Appending to WIM: default to WIM being appended to.
2139 template_wimfile = wimfile;
2141 /* Capturing a normal (non-delta) WIM, so the WIM file
2142 * *must* be explicitly specified. */
2143 if (base_wimfiles.num_strings > 1) {
2144 imagex_error(T("For capture of delta WIM "
2145 "based on multiple existing "
2147 " '--update-of' must "
2148 "specify WIMFILE:IMAGE!"));
2150 imagex_error(T("For capture of non-delta WIM, "
2151 "'--update-of' must specify "
2160 name_defaulted = false;
2162 /* Set default name to SOURCE argument, omitting any directory
2163 * prefixes and trailing slashes. This requires making a copy
2164 * of @source. Leave some free characters at the end in case we
2165 * append a number to keep the name unique. */
2166 size_t source_name_len;
2168 source_name_len = tstrlen(source);
2169 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2170 name = tbasename(tstrcpy(source_copy, source));
2171 name_defaulted = true;
2174 /* Image description (if given). */
2176 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2177 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2178 ret = string_list_append(&image_properties, p);
2184 /* Set up capture sources in source list mode */
2185 if (source[0] == T('-') && source[1] == T('\0')) {
2186 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2188 source_list_contents = file_get_text_contents(source,
2189 &source_list_nchars);
2191 if (!source_list_contents)
2194 capture_sources = parse_source_list(&source_list_contents,
2197 if (!capture_sources) {
2199 goto out_free_source_list_contents;
2201 capture_sources_malloced = true;
2203 /* Set up capture source in non-source-list mode. */
2204 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2205 capture_sources[0].fs_source_path = source;
2206 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2207 capture_sources[0].reserved = 0;
2209 capture_sources_malloced = false;
2210 source_list_contents = NULL;
2213 /* Open the existing WIM, or create a new one. */
2215 ret = wimlib_open_wim_with_progress(wimfile,
2216 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2218 imagex_progress_func,
2221 goto out_free_capture_sources;
2223 ret = wimlib_create_new_wim(compression_type, &wim);
2225 goto out_free_capture_sources;
2226 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2229 /* Set chunk size if non-default. */
2230 if (chunk_size != UINT32_MAX) {
2231 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2234 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2236 int ctype = compression_type;
2239 struct wimlib_wim_info info;
2240 wimlib_get_wim_info(wim, &info);
2241 ctype = info.compression_type;
2244 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2245 ret = wimlib_set_output_chunk_size(wim, 4096);
2250 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2251 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2255 if (solid_chunk_size != UINT32_MAX) {
2256 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2262 /* Detect if source is regular file or block device and set NTFS volume
2267 if (tstat(source, &stbuf) == 0) {
2268 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2269 imagex_printf(T("Capturing WIM image from NTFS "
2270 "filesystem on \"%"TS"\"\n"), source);
2271 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2274 if (errno != ENOENT) {
2275 imagex_error_with_errno(T("Failed to stat "
2276 "\"%"TS"\""), source);
2284 /* If the user did not specify an image name, and the basename of the
2285 * source already exists as an image name in the WIM file, append a
2286 * suffix to make it unique. */
2287 if (appending && name_defaulted) {
2288 unsigned long conflict_idx;
2289 tchar *name_end = tstrchr(name, T('\0'));
2290 for (conflict_idx = 1;
2291 wimlib_image_name_in_use(wim, name);
2294 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2298 /* If capturing a delta WIM, reference resources from the base WIMs
2299 * before adding the new image. */
2300 if (base_wimfiles.num_strings) {
2301 base_wims = calloc(base_wimfiles.num_strings,
2302 sizeof(base_wims[0]));
2303 if (base_wims == NULL) {
2304 imagex_error(T("Out of memory!"));
2309 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2310 ret = wimlib_open_wim_with_progress(
2311 base_wimfiles.strings[i], open_flags,
2312 &base_wims[i], imagex_progress_func, NULL);
2314 goto out_free_base_wims;
2318 ret = wimlib_reference_resources(wim, base_wims,
2319 base_wimfiles.num_strings, 0);
2321 goto out_free_base_wims;
2323 if (base_wimfiles.num_strings == 1) {
2324 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2325 base_wimfiles.strings[0]);
2327 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2328 base_wimfiles.num_strings);
2335 /* If capturing or appending as an update of an existing (template) image,
2336 * open the WIM if needed and parse the image index. */
2337 if (template_image_name_or_num) {
2339 if (appending && !tstrcmp(template_wimfile, wimfile)) {
2342 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2343 if (!tstrcmp(template_wimfile,
2344 base_wimfiles.strings[i])) {
2345 template_wim = base_wims[i];
2351 if (!template_wim) {
2352 ret = wimlib_open_wim_with_progress(template_wimfile,
2355 imagex_progress_func,
2358 goto out_free_base_wims;
2361 template_image = wimlib_resolve_image(template_wim,
2362 template_image_name_or_num);
2364 if (template_image_name_or_num[0] == T('-')) {
2367 struct wimlib_wim_info info;
2369 wimlib_get_wim_info(template_wim, &info);
2370 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2371 if (n >= 1 && n <= info.image_count &&
2373 tmp != template_image_name_or_num + 1)
2375 template_image = info.image_count - (n - 1);
2378 ret = verify_image_exists_and_is_single(template_image,
2379 template_image_name_or_num,
2382 goto out_free_template_wim;
2385 ret = wimlib_add_image_multisource(wim,
2392 goto out_free_template_wim;
2394 if (image_properties.num_strings || template_image_name_or_num) {
2395 /* User asked to set additional image properties, or an image on
2396 * which the added one is to be based has been specified with
2398 struct wimlib_wim_info info;
2400 wimlib_get_wim_info(wim, &info);
2402 ret = apply_image_properties(&image_properties, wim,
2403 info.image_count, NULL);
2405 goto out_free_template_wim;
2407 /* Reference template image if the user provided one. */
2408 if (template_image_name_or_num) {
2409 imagex_printf(T("Using image %d "
2410 "from \"%"TS"\" as template\n"),
2411 template_image, template_wimfile);
2412 ret = wimlib_reference_template_image(wim,
2418 goto out_free_template_wim;
2422 /* Write the new WIM or overwrite the existing WIM with the new image
2425 ret = wimlib_overwrite(wim, write_flags, num_threads);
2426 } else if (wimfile) {
2427 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2428 write_flags, num_threads);
2430 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2431 write_flags, num_threads);
2433 out_free_template_wim:
2434 /* 'template_wim' may alias 'wim' or any of the 'base_wims' */
2435 if (template_wim == wim)
2436 goto out_free_base_wims;
2437 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2438 if (template_wim == base_wims[i])
2439 goto out_free_base_wims;
2440 wimlib_free(template_wim);
2442 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2443 wimlib_free(base_wims[i]);
2447 out_free_capture_sources:
2448 if (capture_sources_malloced)
2449 free(capture_sources);
2450 out_free_source_list_contents:
2451 free(source_list_contents);
2453 string_list_destroy(&image_properties);
2454 string_list_destroy(&base_wimfiles);
2464 /* Remove image(s) from a WIM. */
2466 imagex_delete(int argc, tchar **argv, int cmd)
2469 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2470 int write_flags = 0;
2471 const tchar *wimfile;
2472 const tchar *image_num_or_name;
2477 for_opt(c, delete_options) {
2479 case IMAGEX_CHECK_OPTION:
2480 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2482 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2483 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2485 case IMAGEX_SOFT_OPTION:
2486 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2488 case IMAGEX_UNSAFE_COMPACT_OPTION:
2489 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2500 imagex_error(T("Must specify a WIM file"));
2502 imagex_error(T("Must specify an image"));
2506 image_num_or_name = argv[1];
2508 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2509 imagex_progress_func, NULL);
2513 image = wimlib_resolve_image(wim, image_num_or_name);
2515 ret = verify_image_exists(image, image_num_or_name, wimfile);
2517 goto out_wimlib_free;
2519 ret = wimlib_delete_image(wim, image);
2521 imagex_error(T("Failed to delete image from \"%"TS"\""),
2523 goto out_wimlib_free;
2526 ret = wimlib_overwrite(wim, write_flags, 0);
2528 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2529 "deleted"), wimfile);
2537 usage(CMD_DELETE, stderr);
2542 struct print_dentry_options {
2547 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2549 tprintf(T("%"TS"\n"), dentry->full_path);
2552 static const struct {
2555 } file_attr_flags[] = {
2556 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2557 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2558 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2559 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2560 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2561 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2562 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2563 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2564 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2565 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2566 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2567 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2568 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2569 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2570 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2573 #define TIMESTR_MAX 100
2576 print_time(const tchar *type, const struct wimlib_timespec *wts,
2579 tchar timestr[TIMESTR_MAX];
2583 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2584 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2589 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2590 timestr[TIMESTR_MAX - 1] = '\0';
2592 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2595 static void print_byte_field(const uint8_t field[], size_t len)
2598 tprintf(T("%02hhx"), *field++);
2602 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2604 tchar attr_string[256];
2607 tputs(T("WIM Information:"));
2608 tputs(T("----------------"));
2609 tprintf(T("Path: %"TS"\n"), wimfile);
2610 tprintf(T("GUID: 0x"));
2611 print_byte_field(info->guid, sizeof(info->guid));
2613 tprintf(T("Version: %u\n"), info->wim_version);
2614 tprintf(T("Image Count: %d\n"), info->image_count);
2615 tprintf(T("Compression: %"TS"\n"),
2616 wimlib_get_compression_type_string(info->compression_type));
2617 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2619 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2620 tprintf(T("Boot Index: %d\n"), info->boot_index);
2621 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2623 attr_string[0] = T('\0');
2626 tstrcat(attr_string, T("Pipable, "));
2628 if (info->has_integrity_table)
2629 tstrcat(attr_string, T("Integrity info, "));
2631 if (info->has_rpfix)
2632 tstrcat(attr_string, T("Relative path junction, "));
2634 if (info->resource_only)
2635 tstrcat(attr_string, T("Resource only, "));
2637 if (info->metadata_only)
2638 tstrcat(attr_string, T("Metadata only, "));
2640 if (info->is_marked_readonly)
2641 tstrcat(attr_string, T("Readonly, "));
2643 p = tstrchr(attr_string, T('\0'));
2644 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2647 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2651 print_resource(const struct wimlib_resource_entry *resource,
2654 tprintf(T("Hash = 0x"));
2655 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2658 if (!resource->is_missing) {
2659 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2660 resource->uncompressed_size);
2661 if (resource->packed) {
2662 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2663 "bytes @ offset %"PRIu64"\n"),
2664 resource->raw_resource_uncompressed_size,
2665 resource->raw_resource_compressed_size,
2666 resource->raw_resource_offset_in_wim);
2668 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2671 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2672 resource->compressed_size);
2674 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2678 tprintf(T("Part Number = %u\n"), resource->part_number);
2679 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2681 tprintf(T("Flags = "));
2682 if (resource->is_compressed)
2683 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2684 if (resource->is_metadata)
2685 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2686 if (resource->is_free)
2687 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2688 if (resource->is_spanned)
2689 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2690 if (resource->packed)
2691 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2699 print_blobs(WIMStruct *wim)
2701 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2706 default_print_security_descriptor(const uint8_t *sd, size_t size)
2708 tprintf(T("Security Descriptor = "));
2709 print_byte_field(sd, size);
2715 is_null_guid(const uint8_t *guid)
2717 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2719 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2723 print_guid(const tchar *label, const uint8_t *guid)
2725 if (is_null_guid(guid))
2727 tprintf(T("%-20"TS"= 0x"), label);
2728 print_byte_field(guid, WIMLIB_GUID_LEN);
2733 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2736 "----------------------------------------------------------------------------\n"));
2737 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2738 if (dentry->dos_name)
2739 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2740 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2741 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2742 if (file_attr_flags[i].flag & dentry->attributes)
2743 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2744 file_attr_flags[i].name);
2746 if (dentry->security_descriptor) {
2747 print_security_descriptor(dentry->security_descriptor,
2748 dentry->security_descriptor_size);
2751 print_time(T("Creation Time"),
2752 &dentry->creation_time, dentry->creation_time_high);
2753 print_time(T("Last Write Time"),
2754 &dentry->last_write_time, dentry->last_write_time_high);
2755 print_time(T("Last Access Time"),
2756 &dentry->last_access_time, dentry->last_access_time_high);
2759 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2760 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2762 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2763 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2765 if (dentry->unix_mode != 0) {
2766 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2767 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2768 dentry->unix_uid, dentry->unix_gid,
2769 dentry->unix_mode, dentry->unix_rdev);
2772 if (!is_null_guid(dentry->object_id.object_id)) {
2773 print_guid(T("Object ID"), dentry->object_id.object_id);
2774 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2775 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2776 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2779 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2780 if (dentry->streams[i].stream_name) {
2781 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2782 dentry->streams[i].stream_name);
2783 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2784 tprintf(T("\tRaw encrypted data stream:\n"));
2785 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2786 tprintf(T("\tReparse point stream:\n"));
2788 tprintf(T("\tUnnamed data stream:\n"));
2790 print_resource(&dentry->streams[i].resource, NULL);
2795 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2797 const struct print_dentry_options *options = _options;
2798 if (!options->detailed)
2799 print_dentry_full_path(dentry);
2801 print_dentry_detailed(dentry);
2805 /* Print the files contained in an image(s) in a WIM file. */
2807 imagex_dir(int argc, tchar **argv, int cmd)
2809 const tchar *wimfile;
2810 WIMStruct *wim = NULL;
2813 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2815 struct print_dentry_options options = {
2818 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2820 STRING_LIST(refglobs);
2822 for_opt(c, dir_options) {
2824 case IMAGEX_PATH_OPTION:
2827 case IMAGEX_DETAILED_OPTION:
2828 options.detailed = true;
2830 case IMAGEX_ONE_FILE_ONLY_OPTION:
2831 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2833 case IMAGEX_REF_OPTION:
2834 ret = string_list_append(&refglobs, optarg);
2836 goto out_free_refglobs;
2846 imagex_error(T("Must specify a WIM file"));
2850 imagex_error(T("Too many arguments"));
2855 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2856 imagex_progress_func, NULL);
2858 goto out_free_refglobs;
2861 image = wimlib_resolve_image(wim, argv[1]);
2862 ret = verify_image_exists(image, argv[1], wimfile);
2864 goto out_wimlib_free;
2866 /* No image specified; default to image 1, but only if the WIM
2867 * contains exactly one image. */
2869 struct wimlib_wim_info info;
2871 wimlib_get_wim_info(wim, &info);
2872 if (info.image_count != 1) {
2873 imagex_error(T("\"%"TS"\" contains %d images; Please "
2874 "select one (or all)."),
2875 wimfile, info.image_count);
2882 if (refglobs.num_strings) {
2883 ret = wim_reference_globs(wim, &refglobs, 0);
2885 goto out_wimlib_free;
2888 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2889 print_dentry, &options);
2890 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2891 struct wimlib_wim_info info;
2893 wimlib_get_wim_info(wim, &info);
2894 do_metadata_not_found_warning(wimfile, &info);
2899 string_list_destroy(&refglobs);
2903 usage(CMD_DIR, stderr);
2905 goto out_free_refglobs;
2908 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2911 imagex_export(int argc, tchar **argv, int cmd)
2915 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2916 int write_flags = 0;
2917 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2918 const tchar *src_wimfile;
2919 const tchar *src_image_num_or_name;
2920 const tchar *dest_wimfile;
2922 const tchar *dest_name;
2923 const tchar *dest_desc;
2925 struct wimlib_wim_info src_info;
2926 WIMStruct *dest_wim;
2931 STRING_LIST(refglobs);
2932 unsigned num_threads = 0;
2933 uint32_t chunk_size = UINT32_MAX;
2934 uint32_t solid_chunk_size = UINT32_MAX;
2935 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2937 for_opt(c, export_options) {
2939 case IMAGEX_BOOT_OPTION:
2940 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2942 case IMAGEX_CHECK_OPTION:
2943 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2945 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2946 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2948 case IMAGEX_NOCHECK_OPTION:
2949 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2951 case IMAGEX_COMPRESS_OPTION:
2952 compression_type = get_compression_type(optarg, false);
2953 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2956 case IMAGEX_RECOMPRESS_OPTION:
2957 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2959 case IMAGEX_SOLID_OPTION:
2960 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2962 case IMAGEX_NO_SOLID_SORT_OPTION:
2963 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2965 case IMAGEX_CHUNK_SIZE_OPTION:
2966 chunk_size = parse_chunk_size(optarg);
2967 if (chunk_size == UINT32_MAX)
2970 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2971 solid_chunk_size = parse_chunk_size(optarg);
2972 if (solid_chunk_size == UINT32_MAX)
2975 case IMAGEX_SOLID_COMPRESS_OPTION:
2976 solid_ctype = get_compression_type(optarg, true);
2977 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2980 case IMAGEX_REF_OPTION:
2981 ret = string_list_append(&refglobs, optarg);
2983 goto out_free_refglobs;
2985 case IMAGEX_THREADS_OPTION:
2986 num_threads = parse_num_threads(optarg);
2987 if (num_threads == UINT_MAX)
2990 case IMAGEX_REBUILD_OPTION:
2991 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2993 case IMAGEX_PIPABLE_OPTION:
2994 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2996 case IMAGEX_NOT_PIPABLE_OPTION:
2997 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2999 case IMAGEX_WIMBOOT_OPTION:
3000 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
3002 case IMAGEX_UNSAFE_COMPACT_OPTION:
3003 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3011 if (argc < 3 || argc > 5)
3013 src_wimfile = argv[0];
3014 src_image_num_or_name = argv[1];
3015 dest_wimfile = argv[2];
3016 dest_name = (argc >= 4) ? argv[3] : NULL;
3017 dest_desc = (argc >= 5) ? argv[4] : NULL;
3018 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
3019 imagex_progress_func, NULL);
3021 goto out_free_refglobs;
3023 wimlib_get_wim_info(src_wim, &src_info);
3025 /* Determine if the destination is an existing file or not. If so, we
3026 * try to append the exported image(s) to it; otherwise, we create a new
3027 * WIM containing the exported image(s). Furthermore, determine if we
3028 * need to write a pipable WIM directly to standard output. */
3030 if (tstrcmp(dest_wimfile, T("-")) == 0) {
3032 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
3033 imagex_error("Can't write a non-pipable WIM to "
3034 "standard output! Specify --pipable\n"
3035 " if you want to create a pipable WIM "
3036 "(but read the docs first).");
3038 goto out_free_src_wim;
3041 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3043 dest_wimfile = NULL;
3044 dest_wim_fd = STDOUT_FILENO;
3045 imagex_output_to_stderr();
3046 set_fd_to_binary_mode(dest_wim_fd);
3049 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
3051 /* Destination file exists. */
3053 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
3054 imagex_error(T("\"%"TS"\" is not a regular file "
3055 "or block device"), dest_wimfile);
3057 goto out_free_src_wim;
3059 ret = wimlib_open_wim_with_progress(dest_wimfile,
3061 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3063 imagex_progress_func,
3066 goto out_free_src_wim;
3068 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3069 /* The user specified a compression type, but we're
3070 * exporting to an existing WIM. Make sure the
3071 * specified compression type is the same as the
3072 * compression type of the existing destination WIM. */
3073 struct wimlib_wim_info dest_info;
3075 wimlib_get_wim_info(dest_wim, &dest_info);
3076 if (compression_type != dest_info.compression_type) {
3077 imagex_error(T("Cannot specify a compression type that is "
3078 "not the same as that used in the "
3079 "destination WIM"));
3081 goto out_free_dest_wim;
3087 if (errno != ENOENT) {
3088 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3091 goto out_free_src_wim;
3094 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3095 imagex_error(T("'--unsafe-compact' is only valid when "
3096 "exporting to an existing WIM file!"));
3098 goto out_free_src_wim;
3101 /* dest_wimfile is not an existing file, so create a new WIM. */
3103 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3104 /* The user did not specify a compression type; default
3105 * to that of the source WIM, unless --solid or
3106 * --wimboot was specified. */
3108 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3109 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3110 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3111 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3113 compression_type = src_info.compression_type;
3115 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3117 goto out_free_src_wim;
3119 wimlib_register_progress_function(dest_wim,
3120 imagex_progress_func, NULL);
3122 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3123 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3125 /* For --wimboot export, use small XPRESS chunks. */
3126 wimlib_set_output_chunk_size(dest_wim, 4096);
3127 } else if (compression_type == src_info.compression_type &&
3128 chunk_size == UINT32_MAX)
3130 /* Use same chunk size if compression type is the same. */
3131 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3135 if (chunk_size != UINT32_MAX) {
3136 /* Set destination chunk size. */
3137 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3139 goto out_free_dest_wim;
3141 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3142 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3144 goto out_free_dest_wim;
3146 if (solid_chunk_size != UINT32_MAX) {
3147 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3149 goto out_free_dest_wim;
3152 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3153 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3155 goto out_free_dest_wim;
3157 if (refglobs.num_strings) {
3158 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3160 goto out_free_dest_wim;
3163 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3164 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3166 imagex_error(T("--boot specified for all-images export, but source WIM "
3167 "has no bootable image."));
3169 goto out_free_dest_wim;
3172 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3173 dest_desc, export_flags);
3175 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3176 do_resource_not_found_warning(src_wimfile,
3177 &src_info, &refglobs);
3178 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3179 do_metadata_not_found_warning(src_wimfile, &src_info);
3181 goto out_free_dest_wim;
3185 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3186 else if (dest_wimfile)
3187 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3188 write_flags, num_threads);
3190 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3191 WIMLIB_ALL_IMAGES, write_flags,
3194 wimlib_free(dest_wim);
3196 wimlib_free(src_wim);
3198 string_list_destroy(&refglobs);
3202 usage(CMD_EXPORT, stderr);
3205 goto out_free_refglobs;
3208 /* Extract files or directories from a WIM image */
3210 imagex_extract(int argc, tchar **argv, int cmd)
3217 const tchar *wimfile;
3218 const tchar *image_num_or_name;
3219 tchar *dest_dir = T(".");
3220 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3221 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3222 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3223 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3225 STRING_LIST(refglobs);
3227 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3229 for_opt(c, extract_options) {
3231 case IMAGEX_CHECK_OPTION:
3232 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3234 case IMAGEX_VERBOSE_OPTION:
3235 /* No longer does anything. */
3237 case IMAGEX_REF_OPTION:
3238 ret = string_list_append(&refglobs, optarg);
3240 goto out_free_refglobs;
3242 case IMAGEX_UNIX_DATA_OPTION:
3243 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3245 case IMAGEX_NO_ACLS_OPTION:
3246 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3248 case IMAGEX_STRICT_ACLS_OPTION:
3249 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3251 case IMAGEX_NO_ATTRIBUTES_OPTION:
3252 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3254 case IMAGEX_DEST_DIR_OPTION:
3257 case IMAGEX_TO_STDOUT_OPTION:
3258 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3259 imagex_suppress_output();
3260 set_fd_to_binary_mode(STDOUT_FILENO);
3262 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3263 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3264 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3266 case IMAGEX_NO_GLOBS_OPTION:
3267 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3269 case IMAGEX_NULLGLOB_OPTION:
3270 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3272 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3273 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3275 case IMAGEX_WIMBOOT_OPTION:
3276 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3278 case IMAGEX_COMPACT_OPTION:
3279 ret = set_compact_mode(optarg, &extract_flags);
3281 goto out_free_refglobs;
3293 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3294 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3296 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3301 image_num_or_name = argv[1];
3306 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3307 imagex_progress_func, NULL);
3309 goto out_free_refglobs;
3311 image = wimlib_resolve_image(wim, image_num_or_name);
3312 ret = verify_image_exists_and_is_single(image,
3316 goto out_wimlib_free;
3318 if (refglobs.num_strings) {
3319 ret = wim_reference_globs(wim, &refglobs, open_flags);
3321 goto out_wimlib_free;
3327 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3330 while (argc != 0 && ret == 0) {
3334 num_paths < argc && argv[num_paths][0] != T('@');
3339 ret = wimlib_extract_paths(wim, image, dest_dir,
3340 (const tchar **)argv,
3342 extract_flags | notlist_extract_flags);
3346 const tchar *listfile = argv[0] + 1;
3348 if (!tstrcmp(listfile, T("-"))) {
3349 tputs(T("Reading pathlist file from standard input..."));
3353 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3354 listfile, extract_flags);
3361 imagex_printf(T("Done extracting files.\n"));
3362 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3363 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3364 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3365 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3366 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3369 T("Note: You can use the '--nullglob' "
3370 "option to ignore missing files.\n"));
3372 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3373 "files and directories\n"
3374 " are in the WIM image.\n"),
3375 get_cmd_string(CMD_DIR, false));
3376 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3377 struct wimlib_wim_info info;
3379 wimlib_get_wim_info(wim, &info);
3380 do_resource_not_found_warning(wimfile, &info, &refglobs);
3381 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3382 struct wimlib_wim_info info;
3384 wimlib_get_wim_info(wim, &info);
3385 do_metadata_not_found_warning(wimfile, &info);
3390 string_list_destroy(&refglobs);
3394 usage(CMD_EXTRACT, stderr);
3397 goto out_free_refglobs;
3400 /* Prints information about a WIM file; also can mark an image as bootable,
3401 * change the name of an image, or change the description of an image. */
3403 imagex_info(int argc, tchar **argv, int cmd)
3407 bool header = false;
3410 bool short_header = true;
3411 const tchar *xml_out_file = NULL;
3412 const tchar *wimfile;
3413 const tchar *image_num_or_name;
3414 STRING_LIST(image_properties);
3419 int write_flags = 0;
3420 struct wimlib_wim_info info;
3422 for_opt(c, info_options) {
3424 case IMAGEX_BOOT_OPTION:
3427 case IMAGEX_CHECK_OPTION:
3428 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3430 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3431 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3433 case IMAGEX_NOCHECK_OPTION:
3434 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3436 case IMAGEX_HEADER_OPTION:
3438 short_header = false;
3440 case IMAGEX_BLOBS_OPTION:
3442 short_header = false;
3444 case IMAGEX_XML_OPTION:
3446 short_header = false;
3448 case IMAGEX_EXTRACT_XML_OPTION:
3449 xml_out_file = optarg;
3450 short_header = false;
3452 case IMAGEX_IMAGE_PROPERTY_OPTION:
3453 ret = append_image_property_argument(&image_properties);
3464 if (argc < 1 || argc > 4)
3468 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3472 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3473 tsprintf(p, T("NAME=%"TS), argv[2]);
3474 ret = string_list_append(&image_properties, p);
3481 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3482 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3483 ret = string_list_append(&image_properties, p);
3488 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3489 imagex_progress_func, NULL);
3493 wimlib_get_wim_info(wim, &info);
3495 image = wimlib_resolve_image(wim, image_num_or_name);
3496 ret = WIMLIB_ERR_INVALID_IMAGE;
3497 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3498 verify_image_exists(image, image_num_or_name, wimfile);
3500 imagex_error(T("If you would like to set the boot "
3501 "index to 0, specify image \"0\" with "
3502 "the --boot flag."));
3504 goto out_wimlib_free;
3507 if (boot && info.image_count == 0) {
3508 imagex_error(T("--boot is meaningless on a WIM with no images"));
3509 goto out_wimlib_free;
3512 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3514 imagex_error(T("Cannot specify the --boot flag "
3515 "without specifying a specific "
3516 "image in a multi-image WIM"));
3517 goto out_wimlib_free;
3519 if (image_properties.num_strings) {
3520 imagex_error(T("Can't change image properties without "
3521 "specifying a specific image in a "
3522 "multi-image WIM"));
3523 goto out_wimlib_free;
3527 /* Operations that print information are separated from operations that
3528 * recreate the WIM file. */
3529 if (!image_properties.num_strings && !boot) {
3531 /* Read-only operations */
3533 if (image == WIMLIB_NO_IMAGE) {
3534 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3535 image_num_or_name, wimfile);
3536 goto out_wimlib_free;
3539 if (image == WIMLIB_ALL_IMAGES && short_header)
3540 print_wim_information(wimfile, &info);
3543 wimlib_print_header(wim);
3546 if (info.total_parts != 1) {
3547 tfprintf(stderr, T("Warning: Only showing the blobs "
3548 "for part %d of a %d-part WIM.\n"),
3549 info.part_number, info.total_parts);
3555 ret = wimlib_extract_xml_data(wim, stdout);
3557 goto out_wimlib_free;
3563 fp = tfopen(xml_out_file, T("wb"));
3565 imagex_error_with_errno(T("Failed to open the "
3566 "file \"%"TS"\" for "
3570 goto out_wimlib_free;
3572 ret = wimlib_extract_xml_data(wim, fp);
3574 imagex_error(T("Failed to close the file "
3580 goto out_wimlib_free;
3584 wimlib_print_available_images(wim, image);
3588 /* Modification operations */
3589 bool any_property_changes;
3591 if (image == WIMLIB_ALL_IMAGES)
3594 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3595 imagex_error(T("Cannot change image properties "
3596 "when using image 0"));
3598 goto out_wimlib_free;
3602 if (image == info.boot_index) {
3603 imagex_printf(T("Image %d is already marked as "
3604 "bootable.\n"), image);
3607 imagex_printf(T("Marking image %d as bootable.\n"),
3609 info.boot_index = image;
3610 ret = wimlib_set_wim_info(wim, &info,
3611 WIMLIB_CHANGE_BOOT_INDEX);
3613 goto out_wimlib_free;
3617 ret = apply_image_properties(&image_properties, wim, image,
3618 &any_property_changes);
3620 goto out_wimlib_free;
3622 /* Only call wimlib_overwrite() if something actually needs to
3624 if (boot || any_property_changes ||
3625 ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) &&
3626 !info.has_integrity_table) ||
3627 ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) &&
3628 info.has_integrity_table))
3630 ret = wimlib_overwrite(wim, write_flags, 1);
3632 imagex_printf(T("The file \"%"TS"\" was not modified "
3633 "because nothing needed to be done.\n"),
3641 string_list_destroy(&image_properties);
3645 usage(CMD_INFO, stderr);
3650 /* Join split WIMs into one part WIM */
3652 imagex_join(int argc, tchar **argv, int cmd)
3655 int swm_open_flags = 0;
3656 int wim_write_flags = 0;
3657 const tchar *output_path;
3660 for_opt(c, join_options) {
3662 case IMAGEX_CHECK_OPTION:
3663 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3665 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3666 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3676 imagex_error(T("Must specify one or more split WIM (.swm) "
3680 output_path = argv[0];
3681 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3686 imagex_progress_func,
3692 usage(CMD_JOIN, stderr);
3697 #if WIM_MOUNTING_SUPPORTED
3699 /* Mounts a WIM image. */
3701 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3704 int mount_flags = 0;
3706 const tchar *staging_dir = NULL;
3707 const tchar *wimfile;
3710 struct wimlib_wim_info info;
3714 STRING_LIST(refglobs);
3716 if (cmd == CMD_MOUNTRW) {
3717 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3718 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3721 for_opt(c, mount_options) {
3723 case IMAGEX_ALLOW_OTHER_OPTION:
3724 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3726 case IMAGEX_CHECK_OPTION:
3727 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3729 case IMAGEX_DEBUG_OPTION:
3730 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3732 case IMAGEX_STREAMS_INTERFACE_OPTION:
3733 if (!tstrcasecmp(optarg, T("none")))
3734 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3735 else if (!tstrcasecmp(optarg, T("xattr")))
3736 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3737 else if (!tstrcasecmp(optarg, T("windows")))
3738 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3740 imagex_error(T("Unknown stream interface \"%"TS"\""),
3745 case IMAGEX_REF_OPTION:
3746 ret = string_list_append(&refglobs, optarg);
3748 goto out_free_refglobs;
3750 case IMAGEX_STAGING_DIR_OPTION:
3751 staging_dir = optarg;
3753 case IMAGEX_UNIX_DATA_OPTION:
3754 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3762 if (argc != 2 && argc != 3)
3767 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3768 imagex_progress_func, NULL);
3770 goto out_free_refglobs;
3772 wimlib_get_wim_info(wim, &info);
3775 /* Image explicitly specified. */
3776 image = wimlib_resolve_image(wim, argv[1]);
3778 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3782 /* No image specified; default to image 1, but only if the WIM
3783 * contains exactly one image. */
3785 if (info.image_count != 1) {
3786 imagex_error(T("\"%"TS"\" contains %d images; Please "
3787 "select one."), wimfile, info.image_count);
3795 if (refglobs.num_strings) {
3796 ret = wim_reference_globs(wim, &refglobs, open_flags);
3801 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3803 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3804 do_metadata_not_found_warning(wimfile, &info);
3806 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3808 image, wimfile, dir);
3814 string_list_destroy(&refglobs);
3820 goto out_free_refglobs;
3822 #endif /* WIM_MOUNTING_SUPPORTED */
3824 /* Rebuild a WIM file */
3826 imagex_optimize(int argc, tchar **argv, int cmd)
3829 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3830 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3831 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3832 uint32_t chunk_size = UINT32_MAX;
3833 uint32_t solid_chunk_size = UINT32_MAX;
3834 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3837 const tchar *wimfile;
3840 unsigned num_threads = 0;
3842 for_opt(c, optimize_options) {
3844 case IMAGEX_CHECK_OPTION:
3845 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3847 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3848 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3850 case IMAGEX_NOCHECK_OPTION:
3851 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3853 case IMAGEX_COMPRESS_OPTION:
3854 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3855 compression_type = get_compression_type(optarg, false);
3856 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3859 case IMAGEX_RECOMPRESS_OPTION:
3860 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3862 case IMAGEX_CHUNK_SIZE_OPTION:
3863 chunk_size = parse_chunk_size(optarg);
3864 if (chunk_size == UINT32_MAX)
3867 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3868 solid_chunk_size = parse_chunk_size(optarg);
3869 if (solid_chunk_size == UINT32_MAX)
3872 case IMAGEX_SOLID_COMPRESS_OPTION:
3873 solid_ctype = get_compression_type(optarg, true);
3874 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3877 case IMAGEX_SOLID_OPTION:
3878 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3879 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3881 case IMAGEX_NO_SOLID_SORT_OPTION:
3882 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3884 case IMAGEX_THREADS_OPTION:
3885 num_threads = parse_num_threads(optarg);
3886 if (num_threads == UINT_MAX)
3889 case IMAGEX_PIPABLE_OPTION:
3890 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3892 case IMAGEX_NOT_PIPABLE_OPTION:
3893 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3895 case IMAGEX_UNSAFE_COMPACT_OPTION:
3896 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3910 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3911 imagex_progress_func, NULL);
3915 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3916 /* Change compression type. */
3917 ret = wimlib_set_output_compression_type(wim, compression_type);
3919 goto out_wimlib_free;
3922 if (chunk_size != UINT32_MAX) {
3923 /* Change chunk size. */
3924 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3926 goto out_wimlib_free;
3928 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3929 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3931 goto out_wimlib_free;
3933 if (solid_chunk_size != UINT32_MAX) {
3934 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3936 goto out_wimlib_free;
3939 old_size = file_get_size(wimfile);
3940 tprintf(T("\"%"TS"\" original size: "), wimfile);
3942 tputs(T("Unknown"));
3944 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3946 ret = wimlib_overwrite(wim, write_flags, num_threads);
3948 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3949 goto out_wimlib_free;
3952 new_size = file_get_size(wimfile);
3953 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3955 tputs(T("Unknown"));
3957 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3959 tfputs(T("Space saved: "), stdout);
3960 if (new_size != -1 && old_size != -1) {
3961 tprintf(T("%lld KiB\n"),
3962 ((long long)old_size - (long long)new_size) >> 10);
3964 tputs(T("Unknown"));
3973 usage(CMD_OPTIMIZE, stderr);
3979 /* Split a WIM into a spanned set */
3981 imagex_split(int argc, tchar **argv, int cmd)
3985 int write_flags = 0;
3986 unsigned long part_size;
3991 for_opt(c, split_options) {
3993 case IMAGEX_CHECK_OPTION:
3994 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3996 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3997 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4009 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
4010 if (tmp == argv[2] || *tmp) {
4011 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
4012 imagex_error(T("The part size must be an integer or "
4013 "floating-point number of megabytes."));
4016 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
4017 imagex_progress_func, NULL);
4021 ret = wimlib_split(wim, argv[1], part_size, write_flags);
4027 usage(CMD_SPLIT, stderr);
4033 #if WIM_MOUNTING_SUPPORTED
4034 /* Unmounts a mounted WIM image. */
4036 imagex_unmount(int argc, tchar **argv, int cmd)
4039 int unmount_flags = 0;
4042 for_opt(c, unmount_options) {
4044 case IMAGEX_COMMIT_OPTION:
4045 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
4047 case IMAGEX_CHECK_OPTION:
4048 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
4050 case IMAGEX_REBUILD_OPTION:
4051 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
4053 case IMAGEX_LAZY_OPTION:
4054 case IMAGEX_FORCE_OPTION:
4055 /* Now, unmount is lazy by default. However, committing
4056 * the image will fail with
4057 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4058 * file descriptors on the WIM image. The
4059 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4060 * descriptors to be closed. */
4061 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4063 case IMAGEX_NEW_IMAGE_OPTION:
4064 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4075 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4076 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4077 imagex_error(T("--new-image is meaningless "
4078 "without --commit also specified!"));
4083 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4084 imagex_progress_func, NULL);
4086 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4087 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4089 "\tNote: Use --commit --force to force changes "
4090 "to be committed, regardless\n"
4091 "\t of open files.\n"));
4098 usage(CMD_UNMOUNT, stderr);
4103 #endif /* WIM_MOUNTING_SUPPORTED */
4106 * Add, delete, or rename files in a WIM image.
4109 imagex_update(int argc, tchar **argv, int cmd)
4111 const tchar *wimfile;
4115 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4116 int write_flags = 0;
4117 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4118 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4119 WIMLIB_ADD_FLAG_VERBOSE |
4120 WIMLIB_ADD_FLAG_WINCONFIG;
4121 int default_delete_flags = 0;
4122 unsigned num_threads = 0;
4124 tchar *cmd_file_contents;
4125 size_t cmd_file_nchars;
4126 struct wimlib_update_command *cmds;
4128 tchar *command_str = NULL;
4129 tchar *config_file = NULL;
4130 tchar *wimboot_config = NULL;
4132 for_opt(c, update_options) {
4134 /* Generic or write options */
4135 case IMAGEX_THREADS_OPTION:
4136 num_threads = parse_num_threads(optarg);
4137 if (num_threads == UINT_MAX)
4140 case IMAGEX_CHECK_OPTION:
4141 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4143 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4144 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4146 case IMAGEX_REBUILD_OPTION:
4147 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4149 case IMAGEX_COMMAND_OPTION:
4151 imagex_error(T("--command may only be specified "
4152 "one time. Please provide\n"
4153 " the update commands "
4154 "on standard input instead."));
4157 command_str = tstrdup(optarg);
4159 imagex_error(T("Out of memory!"));
4163 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4164 wimboot_config = optarg;
4166 /* Default delete options */
4167 case IMAGEX_FORCE_OPTION:
4168 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4170 case IMAGEX_RECURSIVE_OPTION:
4171 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4174 /* Global add option */
4175 case IMAGEX_CONFIG_OPTION:
4176 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4177 config_file = optarg;
4180 /* Default add options */
4181 case IMAGEX_VERBOSE_OPTION:
4182 /* No longer does anything. */
4184 case IMAGEX_DEREFERENCE_OPTION:
4185 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4187 case IMAGEX_UNIX_DATA_OPTION:
4188 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4190 case IMAGEX_NO_ACLS_OPTION:
4191 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4193 case IMAGEX_STRICT_ACLS_OPTION:
4194 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4196 case IMAGEX_NO_REPLACE_OPTION:
4197 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4199 case IMAGEX_UNSAFE_COMPACT_OPTION:
4200 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4209 if (argc != 1 && argc != 2)
4213 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4214 imagex_progress_func, NULL);
4216 goto out_free_command_str;
4219 /* Image explicitly specified. */
4220 image = wimlib_resolve_image(wim, argv[1]);
4221 ret = verify_image_exists_and_is_single(image, argv[1],
4224 goto out_wimlib_free;
4226 /* No image specified; default to image 1, but only if the WIM
4227 * contains exactly one image. */
4228 struct wimlib_wim_info info;
4230 wimlib_get_wim_info(wim, &info);
4231 if (info.image_count != 1) {
4232 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4233 wimfile, info.image_count);
4240 /* Read update commands from standard input, or the command string if
4243 cmd_file_contents = NULL;
4244 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4248 goto out_free_cmd_file_contents;
4250 } else if (!wimboot_config) {
4251 if (isatty(STDIN_FILENO)) {
4252 tputs(T("Reading update commands from standard input..."));
4253 recommend_man_page(CMD_UPDATE, stdout);
4255 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4256 if (!cmd_file_contents) {
4258 goto out_wimlib_free;
4261 /* Parse the update commands */
4262 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4266 goto out_free_cmd_file_contents;
4269 cmd_file_contents = NULL;
4274 /* Set default flags and capture config on the update commands */
4275 for (size_t i = 0; i < num_cmds; i++) {
4276 switch (cmds[i].op) {
4277 case WIMLIB_UPDATE_OP_ADD:
4278 cmds[i].add.add_flags |= default_add_flags;
4279 cmds[i].add.config_file = config_file;
4281 case WIMLIB_UPDATE_OP_DELETE:
4282 cmds[i].delete_.delete_flags |= default_delete_flags;
4289 /* Execute the update commands */
4290 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4294 if (wimboot_config) {
4295 /* --wimboot-config=FILE is short for an
4296 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4298 struct wimlib_update_command cmd;
4300 cmd.op = WIMLIB_UPDATE_OP_ADD;
4301 cmd.add.fs_source_path = wimboot_config;
4302 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4303 cmd.add.config_file = NULL;
4304 cmd.add.add_flags = 0;
4306 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4311 /* Overwrite the updated WIM */
4312 ret = wimlib_overwrite(wim, write_flags, num_threads);
4315 out_free_cmd_file_contents:
4316 free(cmd_file_contents);
4319 out_free_command_str:
4324 usage(CMD_UPDATE, stderr);
4327 goto out_free_command_str;
4330 /* Verify a WIM file. */
4332 imagex_verify(int argc, tchar **argv, int cmd)
4335 const tchar *wimfile;
4337 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4338 int verify_flags = 0;
4339 STRING_LIST(refglobs);
4342 for_opt(c, verify_options) {
4344 case IMAGEX_REF_OPTION:
4345 ret = string_list_append(&refglobs, optarg);
4347 goto out_free_refglobs;
4349 case IMAGEX_NOCHECK_OPTION:
4350 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4362 imagex_error(T("Must specify a WIM file!"));
4364 imagex_error(T("At most one WIM file can be specified!"));
4370 ret = wimlib_open_wim_with_progress(wimfile,
4373 imagex_progress_func,
4376 goto out_free_refglobs;
4378 ret = wim_reference_globs(wim, &refglobs, open_flags);
4380 goto out_wimlib_free;
4382 ret = wimlib_verify_wim(wim, verify_flags);
4384 tputc(T('\n'), stderr);
4385 imagex_error(T("\"%"TS"\" failed verification!"),
4387 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4388 refglobs.num_strings == 0)
4390 imagex_printf(T("Note: if this WIM file is not standalone, "
4391 "use the --ref option to specify the other parts.\n"));
4394 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4401 string_list_destroy(&refglobs);
4405 usage(CMD_VERIFY, stderr);
4407 goto out_free_refglobs;
4410 struct imagex_command {
4412 int (*func)(int argc, tchar **argv, int cmd);
4415 static const struct imagex_command imagex_commands[] = {
4416 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4417 [CMD_APPLY] = {T("apply"), imagex_apply},
4418 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4419 [CMD_DELETE] = {T("delete"), imagex_delete},
4420 [CMD_DIR ] = {T("dir"), imagex_dir},
4421 [CMD_EXPORT] = {T("export"), imagex_export},
4422 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4423 [CMD_INFO] = {T("info"), imagex_info},
4424 [CMD_JOIN] = {T("join"), imagex_join},
4425 #if WIM_MOUNTING_SUPPORTED
4426 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4427 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4429 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4430 [CMD_SPLIT] = {T("split"), imagex_split},
4431 #if WIM_MOUNTING_SUPPORTED
4432 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4434 [CMD_UPDATE] = {T("update"), imagex_update},
4435 [CMD_VERIFY] = {T("verify"), imagex_verify},
4440 /* Can be a directory or source list file. But source list file is probably
4441 * a rare use case, so just say directory. */
4442 # define SOURCE_STR T("DIRECTORY")
4444 /* Can only be a directory */
4445 # define TARGET_STR T("DIRECTORY")
4448 /* Can be a directory, NTFS volume, or source list file. */
4449 # define SOURCE_STR T("SOURCE")
4451 /* Can be a directory or NTFS volume. */
4452 # define TARGET_STR T("TARGET")
4456 static const tchar * const usage_strings[] = {
4459 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4460 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4461 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4462 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4463 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4464 " [--dereference] [--snapshot] [--create]\n"
4468 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4469 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4470 " [--no-attributes] [--rpfix] [--norpfix]\n"
4471 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4472 " [--compact=FORMAT]\n"
4476 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4477 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4478 " [--config=FILE] [--threads=NUM_THREADS]\n"
4479 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4480 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4481 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4486 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4490 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4494 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4495 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4496 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4497 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4498 " [--wimboot] [--solid]\n"
4502 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4503 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4504 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4505 " [--no-attributes] [--include-invalid-names]\n"
4506 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4510 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4511 " [--boot] [--check] [--nocheck] [--xml]\n"
4512 " [--extract-xml FILE] [--header] [--blobs]\n"
4513 " [--image-property NAME=VALUE]\n"
4517 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4519 #if WIM_MOUNTING_SUPPORTED
4522 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4523 " [--check] [--streams-interface=INTERFACE]\n"
4524 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4528 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4529 " [--check] [--streams-interface=INTERFACE]\n"
4530 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4536 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4537 " [--check] [--nocheck] [--solid]\n"
4542 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4544 #if WIM_MOUNTING_SUPPORTED
4547 " %"TS" DIRECTORY\n"
4548 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4553 " %"TS" WIMFILE [IMAGE]\n"
4554 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4555 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4556 " [--command=STRING] [--wimboot-config=FILE]\n"
4561 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4565 static const tchar *invocation_name;
4566 static int invocation_cmd = CMD_NONE;
4568 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4570 static tchar buf[50];
4572 if (cmd == CMD_NONE)
4573 return T("wimlib-imagex");
4575 if (only_short_form || invocation_cmd != CMD_NONE) {
4576 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4578 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4579 imagex_commands[cmd].name);
4587 static const tchar * const fmt =
4589 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
4590 "Copyright (C) 2012-2018 Eric Biggers\n"
4591 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4592 "This is free software: you are free to change and redistribute it.\n"
4593 "There is NO WARRANTY, to the extent permitted by law.\n"
4595 "Report bugs to "PACKAGE_BUGREPORT".\n"
4597 tfprintf(stdout, fmt, wimlib_get_version_string());
4601 do_common_options(int *argc_p, tchar **argv, int cmd)
4607 for (i = 1; i < argc; i++) {
4609 if (p[0] == T('-') && p[1] == T('-')) {
4611 if (!tstrcmp(p, T("help"))) {
4612 if (cmd == CMD_NONE)
4617 } else if (!tstrcmp(p, T("version"))) {
4620 } else if (!tstrcmp(p, T("quiet"))) {
4621 imagex_suppress_output();
4622 memmove(&argv[i], &argv[i + 1],
4623 (argc - i) * sizeof(argv[i]));
4626 } else if (!*p) /* reached "--", no more options */
4635 print_usage_string(int cmd, FILE *fp)
4637 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4641 recommend_man_page(int cmd, FILE *fp)
4643 const tchar *format_str;
4645 format_str = T("Some uncommon options are not listed;\n"
4646 "See %"TS".pdf in the doc directory for more details.\n");
4648 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4650 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4654 usage(int cmd, FILE *fp)
4656 tfprintf(fp, T("Usage:\n"));
4657 print_usage_string(cmd, fp);
4658 tfprintf(fp, T("\n"));
4659 recommend_man_page(cmd, fp);
4665 tfprintf(fp, T("Usage:\n"));
4666 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4667 print_usage_string(cmd, fp);
4668 tfprintf(fp, T("\n"));
4670 static const tchar * const extra =
4673 " %"TS" --version\n"
4676 tfprintf(fp, extra, invocation_name, invocation_name);
4678 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4679 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4680 "For some commands IMAGE may be \"all\".\n"
4682 recommend_man_page(CMD_NONE, fp);
4686 extern int wmain(int argc, wchar_t **argv);
4690 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4691 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4692 * something else), while on Windows the command arguments will be UTF-16LE
4693 * encoded 'wchar_t' strings. */
4695 main(int argc, tchar **argv)
4701 imagex_info_file = stdout;
4702 invocation_name = tbasename(argv[0]);
4705 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4706 if (igcase != NULL) {
4707 if (!tstrcmp(igcase, T("no")) ||
4708 !tstrcmp(igcase, T("0")))
4709 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4710 else if (!tstrcmp(igcase, T("yes")) ||
4711 !tstrcmp(igcase, T("1")))
4712 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4715 "WARNING: Ignoring unknown setting of "
4716 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4721 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4723 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4724 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4725 for (int i = 0; i < CMD_MAX; i++) {
4726 if (!tstrcmp(invocation_name + 3,
4727 imagex_commands[i].name))
4736 /* Unless already known from the invocation name, determine which
4737 * command was specified. */
4738 if (cmd == CMD_NONE) {
4740 imagex_error(T("No command specified!\n"));
4744 for (int i = 0; i < CMD_MAX; i++) {
4745 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4750 if (cmd != CMD_NONE) {
4756 /* Handle common options. May exit early (for --help or --version). */
4757 do_common_options(&argc, argv, cmd);
4759 /* Bail if a valid command was not specified. */
4760 if (cmd == CMD_NONE) {
4761 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4766 /* Enable warning and error messages in wimlib to be more user-friendly.
4768 wimlib_set_print_errors(true);
4770 /* Initialize wimlib. */
4771 ret = wimlib_global_init(init_flags);
4773 goto out_check_status;
4775 /* Call the command handler function. */
4776 ret = imagex_commands[cmd].func(argc, argv, cmd);
4778 /* Check for error writing to standard output, especially since for some
4779 * commands, writing to standard output is part of the program's actual
4780 * behavior and not just for informational purposes. */
4781 if (ferror(stdout) || fclose(stdout)) {
4782 imagex_error_with_errno(T("error writing to standard output"));
4787 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4788 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4789 * error code from which an error message can be printed. */
4791 imagex_error(T("Exiting with error code %d:\n"
4793 wimlib_get_error_string(ret));
4794 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4795 imagex_error_with_errno(T("errno"));
4797 /* Make wimlib free any resources it's holding (although this is not
4798 * strictly necessary because the process is ending anyway). */
4799 wimlib_global_cleanup();