4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012-2018 Eric Biggers
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # include "config.h" /* Need for PACKAGE_VERSION, etc. */
30 #include "wimlib_tchar.h"
49 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
52 # include "imagex-win32.h"
53 # define print_security_descriptor win32_print_security_descriptor
56 # include <langinfo.h>
57 # define print_security_descriptor default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
61 /* NetBSD is missing getopt_long_only() but has getopt_long() */
62 #ifndef HAVE_GETOPT_LONG_ONLY
63 # define getopt_long_only getopt_long
67 /* Don't confuse the user by presenting the mounting commands on Windows when
68 * they will never work. However on UNIX-like systems we always present them,
69 * even if WITH_FUSE is not defined at this point, as to not tie the build of
70 * wimlib-imagex to a specific build of wimlib. */
72 # define WIM_MOUNTING_SUPPORTED 0
74 # define WIM_MOUNTING_SUPPORTED 1
77 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
80 is_any_path_separator(tchar c)
82 return c == T('/') || c == T('\\');
85 /* Like basename(), but handles both forward and backwards slashes. */
87 tbasename(tchar *path)
89 tchar *p = tstrchr(path, T('\0'));
94 if (!is_any_path_separator(*--p))
102 if (is_any_path_separator(*--p))
107 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
121 #if WIM_MOUNTING_SUPPORTED
127 #if WIM_MOUNTING_SUPPORTED
135 static void usage(int cmd, FILE *fp);
136 static void usage_all(FILE *fp);
137 static void recommend_man_page(int cmd, FILE *fp);
138 static const tchar *get_cmd_string(int cmd, bool only_short_form);
140 static FILE *imagex_info_file;
142 #define imagex_printf(format, ...) \
143 if (imagex_info_file) \
144 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
146 static void imagex_suppress_output(void)
148 imagex_info_file = NULL;
151 static void imagex_output_to_stderr(void)
153 if (imagex_info_file)
154 imagex_info_file = stderr;
157 static void imagex_flush_output(void)
159 if (imagex_info_file)
160 fflush(imagex_info_file);
164 IMAGEX_ALLOW_OTHER_OPTION,
168 IMAGEX_CHUNK_SIZE_OPTION,
169 IMAGEX_COMMAND_OPTION,
170 IMAGEX_COMMIT_OPTION,
171 IMAGEX_COMPACT_OPTION,
172 IMAGEX_COMPRESS_OPTION,
173 IMAGEX_CONFIG_OPTION,
174 IMAGEX_CREATE_OPTION,
176 IMAGEX_DELTA_FROM_OPTION,
177 IMAGEX_DEREFERENCE_OPTION,
178 IMAGEX_DEST_DIR_OPTION,
179 IMAGEX_DETAILED_OPTION,
180 IMAGEX_EXTRACT_XML_OPTION,
183 IMAGEX_HEADER_OPTION,
184 IMAGEX_IMAGE_PROPERTY_OPTION,
185 IMAGEX_INCLUDE_INTEGRITY_OPTION,
186 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
188 IMAGEX_METADATA_OPTION,
189 IMAGEX_NEW_IMAGE_OPTION,
190 IMAGEX_NOCHECK_OPTION,
191 IMAGEX_NORPFIX_OPTION,
192 IMAGEX_NOT_PIPABLE_OPTION,
193 IMAGEX_NO_ACLS_OPTION,
194 IMAGEX_NO_ATTRIBUTES_OPTION,
195 IMAGEX_NO_GLOBS_OPTION,
196 IMAGEX_NO_REPLACE_OPTION,
197 IMAGEX_NO_SOLID_SORT_OPTION,
198 IMAGEX_NULLGLOB_OPTION,
199 IMAGEX_ONE_FILE_ONLY_OPTION,
201 IMAGEX_PIPABLE_OPTION,
202 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
203 IMAGEX_REBUILD_OPTION,
204 IMAGEX_RECOMPRESS_OPTION,
205 IMAGEX_RECURSIVE_OPTION,
208 IMAGEX_SNAPSHOT_OPTION,
210 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
211 IMAGEX_SOLID_COMPRESS_OPTION,
213 IMAGEX_SOURCE_LIST_OPTION,
214 IMAGEX_STAGING_DIR_OPTION,
215 IMAGEX_STREAMS_INTERFACE_OPTION,
216 IMAGEX_STRICT_ACLS_OPTION,
217 IMAGEX_THREADS_OPTION,
218 IMAGEX_TO_STDOUT_OPTION,
219 IMAGEX_UNIX_DATA_OPTION,
220 IMAGEX_UNSAFE_COMPACT_OPTION,
221 IMAGEX_UPDATE_OF_OPTION,
222 IMAGEX_VERBOSE_OPTION,
223 IMAGEX_WIMBOOT_CONFIG_OPTION,
224 IMAGEX_WIMBOOT_OPTION,
228 static const struct option apply_options[] = {
229 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
230 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
231 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
232 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
233 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
234 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
235 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
236 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
237 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
238 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
239 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
240 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
241 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
245 static const struct option capture_or_append_options[] = {
246 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
247 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
248 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
249 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
250 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
251 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
252 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
253 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
254 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
255 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
256 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
257 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
258 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
259 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
260 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
261 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
262 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
263 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
264 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
265 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
266 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
267 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
268 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
269 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
270 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
271 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
272 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
273 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
274 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
275 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
276 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
277 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
278 {T("create"), no_argument, NULL, IMAGEX_CREATE_OPTION},
282 static const struct option delete_options[] = {
283 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
284 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
285 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
286 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
290 static const struct option dir_options[] = {
291 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
292 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
293 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
294 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
298 static const struct option export_options[] = {
299 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
300 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
301 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
302 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
303 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
304 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
305 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
306 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
307 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
308 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
309 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
310 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
311 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
312 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
313 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
314 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
315 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
316 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
317 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
321 static const struct option extract_options[] = {
322 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
323 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
324 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
325 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
326 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
327 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
328 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
329 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
330 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
331 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
332 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
333 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
334 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
335 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
336 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
337 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
338 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
342 static const struct option info_options[] = {
343 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
344 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
345 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
346 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
347 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
348 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
349 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
350 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
351 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
352 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
353 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
357 static const struct option join_options[] = {
358 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
359 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
363 #if WIM_MOUNTING_SUPPORTED
364 static const struct option mount_options[] = {
365 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
366 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
367 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
368 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
369 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
370 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
371 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
376 static const struct option optimize_options[] = {
377 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
378 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
379 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
380 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
381 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
382 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
383 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
384 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
385 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
386 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
387 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
388 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
389 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
390 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
391 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
395 static const struct option split_options[] = {
396 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
397 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
401 #if WIM_MOUNTING_SUPPORTED
402 static const struct option unmount_options[] = {
403 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
404 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
405 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
406 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
407 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
408 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
413 static const struct option update_options[] = {
414 /* Careful: some of the options here set the defaults for update
415 * commands, but the flags given to an actual update command (and not to
416 * `imagex update' itself are also handled in
417 * update_command_add_option(). */
418 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
419 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
420 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
421 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
422 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
423 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
425 /* Default delete options */
426 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
427 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
429 /* Global add option */
430 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
432 /* Default add options */
433 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
434 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
435 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
436 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
437 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
438 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
439 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
440 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
445 static const struct option verify_options[] = {
446 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
447 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
453 # define _format_attribute(type, format_str, args_start) \
454 __attribute__((format(type, format_str, args_start)))
456 # define _format_attribute(type, format_str, args_start)
459 /* Print formatted error message to stderr. */
460 static void _format_attribute(printf, 1, 2)
461 imagex_error(const tchar *format, ...)
464 va_start(va, format);
465 tfputs(T("ERROR: "), stderr);
466 tvfprintf(stderr, format, va);
467 tputc(T('\n'), stderr);
471 /* Print formatted error message to stderr. */
472 static void _format_attribute(printf, 1, 2)
473 imagex_error_with_errno(const tchar *format, ...)
475 int errno_save = errno;
477 va_start(va, format);
478 tfputs(T("ERROR: "), stderr);
479 tvfprintf(stderr, format, va);
480 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
485 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
487 if (image == WIMLIB_NO_IMAGE) {
488 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
489 " Please specify a 1-based image index or "
490 "image name. To list the images\n"
491 " contained in the WIM archive, run\n"
493 " %"TS" \"%"TS"\"\n"),
494 image_name, wim_name,
495 get_cmd_string(CMD_INFO, false), wim_name);
496 return WIMLIB_ERR_INVALID_IMAGE;
502 verify_image_is_single(int image)
504 if (image == WIMLIB_ALL_IMAGES) {
505 imagex_error(T("Cannot specify all images for this action!"));
506 return WIMLIB_ERR_INVALID_IMAGE;
512 verify_image_exists_and_is_single(int image, const tchar *image_name,
513 const tchar *wim_name)
516 ret = verify_image_exists(image, image_name, wim_name);
518 ret = verify_image_is_single(image);
523 print_available_compression_types(FILE *fp)
525 static const tchar * const s =
527 "Available compression types:\n"
530 " xpress (alias: \"fast\")\n"
531 " lzx (alias: \"maximum\") (default for capture)\n"
532 " lzms (alias: \"recovery\")\n"
538 /* Parse the argument to --compress or --solid-compress */
540 get_compression_type(tchar *optarg, bool solid)
543 unsigned int compression_level = 0;
546 plevel = tstrchr(optarg, T(':'));
552 ultmp = tstrtoul(plevel, &ptmp, 10);
553 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
554 imagex_error(T("Compression level must be a positive integer! "
555 "e.g. --compress=lzx:80"));
556 return WIMLIB_COMPRESSION_TYPE_INVALID;
558 compression_level = ultmp;
561 if (!tstrcasecmp(optarg, T("maximum")) ||
562 !tstrcasecmp(optarg, T("lzx")) ||
563 !tstrcasecmp(optarg, T("max"))) {
564 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
565 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
566 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
567 } else if (!tstrcasecmp(optarg, T("recovery"))) {
571 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
572 " differently from DISM. Instead, you typically want to use '--solid' to\n"
573 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
574 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
575 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
576 " of '--compress=recovery'.\n"));
578 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
579 } else if (!tstrcasecmp(optarg, T("lzms"))) {
580 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
581 } else if (!tstrcasecmp(optarg, T("none"))) {
582 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
584 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
585 print_available_compression_types(stderr);
586 return WIMLIB_COMPRESSION_TYPE_INVALID;
589 if (compression_level != 0)
590 wimlib_set_default_compression_level(ctype, compression_level);
594 /* Parse the argument to --compact */
596 set_compact_mode(const tchar *arg, int *extract_flags)
599 if (!tstrcasecmp(arg, T("xpress4k")))
600 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
601 else if (!tstrcasecmp(arg, T("xpress8k")))
602 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
603 else if (!tstrcasecmp(arg, T("xpress16k")))
604 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
605 else if (!tstrcasecmp(arg, T("lzx")))
606 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
609 *extract_flags |= flag;
614 "\"%"TS"\" is not a recognized System Compression format. The options are:"
616 " --compact=xpress4k\n"
617 " --compact=xpress8k\n"
618 " --compact=xpress16k\n"
627 unsigned num_strings;
628 unsigned num_alloc_strings;
631 #define STRING_LIST_INITIALIZER \
632 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
634 #define STRING_LIST(_strings) \
635 struct string_list _strings = STRING_LIST_INITIALIZER
638 string_list_append(struct string_list *list, tchar *glob)
640 unsigned num_alloc_strings = list->num_alloc_strings;
642 if (list->num_strings == num_alloc_strings) {
645 num_alloc_strings += 4;
646 new_strings = realloc(list->strings,
647 sizeof(list->strings[0]) * num_alloc_strings);
649 imagex_error(T("Out of memory!"));
652 list->strings = new_strings;
653 list->num_alloc_strings = num_alloc_strings;
655 list->strings[list->num_strings++] = glob;
660 string_list_destroy(struct string_list *list)
666 wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags)
668 return wimlib_reference_resource_files(wim, (const tchar **)list->strings,
670 WIMLIB_REF_FLAG_GLOB_ENABLE,
675 append_image_property_argument(struct string_list *image_properties)
677 if (!tstrchr(optarg, '=')) {
678 imagex_error(T("'--image-property' argument "
679 "must be in the form NAME=VALUE"));
682 return string_list_append(image_properties, optarg);
686 apply_image_properties(struct string_list *image_properties,
687 WIMStruct *wim, int image, bool *any_changes_ret)
689 bool any_changes = false;
690 for (unsigned i = 0; i < image_properties->num_strings; i++) {
692 const tchar *current_value;
695 name = image_properties->strings[i];
696 value = tstrchr(name, '=');
699 current_value = wimlib_get_image_property(wim, image, name);
700 if (current_value && !tstrcmp(current_value, value)) {
701 imagex_printf(T("The %"TS" property of image %d "
702 "already has value \"%"TS"\".\n"),
705 imagex_printf(T("Setting the %"TS" property of image "
706 "%d to \"%"TS"\".\n"),
708 ret = wimlib_set_image_property(wim, image, name, value);
715 *any_changes_ret = any_changes;
720 do_resource_not_found_warning(const tchar *wimfile,
721 const struct wimlib_wim_info *info,
722 const struct string_list *refglobs)
724 if (info->total_parts > 1) {
725 if (refglobs->num_strings == 0) {
726 imagex_error(T("\"%"TS"\" is part of a split WIM. "
727 "Use --ref to specify the other parts."),
730 imagex_error(T("Perhaps the '--ref' argument did not "
731 "specify all other parts of the split "
735 imagex_error(T("If this is a delta WIM, use the --ref argument "
736 "to specify the WIM(s) on which it is based."));
741 do_metadata_not_found_warning(const tchar *wimfile,
742 const struct wimlib_wim_info *info)
744 if (info->part_number != 1) {
745 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
746 " You must specify the first part."),
751 /* Returns the size of a file given its name, or -1 if the file does not exist
752 * or its size cannot be determined. */
754 file_get_size(const tchar *filename)
757 if (tstat(filename, &st) == 0)
764 PARSE_STRING_SUCCESS = 0,
765 PARSE_STRING_FAILURE = 1,
766 PARSE_STRING_NONE = 2,
770 * Parses a string token from an array of characters.
772 * Tokens are either whitespace-delimited, or double or single-quoted.
774 * @line_p: Pointer to the pointer to the line of data. Will be updated
775 * to point past the string token iff the return value is
776 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
779 * @len_p: @len_p initially stores the length of the line of data, which may
780 * be 0, and it will be updated to the number of bytes remaining in
781 * the line iff the return value is PARSE_STRING_SUCCESS.
783 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
784 * parsed string token will be returned here.
786 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
787 * PARSE_STRING_FAILURE if the data was invalid due to a missing
788 * closing quote; or PARSE_STRING_NONE if the line ended before the
789 * beginning of a string token was found.
792 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
795 tchar *line = *line_p;
799 /* Skip leading whitespace */
802 return PARSE_STRING_NONE;
803 if (!istspace(*line) && *line != T('\0'))
809 if (quote_char == T('"') || quote_char == T('\'')) {
814 line = tmemchr(line, quote_char, len);
816 imagex_error(T("Missing closing quote: %"TS), fn - 1);
817 return PARSE_STRING_FAILURE;
820 /* Unquoted string. Go until whitespace. Line is terminated
821 * by '\0', so no need to check 'len'. */
825 } while (!istspace(*line) && *line != T('\0'));
832 return PARSE_STRING_SUCCESS;
835 /* Parses a line of data (not an empty line or comment) in the source list file
836 * format. (See the man page for 'wimlib-imagex capture' for details on this
837 * format and the meaning.)
839 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
840 * len == 0. The data in @line will be modified by this function call.
842 * @len: Length of the line of data.
844 * @source: On success, the capture source and target described by the line is
845 * written into this destination. Note that it will contain pointers
846 * to data in the @line array.
848 * Returns true if the line was valid; false otherwise. */
850 parse_source_list_line(tchar *line, size_t len,
851 struct wimlib_capture_source *source)
855 ret = parse_string(&line, &len, &source->fs_source_path);
856 if (ret != PARSE_STRING_SUCCESS)
858 ret = parse_string(&line, &len, &source->wim_target_path);
859 if (ret == PARSE_STRING_NONE)
860 source->wim_target_path = source->fs_source_path;
861 return ret != PARSE_STRING_FAILURE;
864 /* Returns %true if the given line of length @len > 0 is a comment or empty line
865 * in the source list file format. */
867 is_comment_line(const tchar *line, size_t len)
870 if (*line == T('#') || *line == T(';'))
872 if (!istspace(*line) && *line != T('\0'))
882 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
885 tchar *contents = *contents_p;
886 size_t nchars = *nchars_p;
889 for (i = 0; i < nchars; i++)
890 if (contents[i] == T('\n'))
893 /* Handle last line not terminated by a newline */
894 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
895 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
897 imagex_error(T("Out of memory!"));
900 contents[nchars] = T('\n');
901 *contents_p = contents;
909 /* Parses a file in the source list format. (See the man page for
910 * 'wimlib-imagex capture' for details on this format and the meaning.)
912 * @source_list_contents: Contents of the source list file. Note that this
913 * buffer will be modified to save memory allocations,
914 * and cannot be freed until the returned array of
915 * wimlib_capture_source's has also been freed.
917 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
920 * @nsources_ret: On success, the length of the returned array is
923 * Returns: An array of `struct wimlib_capture_source's that can be passed to
924 * the wimlib_add_image_multisource() function to specify how a WIM image is to
926 static struct wimlib_capture_source *
927 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
928 size_t *nsources_ret)
932 struct wimlib_capture_source *sources;
935 nlines = text_file_count_lines(source_list_contents_p,
936 &source_list_nchars);
940 /* Always allocate at least 1 slot, just in case the implementation of
941 * calloc() returns NULL if 0 bytes are requested. */
942 sources = calloc(nlines ?: 1, sizeof(*sources));
944 imagex_error(T("out of memory"));
947 p = *source_list_contents_p;
949 for (i = 0; i < nlines; i++) {
950 /* XXX: Could use rawmemchr() here instead, but it may not be
951 * available on all platforms. */
952 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
953 size_t len = endp - p + 1;
955 if (!is_comment_line(p, len)) {
956 if (!parse_source_list_line(p, len, &sources[j++])) {
968 /* Reads the contents of a file into memory. */
970 file_get_contents(const tchar *filename, size_t *len_ret)
977 if (tstat(filename, &stbuf) != 0) {
978 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
983 fp = tfopen(filename, T("rb"));
985 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
989 buf = malloc(len ? len : 1);
991 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
992 "contents of file \"%"TS"\""), len, filename);
995 if (fread(buf, 1, len, fp) != len) {
996 imagex_error_with_errno(T("Failed to read %zu bytes from the "
997 "file \"%"TS"\""), len, filename);
1011 /* Read standard input until EOF and return the full contents in a malloc()ed
1012 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1015 stdin_get_contents(size_t *len_ret)
1017 /* stdin can, of course, be a pipe or other non-seekable file, so the
1018 * total length of the data cannot be pre-determined */
1020 size_t newlen = 1024;
1024 char *p = realloc(buf, newlen);
1025 size_t bytes_read, bytes_to_read;
1027 imagex_error(T("out of memory while reading stdin"));
1031 bytes_to_read = newlen - pos;
1032 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1034 if (bytes_read != bytes_to_read) {
1039 imagex_error_with_errno(T("error reading stdin"));
1053 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1056 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1058 *num_tchars_ret = num_bytes;
1060 #else /* !__WIN32__ */
1061 /* On Windows, translate the text to UTF-16LE */
1065 if (num_bytes >= 2 &&
1066 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1067 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1069 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1070 * with something that looks like an ASCII character encoded as
1071 * a UTF-16LE code unit. Assume the file is encoded as
1072 * UTF-16LE. This is not a 100% reliable check. */
1073 num_wchars = num_bytes / 2;
1074 text_wstr = (wchar_t*)text;
1076 /* File does not look like UTF-16LE. Assume it is encoded in
1077 * the current Windows code page. I think these are always
1078 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1079 * should work as expected. */
1080 text_wstr = win32_mbs_to_wcs(text,
1085 *num_tchars_ret = num_wchars;
1087 #endif /* __WIN32__ */
1091 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1096 contents = file_get_contents(filename, &num_bytes);
1099 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1103 stdin_get_text_contents(size_t *num_tchars_ret)
1108 contents = stdin_get_contents(&num_bytes);
1111 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1114 #define TO_PERCENT(numerator, denominator) \
1115 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1117 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1118 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1119 #define KIBIBYTE_MIN_NBYTES 10000ULL
1122 get_unit(uint64_t total_bytes, const tchar **name_ret)
1124 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1125 *name_ret = T("GiB");
1127 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1128 *name_ret = T("MiB");
1130 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1131 *name_ret = T("KiB");
1134 *name_ret = T("bytes");
1139 static struct wimlib_progress_info_scan last_scan_progress;
1142 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1144 uint64_t prev_count, cur_count;
1146 prev_count = last_scan_progress.num_nondirs_scanned +
1147 last_scan_progress.num_dirs_scanned;
1148 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1150 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1151 cur_count % 128 == 0)
1153 unsigned unit_shift;
1154 const tchar *unit_name;
1156 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1157 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1158 "%"PRIu64" directories) "),
1159 scan->num_bytes_scanned >> unit_shift,
1161 scan->num_nondirs_scanned,
1162 scan->num_dirs_scanned);
1163 last_scan_progress = *scan;
1166 /* Progress callback function passed to various wimlib functions. */
1167 static enum wimlib_progress_status
1168 imagex_progress_func(enum wimlib_progress_msg msg,
1169 union wimlib_progress_info *info,
1170 void *_ignored_context)
1172 unsigned percent_done;
1173 unsigned unit_shift;
1174 const tchar *unit_name;
1177 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1179 static bool started;
1181 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1182 imagex_printf(T("Using %"TS" compression "
1183 "with %u thread%"TS"\n"),
1184 wimlib_get_compression_type_string(
1185 info->write_streams.compression_type),
1186 info->write_streams.num_threads,
1187 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1192 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1193 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1194 info->write_streams.total_bytes);
1196 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1197 info->write_streams.completed_bytes >> unit_shift,
1199 info->write_streams.total_bytes >> unit_shift,
1202 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1203 imagex_printf(T("\n"));
1205 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1206 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1207 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1208 imagex_printf(T("\n"));
1210 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1211 info->scan.wim_target_path);
1213 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1215 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1216 switch (info->scan.status) {
1217 case WIMLIB_SCAN_DENTRY_OK:
1218 report_scan_progress(&info->scan, false);
1220 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1221 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1223 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1224 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1225 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1227 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1228 /* Symlink fixups are enabled by default. This is
1229 * mainly intended for Windows, which for some reason
1230 * uses absolute junctions (with drive letters!) in the
1231 * default installation. On UNIX-like systems, warn the
1232 * user when fixing the target of an absolute symbolic
1233 * link, so they know to disable this if they want. */
1235 imagex_printf(T("\nWARNING: Adjusted target of "
1236 "absolute symbolic link \"%"TS"\"\n"
1237 " (Use --norpfix to capture "
1238 "absolute symbolic links as-is)\n"),
1239 info->scan.cur_path);
1246 case WIMLIB_PROGRESS_MSG_SCAN_END:
1247 report_scan_progress(&info->scan, true);
1248 imagex_printf(T("\n"));
1250 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1251 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1252 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1253 info->integrity.total_bytes);
1254 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1255 "of %"PRIu64" %"TS" (%u%%) done"),
1256 info->integrity.filename,
1257 info->integrity.completed_bytes >> unit_shift,
1259 info->integrity.total_bytes >> unit_shift,
1262 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1263 imagex_printf(T("\n"));
1265 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1266 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1267 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1268 info->integrity.total_bytes);
1269 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1270 "of %"PRIu64" %"TS" (%u%%) done"),
1271 info->integrity.completed_bytes >> unit_shift,
1273 info->integrity.total_bytes >> unit_shift,
1276 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1277 imagex_printf(T("\n"));
1279 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1280 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1281 "to %"TS" \"%"TS"\"\n"),
1282 info->extract.image,
1283 info->extract.image_name,
1284 info->extract.wimfile_name,
1285 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1286 T("NTFS volume") : T("directory")),
1287 info->extract.target);
1289 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1290 if (info->extract.end_file_count >= 2000) {
1291 percent_done = TO_PERCENT(info->extract.current_file_count,
1292 info->extract.end_file_count);
1293 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1294 info->extract.current_file_count,
1295 info->extract.end_file_count, percent_done);
1296 if (info->extract.current_file_count == info->extract.end_file_count)
1297 imagex_printf(T("\n"));
1300 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1301 percent_done = TO_PERCENT(info->extract.completed_bytes,
1302 info->extract.total_bytes);
1303 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1304 imagex_printf(T("\rExtracting file data: "
1305 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1306 info->extract.completed_bytes >> unit_shift,
1308 info->extract.total_bytes >> unit_shift,
1311 if (info->extract.completed_bytes >= info->extract.total_bytes)
1312 imagex_printf(T("\n"));
1314 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1315 if (info->extract.end_file_count >= 2000) {
1316 percent_done = TO_PERCENT(info->extract.current_file_count,
1317 info->extract.end_file_count);
1318 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1319 info->extract.current_file_count,
1320 info->extract.end_file_count, percent_done);
1321 if (info->extract.current_file_count == info->extract.end_file_count)
1322 imagex_printf(T("\n"));
1325 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1326 if (info->extract.total_parts != 1) {
1327 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1328 info->extract.part_number,
1329 info->extract.total_parts);
1332 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1333 percent_done = TO_PERCENT(info->split.completed_bytes,
1334 info->split.total_bytes);
1335 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1336 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1337 "%"PRIu64" %"TS" (%u%%) written\n"),
1338 info->split.part_name,
1339 info->split.cur_part_number,
1340 info->split.total_parts,
1341 info->split.completed_bytes >> unit_shift,
1343 info->split.total_bytes >> unit_shift,
1347 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1348 if (info->split.completed_bytes == info->split.total_bytes) {
1349 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1350 info->split.cur_part_number,
1351 info->split.total_parts);
1354 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1355 switch (info->update.command->op) {
1356 case WIMLIB_UPDATE_OP_DELETE:
1357 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1358 info->update.command->delete_.wim_path);
1360 case WIMLIB_UPDATE_OP_RENAME:
1361 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1362 info->update.command->rename.wim_source_path,
1363 info->update.command->rename.wim_target_path);
1365 case WIMLIB_UPDATE_OP_ADD:
1370 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1371 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1372 info->replace.path_in_wim);
1374 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1375 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1376 info->wimboot_exclude.path_in_wim);
1378 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1379 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1380 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1381 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1382 info->unmount.mounted_wim,
1383 info->unmount.mounted_image);
1385 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1386 info->unmount.mounted_wim,
1387 info->unmount.mounted_image);
1388 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1392 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1393 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1394 info->verify_image.current_image,
1395 info->verify_image.total_images);
1397 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1398 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1399 info->verify_streams.total_bytes);
1400 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1401 imagex_printf(T("\rVerifying file data: "
1402 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1403 info->verify_streams.completed_bytes >> unit_shift,
1405 info->verify_streams.total_bytes >> unit_shift,
1408 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1409 imagex_printf(T("\n"));
1414 imagex_flush_output();
1415 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1419 parse_num_threads(const tchar *optarg)
1422 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1423 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1424 imagex_error(T("Number of threads must be a non-negative integer!"));
1432 parse_chunk_size(const tchar *optarg)
1435 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1436 if (chunk_size == 0) {
1437 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1438 " with optional K, M, or G suffix"));
1442 if (*tmp == T('k') || *tmp == T('K')) {
1445 } else if (*tmp == T('m') || *tmp == T('M')) {
1448 } else if (*tmp == T('g') || *tmp == T('G')) {
1452 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1453 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1457 if (chunk_size >= UINT32_MAX) {
1458 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1466 * Parse an option passed to an update command.
1468 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1471 * @option: Text string for the option (beginning with --)
1473 * @cmd: `struct wimlib_update_command' that is being constructed for
1476 * Returns true if the option was recognized; false if not.
1479 update_command_add_option(int op, const tchar *option,
1480 struct wimlib_update_command *cmd)
1482 bool recognized = true;
1484 case WIMLIB_UPDATE_OP_ADD:
1485 if (!tstrcmp(option, T("--verbose")))
1486 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1487 else if (!tstrcmp(option, T("--unix-data")))
1488 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1489 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1490 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1491 else if (!tstrcmp(option, T("--strict-acls")))
1492 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1493 else if (!tstrcmp(option, T("--dereference")))
1494 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1495 else if (!tstrcmp(option, T("--no-replace")))
1496 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1500 case WIMLIB_UPDATE_OP_DELETE:
1501 if (!tstrcmp(option, T("--force")))
1502 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1503 else if (!tstrcmp(option, T("--recursive")))
1504 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1515 /* How many nonoption arguments each `imagex update' command expects */
1516 static const unsigned update_command_num_nonoptions[] = {
1517 [WIMLIB_UPDATE_OP_ADD] = 2,
1518 [WIMLIB_UPDATE_OP_DELETE] = 1,
1519 [WIMLIB_UPDATE_OP_RENAME] = 2,
1523 update_command_add_nonoption(int op, const tchar *nonoption,
1524 struct wimlib_update_command *cmd,
1525 unsigned num_nonoptions)
1528 case WIMLIB_UPDATE_OP_ADD:
1529 if (num_nonoptions == 0)
1530 cmd->add.fs_source_path = (tchar*)nonoption;
1532 cmd->add.wim_target_path = (tchar*)nonoption;
1534 case WIMLIB_UPDATE_OP_DELETE:
1535 cmd->delete_.wim_path = (tchar*)nonoption;
1537 case WIMLIB_UPDATE_OP_RENAME:
1538 if (num_nonoptions == 0)
1539 cmd->rename.wim_source_path = (tchar*)nonoption;
1541 cmd->rename.wim_target_path = (tchar*)nonoption;
1547 * Parse a command passed on stdin to `imagex update'.
1549 * @line: Text of the command.
1550 * @len: Length of the line, including a null terminator
1553 * @command: A `struct wimlib_update_command' to fill in from the parsed
1556 * @line_number: Line number of the command, for diagnostics.
1558 * Returns true on success; returns false on parse error.
1561 parse_update_command(tchar *line, size_t len,
1562 struct wimlib_update_command *command,
1566 tchar *command_name;
1568 size_t num_nonoptions;
1570 /* Get the command name ("add", "delete", "rename") */
1571 ret = parse_string(&line, &len, &command_name);
1572 if (ret != PARSE_STRING_SUCCESS)
1575 if (!tstrcasecmp(command_name, T("add"))) {
1576 op = WIMLIB_UPDATE_OP_ADD;
1577 } else if (!tstrcasecmp(command_name, T("delete"))) {
1578 op = WIMLIB_UPDATE_OP_DELETE;
1579 } else if (!tstrcasecmp(command_name, T("rename"))) {
1580 op = WIMLIB_UPDATE_OP_RENAME;
1582 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1583 command_name, line_number);
1588 /* Parse additional options and non-options as needed */
1593 ret = parse_string(&line, &len, &next_string);
1594 if (ret == PARSE_STRING_NONE) /* End of line */
1596 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1598 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1600 if (!update_command_add_option(op, next_string, command))
1602 imagex_error(T("Unrecognized option \"%"TS"\" to "
1603 "update command \"%"TS"\" on line %zu"),
1604 next_string, command_name, line_number);
1610 if (num_nonoptions == update_command_num_nonoptions[op])
1612 imagex_error(T("Unexpected argument \"%"TS"\" in "
1613 "update command on line %zu\n"
1614 " (The \"%"TS"\" command only "
1615 "takes %zu nonoption arguments!)\n"),
1616 next_string, line_number,
1617 command_name, num_nonoptions);
1620 update_command_add_nonoption(op, next_string,
1621 command, num_nonoptions);
1626 if (num_nonoptions != update_command_num_nonoptions[op]) {
1627 imagex_error(T("Not enough arguments to update command "
1628 "\"%"TS"\" on line %zu"), command_name, line_number);
1634 static struct wimlib_update_command *
1635 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1636 size_t *num_cmds_ret)
1640 struct wimlib_update_command *cmds;
1643 nlines = text_file_count_lines(cmd_file_contents_p,
1648 /* Always allocate at least 1 slot, just in case the implementation of
1649 * calloc() returns NULL if 0 bytes are requested. */
1650 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1652 imagex_error(T("out of memory"));
1655 p = *cmd_file_contents_p;
1657 for (i = 0; i < nlines; i++) {
1658 /* XXX: Could use rawmemchr() here instead, but it may not be
1659 * available on all platforms. */
1660 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1661 size_t len = endp - p + 1;
1663 if (!is_comment_line(p, len)) {
1664 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1675 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1676 * one image from a WIM file to an NTFS volume. */
1678 imagex_apply(int argc, tchar **argv, int cmd)
1682 int image = WIMLIB_NO_IMAGE;
1684 struct wimlib_wim_info info;
1686 const tchar *wimfile;
1687 const tchar *target;
1688 const tchar *image_num_or_name = NULL;
1689 int extract_flags = 0;
1691 STRING_LIST(refglobs);
1693 for_opt(c, apply_options) {
1695 case IMAGEX_CHECK_OPTION:
1696 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1698 case IMAGEX_VERBOSE_OPTION:
1699 /* No longer does anything. */
1701 case IMAGEX_REF_OPTION:
1702 ret = string_list_append(&refglobs, optarg);
1704 goto out_free_refglobs;
1706 case IMAGEX_UNIX_DATA_OPTION:
1707 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1709 case IMAGEX_NO_ACLS_OPTION:
1710 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1712 case IMAGEX_STRICT_ACLS_OPTION:
1713 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1715 case IMAGEX_NO_ATTRIBUTES_OPTION:
1716 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1718 case IMAGEX_NORPFIX_OPTION:
1719 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1721 case IMAGEX_RPFIX_OPTION:
1722 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1724 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1725 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1726 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1728 case IMAGEX_WIMBOOT_OPTION:
1729 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1731 case IMAGEX_COMPACT_OPTION:
1732 ret = set_compact_mode(optarg, &extract_flags);
1734 goto out_free_refglobs;
1742 if (argc != 2 && argc != 3)
1747 if (!tstrcmp(wimfile, T("-"))) {
1748 /* Attempt to apply pipable WIM from standard input. */
1750 image_num_or_name = NULL;
1753 image_num_or_name = argv[1];
1758 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1759 imagex_progress_func, NULL);
1761 goto out_free_refglobs;
1763 wimlib_get_wim_info(wim, &info);
1766 /* Image explicitly specified. */
1767 image_num_or_name = argv[1];
1768 image = wimlib_resolve_image(wim, image_num_or_name);
1769 ret = verify_image_exists(image, image_num_or_name, wimfile);
1771 goto out_wimlib_free;
1774 /* No image specified; default to image 1, but only if the WIM
1775 * contains exactly one image. */
1777 if (info.image_count != 1) {
1778 imagex_error(T("\"%"TS"\" contains %d images; "
1779 "Please select one (or all)."),
1780 wimfile, info.image_count);
1789 if (refglobs.num_strings) {
1791 imagex_error(T("Can't specify --ref when applying from stdin!"));
1793 goto out_wimlib_free;
1795 ret = wim_reference_globs(wim, &refglobs, open_flags);
1797 goto out_wimlib_free;
1802 /* Interpret a regular file or block device target as an NTFS
1806 if (tstat(target, &stbuf)) {
1807 if (errno != ENOENT) {
1808 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1811 goto out_wimlib_free;
1814 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1815 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1821 ret = wimlib_extract_image(wim, image, target, extract_flags);
1823 set_fd_to_binary_mode(STDIN_FILENO);
1824 ret = wimlib_extract_image_from_pipe_with_progress(
1829 imagex_progress_func,
1833 imagex_printf(T("Done applying WIM image.\n"));
1834 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1836 do_resource_not_found_warning(wimfile, &info, &refglobs);
1838 imagex_error(T( "If you are applying an image "
1839 "from a split pipable WIM,\n"
1840 " make sure you have "
1841 "concatenated together all parts."));
1843 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1844 do_metadata_not_found_warning(wimfile, &info);
1849 string_list_destroy(&refglobs);
1853 usage(CMD_APPLY, stderr);
1855 goto out_free_refglobs;
1859 * Create a WIM image from a directory tree, NTFS volume, or multiple files or
1860 * directory trees. 'wimcapture': create a new WIM file containing the desired
1861 * image. 'wimappend': add a new image to an existing WIM file; or, with
1862 * '--create' behave like 'wimcapture' if the WIM file doesn't exist.
1865 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1868 bool create = false;
1869 bool appending = (cmd == CMD_APPEND);
1871 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1872 WIMLIB_ADD_FLAG_WINCONFIG |
1873 WIMLIB_ADD_FLAG_VERBOSE |
1874 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1875 int write_flags = 0;
1876 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1877 uint32_t chunk_size = UINT32_MAX;
1878 uint32_t solid_chunk_size = UINT32_MAX;
1879 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1880 const tchar *wimfile;
1883 STRING_LIST(image_properties);
1886 STRING_LIST(base_wimfiles);
1887 WIMStruct **base_wims;
1889 WIMStruct *template_wim = NULL;
1890 const tchar *template_wimfile = NULL;
1891 const tchar *template_image_name_or_num = NULL;
1892 int template_image = WIMLIB_NO_IMAGE;
1895 unsigned num_threads = 0;
1900 tchar *config_file = NULL;
1902 bool source_list = false;
1903 size_t source_list_nchars = 0;
1904 tchar *source_list_contents;
1905 bool capture_sources_malloced;
1906 struct wimlib_capture_source *capture_sources;
1908 bool name_defaulted;
1910 for_opt(c, capture_or_append_options) {
1912 case IMAGEX_BOOT_OPTION:
1913 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1915 case IMAGEX_CHECK_OPTION:
1916 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1918 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
1919 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1921 case IMAGEX_NOCHECK_OPTION:
1922 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1924 case IMAGEX_CONFIG_OPTION:
1925 config_file = optarg;
1926 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1928 case IMAGEX_COMPRESS_OPTION:
1929 compression_type = get_compression_type(optarg, false);
1930 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1933 case IMAGEX_CHUNK_SIZE_OPTION:
1934 chunk_size = parse_chunk_size(optarg);
1935 if (chunk_size == UINT32_MAX)
1938 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1939 solid_chunk_size = parse_chunk_size(optarg);
1940 if (solid_chunk_size == UINT32_MAX)
1943 case IMAGEX_SOLID_COMPRESS_OPTION:
1944 solid_ctype = get_compression_type(optarg, true);
1945 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1948 case IMAGEX_SOLID_OPTION:
1949 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1951 case IMAGEX_NO_SOLID_SORT_OPTION:
1952 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1954 case IMAGEX_FLAGS_OPTION: {
1955 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1956 tsprintf(p, T("FLAGS=%"TS), optarg);
1957 ret = string_list_append(&image_properties, p);
1962 case IMAGEX_IMAGE_PROPERTY_OPTION:
1963 ret = append_image_property_argument(&image_properties);
1967 case IMAGEX_DEREFERENCE_OPTION:
1968 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1970 case IMAGEX_VERBOSE_OPTION:
1971 /* No longer does anything. */
1973 case IMAGEX_THREADS_OPTION:
1974 num_threads = parse_num_threads(optarg);
1975 if (num_threads == UINT_MAX)
1978 case IMAGEX_REBUILD_OPTION:
1979 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1981 case IMAGEX_UNIX_DATA_OPTION:
1982 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1984 case IMAGEX_SOURCE_LIST_OPTION:
1987 case IMAGEX_NO_ACLS_OPTION:
1988 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1990 case IMAGEX_STRICT_ACLS_OPTION:
1991 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1993 case IMAGEX_RPFIX_OPTION:
1994 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1996 case IMAGEX_NORPFIX_OPTION:
1997 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1999 case IMAGEX_PIPABLE_OPTION:
2000 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2002 case IMAGEX_NOT_PIPABLE_OPTION:
2003 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2005 case IMAGEX_UPDATE_OF_OPTION:
2006 if (template_image_name_or_num) {
2007 imagex_error(T("'--update-of' can only be "
2008 "specified one time!"));
2012 colon = tstrrchr(optarg, T(':'));
2015 template_wimfile = optarg;
2017 template_image_name_or_num = colon + 1;
2019 template_wimfile = NULL;
2020 template_image_name_or_num = optarg;
2024 case IMAGEX_DELTA_FROM_OPTION:
2025 ret = string_list_append(&base_wimfiles, optarg);
2028 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2030 case IMAGEX_WIMBOOT_OPTION:
2031 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2033 case IMAGEX_UNSAFE_COMPACT_OPTION:
2034 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2036 case IMAGEX_SNAPSHOT_OPTION:
2037 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2039 case IMAGEX_CREATE_OPTION:
2040 if (cmd == CMD_CAPTURE) {
2041 imagex_error(T("'--create' is only valid for 'wimappend', not 'wimcapture'"));
2053 if (argc < 2 || argc > 4)
2059 /* Set default compression type and parameters. */
2062 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2063 /* No compression type specified. Use the default. */
2065 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2066 /* With --wimboot, default to XPRESS compression. */
2067 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2068 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2069 /* With --solid, default to LZMS compression. (However,
2070 * this will not affect solid resources!) */
2071 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2073 /* Otherwise, default to LZX compression. */
2074 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2078 if (!tstrcmp(wimfile, T("-"))) {
2079 /* Writing captured WIM to standard output. */
2083 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2084 imagex_error("Can't write a non-pipable WIM to "
2085 "standard output! Specify --pipable\n"
2086 " if you want to create a pipable WIM "
2087 "(but read the docs first).");
2091 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2094 imagex_error(T("Using standard output for append does "
2095 "not make sense."));
2098 wim_fd = STDOUT_FILENO;
2100 imagex_output_to_stderr();
2101 set_fd_to_binary_mode(wim_fd);
2105 /* Check for 'wimappend --create' acting as wimcapture */
2106 if (create && tstat(wimfile, &stbuf) != 0 && errno == ENOENT) {
2110 /* Ignore '--update-of' for the target WIMFILE */
2111 if (template_image_name_or_num &&
2112 (!template_wimfile ||
2113 !tstrcmp(template_wimfile, wimfile)))
2115 template_image_name_or_num = NULL;
2116 template_wimfile = NULL;
2121 if ((write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) && !appending) {
2122 imagex_error(T("'--unsafe-compact' is only valid for append!"));
2126 /* If template image was specified using --update-of=IMAGE rather
2127 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2128 if (template_image_name_or_num && !template_wimfile) {
2129 if (base_wimfiles.num_strings == 1) {
2130 /* Capturing delta WIM based on single WIM: default to
2132 template_wimfile = base_wimfiles.strings[0];
2133 } else if (appending) {
2134 /* Appending to WIM: default to WIM being appended to.
2136 template_wimfile = wimfile;
2138 /* Capturing a normal (non-delta) WIM, so the WIM file
2139 * *must* be explicitly specified. */
2140 if (base_wimfiles.num_strings > 1) {
2141 imagex_error(T("For capture of delta WIM "
2142 "based on multiple existing "
2144 " '--update-of' must "
2145 "specify WIMFILE:IMAGE!"));
2147 imagex_error(T("For capture of non-delta WIM, "
2148 "'--update-of' must specify "
2157 name_defaulted = false;
2159 /* Set default name to SOURCE argument, omitting any directory
2160 * prefixes and trailing slashes. This requires making a copy
2161 * of @source. Leave some free characters at the end in case we
2162 * append a number to keep the name unique. */
2163 size_t source_name_len;
2165 source_name_len = tstrlen(source);
2166 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2167 name = tbasename(tstrcpy(source_copy, source));
2168 name_defaulted = true;
2171 /* Image description (if given). */
2173 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2174 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2175 ret = string_list_append(&image_properties, p);
2181 /* Set up capture sources in source list mode */
2182 if (source[0] == T('-') && source[1] == T('\0')) {
2183 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2185 source_list_contents = file_get_text_contents(source,
2186 &source_list_nchars);
2188 if (!source_list_contents)
2191 capture_sources = parse_source_list(&source_list_contents,
2194 if (!capture_sources) {
2196 goto out_free_source_list_contents;
2198 capture_sources_malloced = true;
2200 /* Set up capture source in non-source-list mode. */
2201 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2202 capture_sources[0].fs_source_path = source;
2203 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2204 capture_sources[0].reserved = 0;
2206 capture_sources_malloced = false;
2207 source_list_contents = NULL;
2210 /* Open the existing WIM, or create a new one. */
2212 ret = wimlib_open_wim_with_progress(wimfile,
2213 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2215 imagex_progress_func,
2218 goto out_free_capture_sources;
2220 ret = wimlib_create_new_wim(compression_type, &wim);
2222 goto out_free_capture_sources;
2223 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2226 /* Set chunk size if non-default. */
2227 if (chunk_size != UINT32_MAX) {
2228 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2231 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2233 int ctype = compression_type;
2236 struct wimlib_wim_info info;
2237 wimlib_get_wim_info(wim, &info);
2238 ctype = info.compression_type;
2241 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2242 ret = wimlib_set_output_chunk_size(wim, 4096);
2247 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2248 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2252 if (solid_chunk_size != UINT32_MAX) {
2253 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2259 /* Detect if source is regular file or block device and set NTFS volume
2264 if (tstat(source, &stbuf) == 0) {
2265 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2266 imagex_printf(T("Capturing WIM image from NTFS "
2267 "filesystem on \"%"TS"\"\n"), source);
2268 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2271 if (errno != ENOENT) {
2272 imagex_error_with_errno(T("Failed to stat "
2273 "\"%"TS"\""), source);
2281 /* If the user did not specify an image name, and the basename of the
2282 * source already exists as an image name in the WIM file, append a
2283 * suffix to make it unique. */
2284 if (appending && name_defaulted) {
2285 unsigned long conflict_idx;
2286 tchar *name_end = tstrchr(name, T('\0'));
2287 for (conflict_idx = 1;
2288 wimlib_image_name_in_use(wim, name);
2291 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2295 /* If capturing a delta WIM, reference resources from the base WIMs
2296 * before adding the new image. */
2297 if (base_wimfiles.num_strings) {
2298 base_wims = calloc(base_wimfiles.num_strings,
2299 sizeof(base_wims[0]));
2300 if (base_wims == NULL) {
2301 imagex_error(T("Out of memory!"));
2306 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2307 ret = wimlib_open_wim_with_progress(
2308 base_wimfiles.strings[i], open_flags,
2309 &base_wims[i], imagex_progress_func, NULL);
2311 goto out_free_base_wims;
2315 ret = wimlib_reference_resources(wim, base_wims,
2316 base_wimfiles.num_strings, 0);
2318 goto out_free_base_wims;
2320 if (base_wimfiles.num_strings == 1) {
2321 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2322 base_wimfiles.strings[0]);
2324 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2325 base_wimfiles.num_strings);
2332 /* If capturing or appending as an update of an existing (template) image,
2333 * open the WIM if needed and parse the image index. */
2334 if (template_image_name_or_num) {
2336 if (appending && !tstrcmp(template_wimfile, wimfile)) {
2339 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2340 if (!tstrcmp(template_wimfile,
2341 base_wimfiles.strings[i])) {
2342 template_wim = base_wims[i];
2348 if (!template_wim) {
2349 ret = wimlib_open_wim_with_progress(template_wimfile,
2352 imagex_progress_func,
2355 goto out_free_base_wims;
2358 template_image = wimlib_resolve_image(template_wim,
2359 template_image_name_or_num);
2361 if (template_image_name_or_num[0] == T('-')) {
2364 struct wimlib_wim_info info;
2366 wimlib_get_wim_info(template_wim, &info);
2367 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2368 if (n >= 1 && n <= info.image_count &&
2370 tmp != template_image_name_or_num + 1)
2372 template_image = info.image_count - (n - 1);
2375 ret = verify_image_exists_and_is_single(template_image,
2376 template_image_name_or_num,
2379 goto out_free_template_wim;
2382 ret = wimlib_add_image_multisource(wim,
2389 goto out_free_template_wim;
2391 if (image_properties.num_strings || template_image_name_or_num) {
2392 /* User asked to set additional image properties, or an image on
2393 * which the added one is to be based has been specified with
2395 struct wimlib_wim_info info;
2397 wimlib_get_wim_info(wim, &info);
2399 ret = apply_image_properties(&image_properties, wim,
2400 info.image_count, NULL);
2402 goto out_free_template_wim;
2404 /* Reference template image if the user provided one. */
2405 if (template_image_name_or_num) {
2406 imagex_printf(T("Using image %d "
2407 "from \"%"TS"\" as template\n"),
2408 template_image, template_wimfile);
2409 ret = wimlib_reference_template_image(wim,
2415 goto out_free_template_wim;
2419 /* Write the new WIM or overwrite the existing WIM with the new image
2422 ret = wimlib_overwrite(wim, write_flags, num_threads);
2423 } else if (wimfile) {
2424 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2425 write_flags, num_threads);
2427 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2428 write_flags, num_threads);
2430 out_free_template_wim:
2431 /* 'template_wim' may alias 'wim' or any of the 'base_wims' */
2432 if (template_wim == wim)
2433 goto out_free_base_wims;
2434 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2435 if (template_wim == base_wims[i])
2436 goto out_free_base_wims;
2437 wimlib_free(template_wim);
2439 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2440 wimlib_free(base_wims[i]);
2444 out_free_capture_sources:
2445 if (capture_sources_malloced)
2446 free(capture_sources);
2447 out_free_source_list_contents:
2448 free(source_list_contents);
2450 string_list_destroy(&image_properties);
2451 string_list_destroy(&base_wimfiles);
2461 /* Remove image(s) from a WIM. */
2463 imagex_delete(int argc, tchar **argv, int cmd)
2466 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2467 int write_flags = 0;
2468 const tchar *wimfile;
2469 const tchar *image_num_or_name;
2474 for_opt(c, delete_options) {
2476 case IMAGEX_CHECK_OPTION:
2477 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2479 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2480 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2482 case IMAGEX_SOFT_OPTION:
2483 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2485 case IMAGEX_UNSAFE_COMPACT_OPTION:
2486 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2497 imagex_error(T("Must specify a WIM file"));
2499 imagex_error(T("Must specify an image"));
2503 image_num_or_name = argv[1];
2505 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2506 imagex_progress_func, NULL);
2510 image = wimlib_resolve_image(wim, image_num_or_name);
2512 ret = verify_image_exists(image, image_num_or_name, wimfile);
2514 goto out_wimlib_free;
2516 ret = wimlib_delete_image(wim, image);
2518 imagex_error(T("Failed to delete image from \"%"TS"\""),
2520 goto out_wimlib_free;
2523 ret = wimlib_overwrite(wim, write_flags, 0);
2525 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2526 "deleted"), wimfile);
2534 usage(CMD_DELETE, stderr);
2539 struct print_dentry_options {
2544 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2546 tprintf(T("%"TS"\n"), dentry->full_path);
2549 static const struct {
2552 } file_attr_flags[] = {
2553 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2554 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2555 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2556 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2557 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2558 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2559 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2560 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2561 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2562 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2563 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2564 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2565 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2566 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2567 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2570 #define TIMESTR_MAX 100
2573 print_time(const tchar *type, const struct wimlib_timespec *wts,
2576 tchar timestr[TIMESTR_MAX];
2580 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2581 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2586 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2587 timestr[TIMESTR_MAX - 1] = '\0';
2589 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2592 static void print_byte_field(const uint8_t field[], size_t len)
2595 tprintf(T("%02hhx"), *field++);
2599 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2601 tchar attr_string[256];
2604 tputs(T("WIM Information:"));
2605 tputs(T("----------------"));
2606 tprintf(T("Path: %"TS"\n"), wimfile);
2607 tprintf(T("GUID: 0x"));
2608 print_byte_field(info->guid, sizeof(info->guid));
2610 tprintf(T("Version: %u\n"), info->wim_version);
2611 tprintf(T("Image Count: %d\n"), info->image_count);
2612 tprintf(T("Compression: %"TS"\n"),
2613 wimlib_get_compression_type_string(info->compression_type));
2614 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2616 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2617 tprintf(T("Boot Index: %d\n"), info->boot_index);
2618 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2620 attr_string[0] = T('\0');
2623 tstrcat(attr_string, T("Pipable, "));
2625 if (info->has_integrity_table)
2626 tstrcat(attr_string, T("Integrity info, "));
2628 if (info->has_rpfix)
2629 tstrcat(attr_string, T("Relative path junction, "));
2631 if (info->resource_only)
2632 tstrcat(attr_string, T("Resource only, "));
2634 if (info->metadata_only)
2635 tstrcat(attr_string, T("Metadata only, "));
2637 if (info->is_marked_readonly)
2638 tstrcat(attr_string, T("Readonly, "));
2640 p = tstrchr(attr_string, T('\0'));
2641 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2644 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2648 print_resource(const struct wimlib_resource_entry *resource,
2651 tprintf(T("Hash = 0x"));
2652 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2655 if (!resource->is_missing) {
2656 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2657 resource->uncompressed_size);
2658 if (resource->packed) {
2659 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2660 "bytes @ offset %"PRIu64"\n"),
2661 resource->raw_resource_uncompressed_size,
2662 resource->raw_resource_compressed_size,
2663 resource->raw_resource_offset_in_wim);
2665 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2668 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2669 resource->compressed_size);
2671 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2675 tprintf(T("Part Number = %u\n"), resource->part_number);
2676 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2678 tprintf(T("Flags = "));
2679 if (resource->is_compressed)
2680 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2681 if (resource->is_metadata)
2682 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2683 if (resource->is_free)
2684 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2685 if (resource->is_spanned)
2686 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2687 if (resource->packed)
2688 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2696 print_blobs(WIMStruct *wim)
2698 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2703 default_print_security_descriptor(const uint8_t *sd, size_t size)
2705 tprintf(T("Security Descriptor = "));
2706 print_byte_field(sd, size);
2712 is_null_guid(const uint8_t *guid)
2714 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2716 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2720 print_guid(const tchar *label, const uint8_t *guid)
2722 if (is_null_guid(guid))
2724 tprintf(T("%-20"TS"= 0x"), label);
2725 print_byte_field(guid, WIMLIB_GUID_LEN);
2730 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2733 "----------------------------------------------------------------------------\n"));
2734 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2735 if (dentry->dos_name)
2736 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2737 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2738 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2739 if (file_attr_flags[i].flag & dentry->attributes)
2740 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2741 file_attr_flags[i].name);
2743 if (dentry->security_descriptor) {
2744 print_security_descriptor(dentry->security_descriptor,
2745 dentry->security_descriptor_size);
2748 print_time(T("Creation Time"),
2749 &dentry->creation_time, dentry->creation_time_high);
2750 print_time(T("Last Write Time"),
2751 &dentry->last_write_time, dentry->last_write_time_high);
2752 print_time(T("Last Access Time"),
2753 &dentry->last_access_time, dentry->last_access_time_high);
2756 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2757 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2759 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2760 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2762 if (dentry->unix_mode != 0) {
2763 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2764 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2765 dentry->unix_uid, dentry->unix_gid,
2766 dentry->unix_mode, dentry->unix_rdev);
2769 if (!is_null_guid(dentry->object_id.object_id)) {
2770 print_guid(T("Object ID"), dentry->object_id.object_id);
2771 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2772 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2773 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2776 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2777 if (dentry->streams[i].stream_name) {
2778 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2779 dentry->streams[i].stream_name);
2780 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2781 tprintf(T("\tRaw encrypted data stream:\n"));
2782 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2783 tprintf(T("\tReparse point stream:\n"));
2785 tprintf(T("\tUnnamed data stream:\n"));
2787 print_resource(&dentry->streams[i].resource, NULL);
2792 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2794 const struct print_dentry_options *options = _options;
2795 if (!options->detailed)
2796 print_dentry_full_path(dentry);
2798 print_dentry_detailed(dentry);
2802 /* Print the files contained in an image(s) in a WIM file. */
2804 imagex_dir(int argc, tchar **argv, int cmd)
2806 const tchar *wimfile;
2807 WIMStruct *wim = NULL;
2810 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2812 struct print_dentry_options options = {
2815 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2817 STRING_LIST(refglobs);
2819 for_opt(c, dir_options) {
2821 case IMAGEX_PATH_OPTION:
2824 case IMAGEX_DETAILED_OPTION:
2825 options.detailed = true;
2827 case IMAGEX_ONE_FILE_ONLY_OPTION:
2828 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2830 case IMAGEX_REF_OPTION:
2831 ret = string_list_append(&refglobs, optarg);
2833 goto out_free_refglobs;
2843 imagex_error(T("Must specify a WIM file"));
2847 imagex_error(T("Too many arguments"));
2852 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2853 imagex_progress_func, NULL);
2855 goto out_free_refglobs;
2858 image = wimlib_resolve_image(wim, argv[1]);
2859 ret = verify_image_exists(image, argv[1], wimfile);
2861 goto out_wimlib_free;
2863 /* No image specified; default to image 1, but only if the WIM
2864 * contains exactly one image. */
2866 struct wimlib_wim_info info;
2868 wimlib_get_wim_info(wim, &info);
2869 if (info.image_count != 1) {
2870 imagex_error(T("\"%"TS"\" contains %d images; Please "
2871 "select one (or all)."),
2872 wimfile, info.image_count);
2879 if (refglobs.num_strings) {
2880 ret = wim_reference_globs(wim, &refglobs, 0);
2882 goto out_wimlib_free;
2885 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2886 print_dentry, &options);
2887 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2888 struct wimlib_wim_info info;
2890 wimlib_get_wim_info(wim, &info);
2891 do_metadata_not_found_warning(wimfile, &info);
2896 string_list_destroy(&refglobs);
2900 usage(CMD_DIR, stderr);
2902 goto out_free_refglobs;
2905 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2908 imagex_export(int argc, tchar **argv, int cmd)
2912 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2913 int write_flags = 0;
2914 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2915 const tchar *src_wimfile;
2916 const tchar *src_image_num_or_name;
2917 const tchar *dest_wimfile;
2919 const tchar *dest_name;
2920 const tchar *dest_desc;
2922 struct wimlib_wim_info src_info;
2923 WIMStruct *dest_wim;
2928 STRING_LIST(refglobs);
2929 unsigned num_threads = 0;
2930 uint32_t chunk_size = UINT32_MAX;
2931 uint32_t solid_chunk_size = UINT32_MAX;
2932 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2934 for_opt(c, export_options) {
2936 case IMAGEX_BOOT_OPTION:
2937 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2939 case IMAGEX_CHECK_OPTION:
2940 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2942 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2943 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2945 case IMAGEX_NOCHECK_OPTION:
2946 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2948 case IMAGEX_COMPRESS_OPTION:
2949 compression_type = get_compression_type(optarg, false);
2950 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2953 case IMAGEX_RECOMPRESS_OPTION:
2954 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2956 case IMAGEX_SOLID_OPTION:
2957 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2959 case IMAGEX_NO_SOLID_SORT_OPTION:
2960 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2962 case IMAGEX_CHUNK_SIZE_OPTION:
2963 chunk_size = parse_chunk_size(optarg);
2964 if (chunk_size == UINT32_MAX)
2967 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2968 solid_chunk_size = parse_chunk_size(optarg);
2969 if (solid_chunk_size == UINT32_MAX)
2972 case IMAGEX_SOLID_COMPRESS_OPTION:
2973 solid_ctype = get_compression_type(optarg, true);
2974 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2977 case IMAGEX_REF_OPTION:
2978 ret = string_list_append(&refglobs, optarg);
2980 goto out_free_refglobs;
2982 case IMAGEX_THREADS_OPTION:
2983 num_threads = parse_num_threads(optarg);
2984 if (num_threads == UINT_MAX)
2987 case IMAGEX_REBUILD_OPTION:
2988 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2990 case IMAGEX_PIPABLE_OPTION:
2991 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2993 case IMAGEX_NOT_PIPABLE_OPTION:
2994 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2996 case IMAGEX_WIMBOOT_OPTION:
2997 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2999 case IMAGEX_UNSAFE_COMPACT_OPTION:
3000 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3008 if (argc < 3 || argc > 5)
3010 src_wimfile = argv[0];
3011 src_image_num_or_name = argv[1];
3012 dest_wimfile = argv[2];
3013 dest_name = (argc >= 4) ? argv[3] : NULL;
3014 dest_desc = (argc >= 5) ? argv[4] : NULL;
3015 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
3016 imagex_progress_func, NULL);
3018 goto out_free_refglobs;
3020 wimlib_get_wim_info(src_wim, &src_info);
3022 /* Determine if the destination is an existing file or not. If so, we
3023 * try to append the exported image(s) to it; otherwise, we create a new
3024 * WIM containing the exported image(s). Furthermore, determine if we
3025 * need to write a pipable WIM directly to standard output. */
3027 if (tstrcmp(dest_wimfile, T("-")) == 0) {
3029 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
3030 imagex_error("Can't write a non-pipable WIM to "
3031 "standard output! Specify --pipable\n"
3032 " if you want to create a pipable WIM "
3033 "(but read the docs first).");
3035 goto out_free_src_wim;
3038 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3040 dest_wimfile = NULL;
3041 dest_wim_fd = STDOUT_FILENO;
3042 imagex_output_to_stderr();
3043 set_fd_to_binary_mode(dest_wim_fd);
3046 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
3048 /* Destination file exists. */
3050 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
3051 imagex_error(T("\"%"TS"\" is not a regular file "
3052 "or block device"), dest_wimfile);
3054 goto out_free_src_wim;
3056 ret = wimlib_open_wim_with_progress(dest_wimfile,
3058 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3060 imagex_progress_func,
3063 goto out_free_src_wim;
3065 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3066 /* The user specified a compression type, but we're
3067 * exporting to an existing WIM. Make sure the
3068 * specified compression type is the same as the
3069 * compression type of the existing destination WIM. */
3070 struct wimlib_wim_info dest_info;
3072 wimlib_get_wim_info(dest_wim, &dest_info);
3073 if (compression_type != dest_info.compression_type) {
3074 imagex_error(T("Cannot specify a compression type that is "
3075 "not the same as that used in the "
3076 "destination WIM"));
3078 goto out_free_dest_wim;
3084 if (errno != ENOENT) {
3085 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3088 goto out_free_src_wim;
3091 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3092 imagex_error(T("'--unsafe-compact' is only valid when "
3093 "exporting to an existing WIM file!"));
3095 goto out_free_src_wim;
3098 /* dest_wimfile is not an existing file, so create a new WIM. */
3100 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3101 /* The user did not specify a compression type; default
3102 * to that of the source WIM, unless --solid or
3103 * --wimboot was specified. */
3105 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3106 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3107 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3108 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3110 compression_type = src_info.compression_type;
3112 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3114 goto out_free_src_wim;
3116 wimlib_register_progress_function(dest_wim,
3117 imagex_progress_func, NULL);
3119 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3120 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3122 /* For --wimboot export, use small XPRESS chunks. */
3123 wimlib_set_output_chunk_size(dest_wim, 4096);
3124 } else if (compression_type == src_info.compression_type &&
3125 chunk_size == UINT32_MAX)
3127 /* Use same chunk size if compression type is the same. */
3128 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3132 if (chunk_size != UINT32_MAX) {
3133 /* Set destination chunk size. */
3134 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3136 goto out_free_dest_wim;
3138 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3139 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3141 goto out_free_dest_wim;
3143 if (solid_chunk_size != UINT32_MAX) {
3144 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3146 goto out_free_dest_wim;
3149 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3150 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3152 goto out_free_dest_wim;
3154 if (refglobs.num_strings) {
3155 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3157 goto out_free_dest_wim;
3160 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3161 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3163 imagex_error(T("--boot specified for all-images export, but source WIM "
3164 "has no bootable image."));
3166 goto out_free_dest_wim;
3169 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3170 dest_desc, export_flags);
3172 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3173 do_resource_not_found_warning(src_wimfile,
3174 &src_info, &refglobs);
3175 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3176 do_metadata_not_found_warning(src_wimfile, &src_info);
3178 goto out_free_dest_wim;
3182 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3183 else if (dest_wimfile)
3184 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3185 write_flags, num_threads);
3187 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3188 WIMLIB_ALL_IMAGES, write_flags,
3191 wimlib_free(dest_wim);
3193 wimlib_free(src_wim);
3195 string_list_destroy(&refglobs);
3199 usage(CMD_EXPORT, stderr);
3202 goto out_free_refglobs;
3205 /* Extract files or directories from a WIM image */
3207 imagex_extract(int argc, tchar **argv, int cmd)
3214 const tchar *wimfile;
3215 const tchar *image_num_or_name;
3216 tchar *dest_dir = T(".");
3217 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3218 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3219 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3220 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3222 STRING_LIST(refglobs);
3224 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3226 for_opt(c, extract_options) {
3228 case IMAGEX_CHECK_OPTION:
3229 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3231 case IMAGEX_VERBOSE_OPTION:
3232 /* No longer does anything. */
3234 case IMAGEX_REF_OPTION:
3235 ret = string_list_append(&refglobs, optarg);
3237 goto out_free_refglobs;
3239 case IMAGEX_UNIX_DATA_OPTION:
3240 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3242 case IMAGEX_NO_ACLS_OPTION:
3243 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3245 case IMAGEX_STRICT_ACLS_OPTION:
3246 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3248 case IMAGEX_NO_ATTRIBUTES_OPTION:
3249 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3251 case IMAGEX_DEST_DIR_OPTION:
3254 case IMAGEX_TO_STDOUT_OPTION:
3255 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3256 imagex_suppress_output();
3257 set_fd_to_binary_mode(STDOUT_FILENO);
3259 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3260 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3261 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3263 case IMAGEX_NO_GLOBS_OPTION:
3264 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3266 case IMAGEX_NULLGLOB_OPTION:
3267 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3269 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3270 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3272 case IMAGEX_WIMBOOT_OPTION:
3273 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3275 case IMAGEX_COMPACT_OPTION:
3276 ret = set_compact_mode(optarg, &extract_flags);
3278 goto out_free_refglobs;
3290 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3291 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3293 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3298 image_num_or_name = argv[1];
3303 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3304 imagex_progress_func, NULL);
3306 goto out_free_refglobs;
3308 image = wimlib_resolve_image(wim, image_num_or_name);
3309 ret = verify_image_exists_and_is_single(image,
3313 goto out_wimlib_free;
3315 if (refglobs.num_strings) {
3316 ret = wim_reference_globs(wim, &refglobs, open_flags);
3318 goto out_wimlib_free;
3324 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3327 while (argc != 0 && ret == 0) {
3331 num_paths < argc && argv[num_paths][0] != T('@');
3336 ret = wimlib_extract_paths(wim, image, dest_dir,
3337 (const tchar **)argv,
3339 extract_flags | notlist_extract_flags);
3343 const tchar *listfile = argv[0] + 1;
3345 if (!tstrcmp(listfile, T("-"))) {
3346 tputs(T("Reading pathlist file from standard input..."));
3350 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3351 listfile, extract_flags);
3358 imagex_printf(T("Done extracting files.\n"));
3359 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3360 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3361 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3362 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3363 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3366 T("Note: You can use the '--nullglob' "
3367 "option to ignore missing files.\n"));
3369 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3370 "files and directories\n"
3371 " are in the WIM image.\n"),
3372 get_cmd_string(CMD_DIR, false));
3373 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3374 struct wimlib_wim_info info;
3376 wimlib_get_wim_info(wim, &info);
3377 do_resource_not_found_warning(wimfile, &info, &refglobs);
3378 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3379 struct wimlib_wim_info info;
3381 wimlib_get_wim_info(wim, &info);
3382 do_metadata_not_found_warning(wimfile, &info);
3387 string_list_destroy(&refglobs);
3391 usage(CMD_EXTRACT, stderr);
3394 goto out_free_refglobs;
3397 /* Prints information about a WIM file; also can mark an image as bootable,
3398 * change the name of an image, or change the description of an image. */
3400 imagex_info(int argc, tchar **argv, int cmd)
3404 bool header = false;
3407 bool short_header = true;
3408 const tchar *xml_out_file = NULL;
3409 const tchar *wimfile;
3410 const tchar *image_num_or_name;
3411 STRING_LIST(image_properties);
3416 int write_flags = 0;
3417 struct wimlib_wim_info info;
3419 for_opt(c, info_options) {
3421 case IMAGEX_BOOT_OPTION:
3424 case IMAGEX_CHECK_OPTION:
3425 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3427 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3428 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3430 case IMAGEX_NOCHECK_OPTION:
3431 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3433 case IMAGEX_HEADER_OPTION:
3435 short_header = false;
3437 case IMAGEX_BLOBS_OPTION:
3439 short_header = false;
3441 case IMAGEX_XML_OPTION:
3443 short_header = false;
3445 case IMAGEX_EXTRACT_XML_OPTION:
3446 xml_out_file = optarg;
3447 short_header = false;
3449 case IMAGEX_IMAGE_PROPERTY_OPTION:
3450 ret = append_image_property_argument(&image_properties);
3461 if (argc < 1 || argc > 4)
3465 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3469 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3470 tsprintf(p, T("NAME=%"TS), argv[2]);
3471 ret = string_list_append(&image_properties, p);
3478 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3479 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3480 ret = string_list_append(&image_properties, p);
3485 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3486 imagex_progress_func, NULL);
3490 wimlib_get_wim_info(wim, &info);
3492 image = wimlib_resolve_image(wim, image_num_or_name);
3493 ret = WIMLIB_ERR_INVALID_IMAGE;
3494 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3495 verify_image_exists(image, image_num_or_name, wimfile);
3497 imagex_error(T("If you would like to set the boot "
3498 "index to 0, specify image \"0\" with "
3499 "the --boot flag."));
3501 goto out_wimlib_free;
3504 if (boot && info.image_count == 0) {
3505 imagex_error(T("--boot is meaningless on a WIM with no images"));
3506 goto out_wimlib_free;
3509 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3511 imagex_error(T("Cannot specify the --boot flag "
3512 "without specifying a specific "
3513 "image in a multi-image WIM"));
3514 goto out_wimlib_free;
3516 if (image_properties.num_strings) {
3517 imagex_error(T("Can't change image properties without "
3518 "specifying a specific image in a "
3519 "multi-image WIM"));
3520 goto out_wimlib_free;
3524 /* Operations that print information are separated from operations that
3525 * recreate the WIM file. */
3526 if (!image_properties.num_strings && !boot) {
3528 /* Read-only operations */
3530 if (image == WIMLIB_NO_IMAGE) {
3531 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3532 image_num_or_name, wimfile);
3533 goto out_wimlib_free;
3536 if (image == WIMLIB_ALL_IMAGES && short_header)
3537 print_wim_information(wimfile, &info);
3540 wimlib_print_header(wim);
3543 if (info.total_parts != 1) {
3544 tfprintf(stderr, T("Warning: Only showing the blobs "
3545 "for part %d of a %d-part WIM.\n"),
3546 info.part_number, info.total_parts);
3552 ret = wimlib_extract_xml_data(wim, stdout);
3554 goto out_wimlib_free;
3560 fp = tfopen(xml_out_file, T("wb"));
3562 imagex_error_with_errno(T("Failed to open the "
3563 "file \"%"TS"\" for "
3567 goto out_wimlib_free;
3569 ret = wimlib_extract_xml_data(wim, fp);
3571 imagex_error(T("Failed to close the file "
3577 goto out_wimlib_free;
3581 wimlib_print_available_images(wim, image);
3585 /* Modification operations */
3586 bool any_property_changes;
3588 if (image == WIMLIB_ALL_IMAGES)
3591 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3592 imagex_error(T("Cannot change image properties "
3593 "when using image 0"));
3595 goto out_wimlib_free;
3599 if (image == info.boot_index) {
3600 imagex_printf(T("Image %d is already marked as "
3601 "bootable.\n"), image);
3604 imagex_printf(T("Marking image %d as bootable.\n"),
3606 info.boot_index = image;
3607 ret = wimlib_set_wim_info(wim, &info,
3608 WIMLIB_CHANGE_BOOT_INDEX);
3610 goto out_wimlib_free;
3614 ret = apply_image_properties(&image_properties, wim, image,
3615 &any_property_changes);
3617 goto out_wimlib_free;
3619 /* Only call wimlib_overwrite() if something actually needs to
3621 if (boot || any_property_changes ||
3622 ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) &&
3623 !info.has_integrity_table) ||
3624 ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) &&
3625 info.has_integrity_table))
3627 ret = wimlib_overwrite(wim, write_flags, 1);
3629 imagex_printf(T("The file \"%"TS"\" was not modified "
3630 "because nothing needed to be done.\n"),
3638 string_list_destroy(&image_properties);
3642 usage(CMD_INFO, stderr);
3647 /* Join split WIMs into one part WIM */
3649 imagex_join(int argc, tchar **argv, int cmd)
3652 int swm_open_flags = 0;
3653 int wim_write_flags = 0;
3654 const tchar *output_path;
3657 for_opt(c, join_options) {
3659 case IMAGEX_CHECK_OPTION:
3660 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3662 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3663 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3673 imagex_error(T("Must specify one or more split WIM (.swm) "
3677 output_path = argv[0];
3678 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3683 imagex_progress_func,
3689 usage(CMD_JOIN, stderr);
3694 #if WIM_MOUNTING_SUPPORTED
3696 /* Mounts a WIM image. */
3698 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3701 int mount_flags = 0;
3703 const tchar *staging_dir = NULL;
3704 const tchar *wimfile;
3707 struct wimlib_wim_info info;
3711 STRING_LIST(refglobs);
3713 if (cmd == CMD_MOUNTRW) {
3714 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3715 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3718 for_opt(c, mount_options) {
3720 case IMAGEX_ALLOW_OTHER_OPTION:
3721 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3723 case IMAGEX_CHECK_OPTION:
3724 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3726 case IMAGEX_DEBUG_OPTION:
3727 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3729 case IMAGEX_STREAMS_INTERFACE_OPTION:
3730 if (!tstrcasecmp(optarg, T("none")))
3731 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3732 else if (!tstrcasecmp(optarg, T("xattr")))
3733 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3734 else if (!tstrcasecmp(optarg, T("windows")))
3735 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3737 imagex_error(T("Unknown stream interface \"%"TS"\""),
3742 case IMAGEX_REF_OPTION:
3743 ret = string_list_append(&refglobs, optarg);
3745 goto out_free_refglobs;
3747 case IMAGEX_STAGING_DIR_OPTION:
3748 staging_dir = optarg;
3750 case IMAGEX_UNIX_DATA_OPTION:
3751 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3759 if (argc != 2 && argc != 3)
3764 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3765 imagex_progress_func, NULL);
3767 goto out_free_refglobs;
3769 wimlib_get_wim_info(wim, &info);
3772 /* Image explicitly specified. */
3773 image = wimlib_resolve_image(wim, argv[1]);
3775 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3779 /* No image specified; default to image 1, but only if the WIM
3780 * contains exactly one image. */
3782 if (info.image_count != 1) {
3783 imagex_error(T("\"%"TS"\" contains %d images; Please "
3784 "select one."), wimfile, info.image_count);
3792 if (refglobs.num_strings) {
3793 ret = wim_reference_globs(wim, &refglobs, open_flags);
3798 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3800 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3801 do_metadata_not_found_warning(wimfile, &info);
3803 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3805 image, wimfile, dir);
3811 string_list_destroy(&refglobs);
3817 goto out_free_refglobs;
3819 #endif /* WIM_MOUNTING_SUPPORTED */
3821 /* Rebuild a WIM file */
3823 imagex_optimize(int argc, tchar **argv, int cmd)
3826 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3827 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3828 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3829 uint32_t chunk_size = UINT32_MAX;
3830 uint32_t solid_chunk_size = UINT32_MAX;
3831 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3834 const tchar *wimfile;
3837 unsigned num_threads = 0;
3839 for_opt(c, optimize_options) {
3841 case IMAGEX_CHECK_OPTION:
3842 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3844 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3845 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3847 case IMAGEX_NOCHECK_OPTION:
3848 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3850 case IMAGEX_COMPRESS_OPTION:
3851 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3852 compression_type = get_compression_type(optarg, false);
3853 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3856 case IMAGEX_RECOMPRESS_OPTION:
3857 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3859 case IMAGEX_CHUNK_SIZE_OPTION:
3860 chunk_size = parse_chunk_size(optarg);
3861 if (chunk_size == UINT32_MAX)
3864 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3865 solid_chunk_size = parse_chunk_size(optarg);
3866 if (solid_chunk_size == UINT32_MAX)
3869 case IMAGEX_SOLID_COMPRESS_OPTION:
3870 solid_ctype = get_compression_type(optarg, true);
3871 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3874 case IMAGEX_SOLID_OPTION:
3875 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3876 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3878 case IMAGEX_NO_SOLID_SORT_OPTION:
3879 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3881 case IMAGEX_THREADS_OPTION:
3882 num_threads = parse_num_threads(optarg);
3883 if (num_threads == UINT_MAX)
3886 case IMAGEX_PIPABLE_OPTION:
3887 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3889 case IMAGEX_NOT_PIPABLE_OPTION:
3890 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3892 case IMAGEX_UNSAFE_COMPACT_OPTION:
3893 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3907 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3908 imagex_progress_func, NULL);
3912 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3913 /* Change compression type. */
3914 ret = wimlib_set_output_compression_type(wim, compression_type);
3916 goto out_wimlib_free;
3919 if (chunk_size != UINT32_MAX) {
3920 /* Change chunk size. */
3921 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3923 goto out_wimlib_free;
3925 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3926 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3928 goto out_wimlib_free;
3930 if (solid_chunk_size != UINT32_MAX) {
3931 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3933 goto out_wimlib_free;
3936 old_size = file_get_size(wimfile);
3937 tprintf(T("\"%"TS"\" original size: "), wimfile);
3939 tputs(T("Unknown"));
3941 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3943 ret = wimlib_overwrite(wim, write_flags, num_threads);
3945 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3946 goto out_wimlib_free;
3949 new_size = file_get_size(wimfile);
3950 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3952 tputs(T("Unknown"));
3954 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3956 tfputs(T("Space saved: "), stdout);
3957 if (new_size != -1 && old_size != -1) {
3958 tprintf(T("%lld KiB\n"),
3959 ((long long)old_size - (long long)new_size) >> 10);
3961 tputs(T("Unknown"));
3970 usage(CMD_OPTIMIZE, stderr);
3976 /* Split a WIM into a spanned set */
3978 imagex_split(int argc, tchar **argv, int cmd)
3982 int write_flags = 0;
3983 unsigned long part_size;
3988 for_opt(c, split_options) {
3990 case IMAGEX_CHECK_OPTION:
3991 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3993 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3994 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4006 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
4007 if (tmp == argv[2] || *tmp) {
4008 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
4009 imagex_error(T("The part size must be an integer or "
4010 "floating-point number of megabytes."));
4013 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
4014 imagex_progress_func, NULL);
4018 ret = wimlib_split(wim, argv[1], part_size, write_flags);
4024 usage(CMD_SPLIT, stderr);
4030 #if WIM_MOUNTING_SUPPORTED
4031 /* Unmounts a mounted WIM image. */
4033 imagex_unmount(int argc, tchar **argv, int cmd)
4036 int unmount_flags = 0;
4039 for_opt(c, unmount_options) {
4041 case IMAGEX_COMMIT_OPTION:
4042 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
4044 case IMAGEX_CHECK_OPTION:
4045 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
4047 case IMAGEX_REBUILD_OPTION:
4048 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
4050 case IMAGEX_LAZY_OPTION:
4051 case IMAGEX_FORCE_OPTION:
4052 /* Now, unmount is lazy by default. However, committing
4053 * the image will fail with
4054 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4055 * file descriptors on the WIM image. The
4056 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4057 * descriptors to be closed. */
4058 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4060 case IMAGEX_NEW_IMAGE_OPTION:
4061 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4072 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4073 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4074 imagex_error(T("--new-image is meaningless "
4075 "without --commit also specified!"));
4080 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4081 imagex_progress_func, NULL);
4083 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4084 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4086 "\tNote: Use --commit --force to force changes "
4087 "to be committed, regardless\n"
4088 "\t of open files.\n"));
4095 usage(CMD_UNMOUNT, stderr);
4100 #endif /* WIM_MOUNTING_SUPPORTED */
4103 * Add, delete, or rename files in a WIM image.
4106 imagex_update(int argc, tchar **argv, int cmd)
4108 const tchar *wimfile;
4112 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4113 int write_flags = 0;
4114 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4115 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4116 WIMLIB_ADD_FLAG_VERBOSE |
4117 WIMLIB_ADD_FLAG_WINCONFIG;
4118 int default_delete_flags = 0;
4119 unsigned num_threads = 0;
4121 tchar *cmd_file_contents;
4122 size_t cmd_file_nchars;
4123 struct wimlib_update_command *cmds;
4125 tchar *command_str = NULL;
4126 tchar *config_file = NULL;
4127 tchar *wimboot_config = NULL;
4129 for_opt(c, update_options) {
4131 /* Generic or write options */
4132 case IMAGEX_THREADS_OPTION:
4133 num_threads = parse_num_threads(optarg);
4134 if (num_threads == UINT_MAX)
4137 case IMAGEX_CHECK_OPTION:
4138 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4140 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4141 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4143 case IMAGEX_REBUILD_OPTION:
4144 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4146 case IMAGEX_COMMAND_OPTION:
4148 imagex_error(T("--command may only be specified "
4149 "one time. Please provide\n"
4150 " the update commands "
4151 "on standard input instead."));
4154 command_str = tstrdup(optarg);
4156 imagex_error(T("Out of memory!"));
4160 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4161 wimboot_config = optarg;
4163 /* Default delete options */
4164 case IMAGEX_FORCE_OPTION:
4165 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4167 case IMAGEX_RECURSIVE_OPTION:
4168 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4171 /* Global add option */
4172 case IMAGEX_CONFIG_OPTION:
4173 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4174 config_file = optarg;
4177 /* Default add options */
4178 case IMAGEX_VERBOSE_OPTION:
4179 /* No longer does anything. */
4181 case IMAGEX_DEREFERENCE_OPTION:
4182 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4184 case IMAGEX_UNIX_DATA_OPTION:
4185 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4187 case IMAGEX_NO_ACLS_OPTION:
4188 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4190 case IMAGEX_STRICT_ACLS_OPTION:
4191 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4193 case IMAGEX_NO_REPLACE_OPTION:
4194 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4196 case IMAGEX_UNSAFE_COMPACT_OPTION:
4197 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4206 if (argc != 1 && argc != 2)
4210 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4211 imagex_progress_func, NULL);
4213 goto out_free_command_str;
4216 /* Image explicitly specified. */
4217 image = wimlib_resolve_image(wim, argv[1]);
4218 ret = verify_image_exists_and_is_single(image, argv[1],
4221 goto out_wimlib_free;
4223 /* No image specified; default to image 1, but only if the WIM
4224 * contains exactly one image. */
4225 struct wimlib_wim_info info;
4227 wimlib_get_wim_info(wim, &info);
4228 if (info.image_count != 1) {
4229 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4230 wimfile, info.image_count);
4237 /* Read update commands from standard input, or the command string if
4240 cmd_file_contents = NULL;
4241 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4245 goto out_free_cmd_file_contents;
4247 } else if (!wimboot_config) {
4248 if (isatty(STDIN_FILENO)) {
4249 tputs(T("Reading update commands from standard input..."));
4250 recommend_man_page(CMD_UPDATE, stdout);
4252 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4253 if (!cmd_file_contents) {
4255 goto out_wimlib_free;
4258 /* Parse the update commands */
4259 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4263 goto out_free_cmd_file_contents;
4266 cmd_file_contents = NULL;
4271 /* Set default flags and capture config on the update commands */
4272 for (size_t i = 0; i < num_cmds; i++) {
4273 switch (cmds[i].op) {
4274 case WIMLIB_UPDATE_OP_ADD:
4275 cmds[i].add.add_flags |= default_add_flags;
4276 cmds[i].add.config_file = config_file;
4278 case WIMLIB_UPDATE_OP_DELETE:
4279 cmds[i].delete_.delete_flags |= default_delete_flags;
4286 /* Execute the update commands */
4287 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4291 if (wimboot_config) {
4292 /* --wimboot-config=FILE is short for an
4293 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4295 struct wimlib_update_command cmd;
4297 cmd.op = WIMLIB_UPDATE_OP_ADD;
4298 cmd.add.fs_source_path = wimboot_config;
4299 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4300 cmd.add.config_file = NULL;
4301 cmd.add.add_flags = 0;
4303 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4308 /* Overwrite the updated WIM */
4309 ret = wimlib_overwrite(wim, write_flags, num_threads);
4312 out_free_cmd_file_contents:
4313 free(cmd_file_contents);
4316 out_free_command_str:
4321 usage(CMD_UPDATE, stderr);
4324 goto out_free_command_str;
4327 /* Verify a WIM file. */
4329 imagex_verify(int argc, tchar **argv, int cmd)
4332 const tchar *wimfile;
4334 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4335 int verify_flags = 0;
4336 STRING_LIST(refglobs);
4339 for_opt(c, verify_options) {
4341 case IMAGEX_REF_OPTION:
4342 ret = string_list_append(&refglobs, optarg);
4344 goto out_free_refglobs;
4346 case IMAGEX_NOCHECK_OPTION:
4347 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4359 imagex_error(T("Must specify a WIM file!"));
4361 imagex_error(T("At most one WIM file can be specified!"));
4367 ret = wimlib_open_wim_with_progress(wimfile,
4370 imagex_progress_func,
4373 goto out_free_refglobs;
4375 ret = wim_reference_globs(wim, &refglobs, open_flags);
4377 goto out_wimlib_free;
4379 ret = wimlib_verify_wim(wim, verify_flags);
4381 tputc(T('\n'), stderr);
4382 imagex_error(T("\"%"TS"\" failed verification!"),
4384 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4385 refglobs.num_strings == 0)
4387 imagex_printf(T("Note: if this WIM file is not standalone, "
4388 "use the --ref option to specify the other parts.\n"));
4391 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4398 string_list_destroy(&refglobs);
4402 usage(CMD_VERIFY, stderr);
4404 goto out_free_refglobs;
4407 struct imagex_command {
4409 int (*func)(int argc, tchar **argv, int cmd);
4412 static const struct imagex_command imagex_commands[] = {
4413 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4414 [CMD_APPLY] = {T("apply"), imagex_apply},
4415 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4416 [CMD_DELETE] = {T("delete"), imagex_delete},
4417 [CMD_DIR ] = {T("dir"), imagex_dir},
4418 [CMD_EXPORT] = {T("export"), imagex_export},
4419 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4420 [CMD_INFO] = {T("info"), imagex_info},
4421 [CMD_JOIN] = {T("join"), imagex_join},
4422 #if WIM_MOUNTING_SUPPORTED
4423 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4424 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4426 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4427 [CMD_SPLIT] = {T("split"), imagex_split},
4428 #if WIM_MOUNTING_SUPPORTED
4429 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4431 [CMD_UPDATE] = {T("update"), imagex_update},
4432 [CMD_VERIFY] = {T("verify"), imagex_verify},
4437 /* Can be a directory or source list file. But source list file is probably
4438 * a rare use case, so just say directory. */
4439 # define SOURCE_STR T("DIRECTORY")
4441 /* Can only be a directory */
4442 # define TARGET_STR T("DIRECTORY")
4445 /* Can be a directory, NTFS volume, or source list file. */
4446 # define SOURCE_STR T("SOURCE")
4448 /* Can be a directory or NTFS volume. */
4449 # define TARGET_STR T("TARGET")
4453 static const tchar * const usage_strings[] = {
4456 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4457 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4458 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4459 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4460 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4461 " [--dereference] [--snapshot] [--create]\n"
4465 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4466 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4467 " [--no-attributes] [--rpfix] [--norpfix]\n"
4468 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4469 " [--compact=FORMAT]\n"
4473 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4474 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4475 " [--config=FILE] [--threads=NUM_THREADS]\n"
4476 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4477 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4478 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4483 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4487 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4491 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4492 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4493 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4494 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4495 " [--wimboot] [--solid]\n"
4499 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4500 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4501 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4502 " [--no-attributes] [--include-invalid-names]\n"
4503 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4507 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4508 " [--boot] [--check] [--nocheck] [--xml]\n"
4509 " [--extract-xml FILE] [--header] [--blobs]\n"
4510 " [--image-property NAME=VALUE]\n"
4514 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4516 #if WIM_MOUNTING_SUPPORTED
4519 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4520 " [--check] [--streams-interface=INTERFACE]\n"
4521 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4525 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4526 " [--check] [--streams-interface=INTERFACE]\n"
4527 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4533 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4534 " [--check] [--nocheck] [--solid]\n"
4539 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4541 #if WIM_MOUNTING_SUPPORTED
4544 " %"TS" DIRECTORY\n"
4545 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4550 " %"TS" WIMFILE [IMAGE]\n"
4551 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4552 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4553 " [--command=STRING] [--wimboot-config=FILE]\n"
4558 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4562 static const tchar *invocation_name;
4563 static int invocation_cmd = CMD_NONE;
4565 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4567 static tchar buf[50];
4569 if (cmd == CMD_NONE)
4570 return T("wimlib-imagex");
4572 if (only_short_form || invocation_cmd != CMD_NONE) {
4573 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4575 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4576 imagex_commands[cmd].name);
4584 static const tchar * const fmt =
4586 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
4587 "Copyright (C) 2012-2018 Eric Biggers\n"
4588 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4589 "This is free software: you are free to change and redistribute it.\n"
4590 "There is NO WARRANTY, to the extent permitted by law.\n"
4592 "Report bugs to "PACKAGE_BUGREPORT".\n"
4594 tfprintf(stdout, fmt, wimlib_get_version_string());
4598 do_common_options(int *argc_p, tchar **argv, int cmd)
4604 for (i = 1; i < argc; i++) {
4606 if (p[0] == T('-') && p[1] == T('-')) {
4608 if (!tstrcmp(p, T("help"))) {
4609 if (cmd == CMD_NONE)
4614 } else if (!tstrcmp(p, T("version"))) {
4617 } else if (!tstrcmp(p, T("quiet"))) {
4618 imagex_suppress_output();
4619 memmove(&argv[i], &argv[i + 1],
4620 (argc - i) * sizeof(argv[i]));
4623 } else if (!*p) /* reached "--", no more options */
4632 print_usage_string(int cmd, FILE *fp)
4634 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4638 recommend_man_page(int cmd, FILE *fp)
4640 const tchar *format_str;
4642 format_str = T("Some uncommon options are not listed;\n"
4643 "See %"TS".pdf in the doc directory for more details.\n");
4645 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4647 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4651 usage(int cmd, FILE *fp)
4653 tfprintf(fp, T("Usage:\n"));
4654 print_usage_string(cmd, fp);
4655 tfprintf(fp, T("\n"));
4656 recommend_man_page(cmd, fp);
4662 tfprintf(fp, T("Usage:\n"));
4663 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4664 print_usage_string(cmd, fp);
4665 tfprintf(fp, T("\n"));
4667 static const tchar * const extra =
4670 " %"TS" --version\n"
4673 tfprintf(fp, extra, invocation_name, invocation_name);
4675 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4676 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4677 "For some commands IMAGE may be \"all\".\n"
4679 recommend_man_page(CMD_NONE, fp);
4683 extern int wmain(int argc, wchar_t **argv);
4687 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4688 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4689 * something else), while on Windows the command arguments will be UTF-16LE
4690 * encoded 'wchar_t' strings. */
4692 main(int argc, tchar **argv)
4698 imagex_info_file = stdout;
4699 invocation_name = tbasename(argv[0]);
4702 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4703 if (igcase != NULL) {
4704 if (!tstrcmp(igcase, T("no")) ||
4705 !tstrcmp(igcase, T("0")))
4706 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4707 else if (!tstrcmp(igcase, T("yes")) ||
4708 !tstrcmp(igcase, T("1")))
4709 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4712 "WARNING: Ignoring unknown setting of "
4713 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4718 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4720 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4721 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4722 for (int i = 0; i < CMD_MAX; i++) {
4723 if (!tstrcmp(invocation_name + 3,
4724 imagex_commands[i].name))
4733 /* Unless already known from the invocation name, determine which
4734 * command was specified. */
4735 if (cmd == CMD_NONE) {
4737 imagex_error(T("No command specified!\n"));
4741 for (int i = 0; i < CMD_MAX; i++) {
4742 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4747 if (cmd != CMD_NONE) {
4753 /* Handle common options. May exit early (for --help or --version). */
4754 do_common_options(&argc, argv, cmd);
4756 /* Bail if a valid command was not specified. */
4757 if (cmd == CMD_NONE) {
4758 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4763 /* Enable warning and error messages in wimlib to be more user-friendly.
4765 wimlib_set_print_errors(true);
4767 /* Initialize wimlib. */
4768 ret = wimlib_global_init(init_flags);
4770 goto out_check_status;
4772 /* Call the command handler function. */
4773 ret = imagex_commands[cmd].func(argc, argv, cmd);
4775 /* Check for error writing to standard output, especially since for some
4776 * commands, writing to standard output is part of the program's actual
4777 * behavior and not just for informational purposes. */
4778 if (ferror(stdout) || fclose(stdout)) {
4779 imagex_error_with_errno(T("error writing to standard output"));
4784 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4785 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4786 * error code from which an error message can be printed. */
4788 imagex_error(T("Exiting with error code %d:\n"
4790 wimlib_get_error_string(ret));
4791 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4792 imagex_error_with_errno(T("errno"));
4794 /* Make wimlib free any resources it's holding (although this is not
4795 * strictly necessary because the process is ending anyway). */
4796 wimlib_global_cleanup();