4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012-2016 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)
63 /* Don't confuse the user by presenting the mounting commands on Windows when
64 * they will never work. However on UNIX-like systems we always present them,
65 * even if WITH_FUSE is not defined at this point, as to not tie the build of
66 * wimlib-imagex to a specific build of wimlib. */
68 # define WIM_MOUNTING_SUPPORTED 0
70 # define WIM_MOUNTING_SUPPORTED 1
73 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
76 is_any_path_separator(tchar c)
78 return c == T('/') || c == T('\\');
81 /* Like basename(), but handles both forward and backwards slashes. */
83 tbasename(tchar *path)
85 tchar *p = tstrchr(path, T('\0'));
90 if (!is_any_path_separator(*--p))
98 if (is_any_path_separator(*--p))
103 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
117 #if WIM_MOUNTING_SUPPORTED
123 #if WIM_MOUNTING_SUPPORTED
131 static void usage(int cmd, FILE *fp);
132 static void usage_all(FILE *fp);
133 static void recommend_man_page(int cmd, FILE *fp);
134 static const tchar *get_cmd_string(int cmd, bool nospace);
136 static bool imagex_be_quiet = false;
137 static FILE *imagex_info_file;
139 #define imagex_printf(format, ...) \
140 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
143 IMAGEX_ALLOW_OTHER_OPTION,
147 IMAGEX_CHUNK_SIZE_OPTION,
148 IMAGEX_COMMAND_OPTION,
149 IMAGEX_COMMIT_OPTION,
150 IMAGEX_COMPACT_OPTION,
151 IMAGEX_COMPRESS_OPTION,
152 IMAGEX_COMPRESS_SLOW_OPTION,
153 IMAGEX_CONFIG_OPTION,
155 IMAGEX_DELTA_FROM_OPTION,
156 IMAGEX_DEREFERENCE_OPTION,
157 IMAGEX_DEST_DIR_OPTION,
158 IMAGEX_DETAILED_OPTION,
159 IMAGEX_EXTRACT_XML_OPTION,
162 IMAGEX_HEADER_OPTION,
163 IMAGEX_IMAGE_PROPERTY_OPTION,
164 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
166 IMAGEX_METADATA_OPTION,
167 IMAGEX_NEW_IMAGE_OPTION,
168 IMAGEX_NOCHECK_OPTION,
169 IMAGEX_NORPFIX_OPTION,
170 IMAGEX_NOT_PIPABLE_OPTION,
171 IMAGEX_NO_ACLS_OPTION,
172 IMAGEX_NO_ATTRIBUTES_OPTION,
173 IMAGEX_NO_GLOBS_OPTION,
174 IMAGEX_NO_REPLACE_OPTION,
175 IMAGEX_NO_SOLID_SORT_OPTION,
176 IMAGEX_NULLGLOB_OPTION,
177 IMAGEX_ONE_FILE_ONLY_OPTION,
179 IMAGEX_PIPABLE_OPTION,
180 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
181 IMAGEX_REBUILD_OPTION,
182 IMAGEX_RECOMPRESS_OPTION,
183 IMAGEX_RECURSIVE_OPTION,
186 IMAGEX_SNAPSHOT_OPTION,
188 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
189 IMAGEX_SOLID_COMPRESS_OPTION,
191 IMAGEX_SOURCE_LIST_OPTION,
192 IMAGEX_STAGING_DIR_OPTION,
193 IMAGEX_STREAMS_INTERFACE_OPTION,
194 IMAGEX_STRICT_ACLS_OPTION,
195 IMAGEX_THREADS_OPTION,
196 IMAGEX_TO_STDOUT_OPTION,
197 IMAGEX_UNIX_DATA_OPTION,
198 IMAGEX_UNSAFE_COMPACT_OPTION,
199 IMAGEX_UPDATE_OF_OPTION,
200 IMAGEX_VERBOSE_OPTION,
201 IMAGEX_WIMBOOT_CONFIG_OPTION,
202 IMAGEX_WIMBOOT_OPTION,
206 static const struct option apply_options[] = {
207 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
208 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
209 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
210 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
211 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
212 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
213 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
214 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
215 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
216 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
217 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
218 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
219 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
223 static const struct option capture_or_append_options[] = {
224 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
225 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
226 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
227 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
228 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
229 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
230 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
231 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
232 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
233 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
234 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
235 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
236 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
237 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
238 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
239 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
240 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
241 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
242 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
243 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
244 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
245 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
246 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
247 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
248 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
249 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
250 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
251 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
252 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
253 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
254 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
255 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
259 static const struct option delete_options[] = {
260 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
261 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
262 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
266 static const struct option dir_options[] = {
267 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
268 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
269 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
270 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
274 static const struct option export_options[] = {
275 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
276 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
277 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
278 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
279 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
280 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
281 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
282 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
283 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
284 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
285 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
286 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
287 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
288 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
289 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
290 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
291 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
292 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
293 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
297 static const struct option extract_options[] = {
298 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
299 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
300 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
301 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
302 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
303 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
304 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
305 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
306 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
307 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
308 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
309 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
310 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
311 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
312 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
313 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
314 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
318 static const struct option info_options[] = {
319 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
320 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
321 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
322 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
323 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
324 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
325 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
326 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
327 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
328 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
329 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
333 static const struct option join_options[] = {
334 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
338 static const struct option mount_options[] = {
339 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
340 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
341 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
342 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
343 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
344 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
345 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
349 static const struct option optimize_options[] = {
350 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
351 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
352 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
353 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
354 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
355 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
356 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
357 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
358 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
359 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
360 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
361 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
362 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
363 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
364 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
365 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
369 static const struct option split_options[] = {
370 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
374 static const struct option unmount_options[] = {
375 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
376 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
377 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
378 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
379 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
380 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
384 static const struct option update_options[] = {
385 /* Careful: some of the options here set the defaults for update
386 * commands, but the flags given to an actual update command (and not to
387 * `imagex update' itself are also handled in
388 * update_command_add_option(). */
389 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
390 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
391 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
392 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
393 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
395 /* Default delete options */
396 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
397 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
399 /* Global add option */
400 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
402 /* Default add options */
403 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
404 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
405 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
406 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
407 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
408 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
409 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
410 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
415 static const struct option verify_options[] = {
416 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
417 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
423 # define _format_attribute(type, format_str, args_start) \
424 __attribute__((format(type, format_str, args_start)))
426 # define _format_attribute(type, format_str, args_start)
429 /* Print formatted error message to stderr. */
430 static void _format_attribute(printf, 1, 2)
431 imagex_error(const tchar *format, ...)
434 va_start(va, format);
435 tfputs(T("ERROR: "), stderr);
436 tvfprintf(stderr, format, va);
437 tputc(T('\n'), stderr);
441 /* Print formatted error message to stderr. */
442 static void _format_attribute(printf, 1, 2)
443 imagex_error_with_errno(const tchar *format, ...)
445 int errno_save = errno;
447 va_start(va, format);
448 tfputs(T("ERROR: "), stderr);
449 tvfprintf(stderr, format, va);
450 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
455 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
457 if (image == WIMLIB_NO_IMAGE) {
458 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
459 " Please specify a 1-based image index or "
460 "image name. To list the images\n"
461 " contained in the WIM archive, run\n"
463 " %"TS" \"%"TS"\"\n"),
464 image_name, wim_name,
465 get_cmd_string(CMD_INFO, false), wim_name);
466 return WIMLIB_ERR_INVALID_IMAGE;
472 verify_image_is_single(int image)
474 if (image == WIMLIB_ALL_IMAGES) {
475 imagex_error(T("Cannot specify all images for this action!"));
476 return WIMLIB_ERR_INVALID_IMAGE;
482 verify_image_exists_and_is_single(int image, const tchar *image_name,
483 const tchar *wim_name)
486 ret = verify_image_exists(image, image_name, wim_name);
488 ret = verify_image_is_single(image);
493 print_available_compression_types(FILE *fp)
495 static const tchar *s =
497 "Available compression types:\n"
500 " xpress (alias: \"fast\")\n"
501 " lzx (alias: \"maximum\") (default for capture)\n"
502 " lzms (alias: \"recovery\")\n"
508 /* Parse the argument to --compress or --solid-compress */
510 get_compression_type(tchar *optarg, bool solid)
513 unsigned int compression_level = 0;
516 plevel = tstrchr(optarg, T(':'));
522 ultmp = tstrtoul(plevel, &ptmp, 10);
523 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
524 imagex_error(T("Compression level must be a positive integer! "
525 "e.g. --compress=lzx:80"));
526 return WIMLIB_COMPRESSION_TYPE_INVALID;
528 compression_level = ultmp;
531 if (!tstrcasecmp(optarg, T("maximum")) ||
532 !tstrcasecmp(optarg, T("lzx")) ||
533 !tstrcasecmp(optarg, T("max"))) {
534 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
535 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
536 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
537 } else if (!tstrcasecmp(optarg, T("recovery"))) {
541 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
542 " differently from DISM. Instead, you typically want to use '--solid' to\n"
543 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
544 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
545 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
546 " of '--compress=recovery'.\n"));
548 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
549 } else if (!tstrcasecmp(optarg, T("lzms"))) {
550 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
551 } else if (!tstrcasecmp(optarg, T("none"))) {
552 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
554 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
555 print_available_compression_types(stderr);
556 return WIMLIB_COMPRESSION_TYPE_INVALID;
559 if (compression_level != 0)
560 wimlib_set_default_compression_level(ctype, compression_level);
564 /* Parse the argument to --compact */
566 set_compact_mode(const tchar *arg, int *extract_flags)
569 if (!tstrcasecmp(arg, T("xpress4k")))
570 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
571 else if (!tstrcasecmp(arg, T("xpress8k")))
572 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
573 else if (!tstrcasecmp(arg, T("xpress16k")))
574 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
575 else if (!tstrcasecmp(arg, T("lzx")))
576 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
579 *extract_flags |= flag;
584 "\"%"TS"\" is not a recognized System Compression format. The options are:"
586 " --compact=xpress4k\n"
587 " --compact=xpress8k\n"
588 " --compact=xpress16k\n"
596 set_compress_slow(void)
599 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
600 " Use the '--compress=TYPE:LEVEL' option instead.\n");
602 wimlib_set_default_compression_level(-1, 100);
607 unsigned num_strings;
608 unsigned num_alloc_strings;
611 #define STRING_SET_INITIALIZER \
612 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
614 #define STRING_SET(_strings) \
615 struct string_set _strings = STRING_SET_INITIALIZER
618 string_set_append(struct string_set *set, tchar *glob)
620 unsigned num_alloc_strings = set->num_alloc_strings;
622 if (set->num_strings == num_alloc_strings) {
625 num_alloc_strings += 4;
626 new_strings = realloc(set->strings,
627 sizeof(set->strings[0]) * num_alloc_strings);
629 imagex_error(T("Out of memory!"));
632 set->strings = new_strings;
633 set->num_alloc_strings = num_alloc_strings;
635 set->strings[set->num_strings++] = glob;
640 string_set_destroy(struct string_set *set)
646 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
648 return wimlib_reference_resource_files(wim, (const tchar **)set->strings,
650 WIMLIB_REF_FLAG_GLOB_ENABLE,
655 append_image_property_argument(struct string_set *image_properties)
657 if (!tstrchr(optarg, '=')) {
658 imagex_error(T("'--image-property' argument "
659 "must be in the form NAME=VALUE"));
662 return string_set_append(image_properties, optarg);
666 apply_image_properties(struct string_set *image_properties,
667 WIMStruct *wim, int image, bool *any_changes_ret)
669 bool any_changes = false;
670 for (unsigned i = 0; i < image_properties->num_strings; i++) {
672 const tchar *current_value;
675 name = image_properties->strings[i];
676 value = tstrchr(name, '=');
679 current_value = wimlib_get_image_property(wim, image, name);
680 if (current_value && !tstrcmp(current_value, value)) {
681 imagex_printf(T("The %"TS" property of image %d "
682 "already has value \"%"TS"\".\n"),
685 imagex_printf(T("Setting the %"TS" property of image "
686 "%d to \"%"TS"\".\n"),
688 ret = wimlib_set_image_property(wim, image, name, value);
695 *any_changes_ret = any_changes;
700 do_resource_not_found_warning(const tchar *wimfile,
701 const struct wimlib_wim_info *info,
702 const struct string_set *refglobs)
704 if (info->total_parts > 1) {
705 if (refglobs->num_strings == 0) {
706 imagex_error(T("\"%"TS"\" is part of a split WIM. "
707 "Use --ref to specify the other parts."),
710 imagex_error(T("Perhaps the '--ref' argument did not "
711 "specify all other parts of the split "
715 imagex_error(T("If this is a delta WIM, use the --ref argument "
716 "to specify the WIM(s) on which it is based."));
721 do_metadata_not_found_warning(const tchar *wimfile,
722 const struct wimlib_wim_info *info)
724 if (info->part_number != 1) {
725 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
726 " You must specify the first part."),
731 /* Returns the size of a file given its name, or -1 if the file does not exist
732 * or its size cannot be determined. */
734 file_get_size(const tchar *filename)
737 if (tstat(filename, &st) == 0)
744 PARSE_STRING_SUCCESS = 0,
745 PARSE_STRING_FAILURE = 1,
746 PARSE_STRING_NONE = 2,
750 * Parses a string token from an array of characters.
752 * Tokens are either whitespace-delimited, or double or single-quoted.
754 * @line_p: Pointer to the pointer to the line of data. Will be updated
755 * to point past the string token iff the return value is
756 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
759 * @len_p: @len_p initially stores the length of the line of data, which may
760 * be 0, and it will be updated to the number of bytes remaining in
761 * the line iff the return value is PARSE_STRING_SUCCESS.
763 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
764 * parsed string token will be returned here.
766 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
767 * PARSE_STRING_FAILURE if the data was invalid due to a missing
768 * closing quote; or PARSE_STRING_NONE if the line ended before the
769 * beginning of a string token was found.
772 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
775 tchar *line = *line_p;
779 /* Skip leading whitespace */
782 return PARSE_STRING_NONE;
783 if (!istspace(*line) && *line != T('\0'))
789 if (quote_char == T('"') || quote_char == T('\'')) {
794 line = tmemchr(line, quote_char, len);
796 imagex_error(T("Missing closing quote: %"TS), fn - 1);
797 return PARSE_STRING_FAILURE;
800 /* Unquoted string. Go until whitespace. Line is terminated
801 * by '\0', so no need to check 'len'. */
805 } while (!istspace(*line) && *line != T('\0'));
812 return PARSE_STRING_SUCCESS;
815 /* Parses a line of data (not an empty line or comment) in the source list file
816 * format. (See the man page for 'wimlib-imagex capture' for details on this
817 * format and the meaning.)
819 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
820 * len == 0. The data in @line will be modified by this function call.
822 * @len: Length of the line of data.
824 * @source: On success, the capture source and target described by the line is
825 * written into this destination. Note that it will contain pointers
826 * to data in the @line array.
828 * Returns true if the line was valid; false otherwise. */
830 parse_source_list_line(tchar *line, size_t len,
831 struct wimlib_capture_source *source)
835 ret = parse_string(&line, &len, &source->fs_source_path);
836 if (ret != PARSE_STRING_SUCCESS)
838 ret = parse_string(&line, &len, &source->wim_target_path);
839 if (ret == PARSE_STRING_NONE)
840 source->wim_target_path = source->fs_source_path;
841 return ret != PARSE_STRING_FAILURE;
844 /* Returns %true if the given line of length @len > 0 is a comment or empty line
845 * in the source list file format. */
847 is_comment_line(const tchar *line, size_t len)
850 if (*line == T('#') || *line == T(';'))
852 if (!istspace(*line) && *line != T('\0'))
862 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
865 tchar *contents = *contents_p;
866 size_t nchars = *nchars_p;
869 for (i = 0; i < nchars; i++)
870 if (contents[i] == T('\n'))
873 /* Handle last line not terminated by a newline */
874 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
875 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
877 imagex_error(T("Out of memory!"));
880 contents[nchars] = T('\n');
881 *contents_p = contents;
889 /* Parses a file in the source list format. (See the man page for
890 * 'wimlib-imagex capture' for details on this format and the meaning.)
892 * @source_list_contents: Contents of the source list file. Note that this
893 * buffer will be modified to save memory allocations,
894 * and cannot be freed until the returned array of
895 * wimlib_capture_source's has also been freed.
897 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
900 * @nsources_ret: On success, the length of the returned array is
903 * Returns: An array of `struct wimlib_capture_source's that can be passed to
904 * the wimlib_add_image_multisource() function to specify how a WIM image is to
906 static struct wimlib_capture_source *
907 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
908 size_t *nsources_ret)
912 struct wimlib_capture_source *sources;
915 nlines = text_file_count_lines(source_list_contents_p,
916 &source_list_nchars);
920 /* Always allocate at least 1 slot, just in case the implementation of
921 * calloc() returns NULL if 0 bytes are requested. */
922 sources = calloc(nlines ?: 1, sizeof(*sources));
924 imagex_error(T("out of memory"));
927 p = *source_list_contents_p;
929 for (i = 0; i < nlines; i++) {
930 /* XXX: Could use rawmemchr() here instead, but it may not be
931 * available on all platforms. */
932 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
933 size_t len = endp - p + 1;
935 if (!is_comment_line(p, len)) {
936 if (!parse_source_list_line(p, len, &sources[j++])) {
948 /* Reads the contents of a file into memory. */
950 file_get_contents(const tchar *filename, size_t *len_ret)
957 if (tstat(filename, &stbuf) != 0) {
958 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
963 fp = tfopen(filename, T("rb"));
965 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
969 buf = malloc(len ? len : 1);
971 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
972 "contents of file \"%"TS"\""), len, filename);
975 if (fread(buf, 1, len, fp) != len) {
976 imagex_error_with_errno(T("Failed to read %zu bytes from the "
977 "file \"%"TS"\""), len, filename);
991 /* Read standard input until EOF and return the full contents in a malloc()ed
992 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
995 stdin_get_contents(size_t *len_ret)
997 /* stdin can, of course, be a pipe or other non-seekable file, so the
998 * total length of the data cannot be pre-determined */
1000 size_t newlen = 1024;
1004 char *p = realloc(buf, newlen);
1005 size_t bytes_read, bytes_to_read;
1007 imagex_error(T("out of memory while reading stdin"));
1011 bytes_to_read = newlen - pos;
1012 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1014 if (bytes_read != bytes_to_read) {
1019 imagex_error_with_errno(T("error reading stdin"));
1033 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1036 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1038 *num_tchars_ret = num_bytes;
1040 #else /* !__WIN32__ */
1041 /* On Windows, translate the text to UTF-16LE */
1045 if (num_bytes >= 2 &&
1046 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1047 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1049 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1050 * with something that looks like an ASCII character encoded as
1051 * a UTF-16LE code unit. Assume the file is encoded as
1052 * UTF-16LE. This is not a 100% reliable check. */
1053 num_wchars = num_bytes / 2;
1054 text_wstr = (wchar_t*)text;
1056 /* File does not look like UTF-16LE. Assume it is encoded in
1057 * the current Windows code page. I think these are always
1058 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1059 * should work as expected. */
1060 text_wstr = win32_mbs_to_wcs(text,
1065 *num_tchars_ret = num_wchars;
1067 #endif /* __WIN32__ */
1071 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1076 contents = file_get_contents(filename, &num_bytes);
1079 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1083 stdin_get_text_contents(size_t *num_tchars_ret)
1088 contents = stdin_get_contents(&num_bytes);
1091 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1094 #define TO_PERCENT(numerator, denominator) \
1095 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1097 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1098 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1099 #define KIBIBYTE_MIN_NBYTES 10000ULL
1102 get_unit(uint64_t total_bytes, const tchar **name_ret)
1104 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1105 *name_ret = T("GiB");
1107 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1108 *name_ret = T("MiB");
1110 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1111 *name_ret = T("KiB");
1114 *name_ret = T("bytes");
1119 static struct wimlib_progress_info_scan last_scan_progress;
1122 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1124 uint64_t prev_count, cur_count;
1126 prev_count = last_scan_progress.num_nondirs_scanned +
1127 last_scan_progress.num_dirs_scanned;
1128 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1130 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1131 cur_count % 128 == 0)
1133 unsigned unit_shift;
1134 const tchar *unit_name;
1136 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1137 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1138 "%"PRIu64" directories) "),
1139 scan->num_bytes_scanned >> unit_shift,
1141 scan->num_nondirs_scanned,
1142 scan->num_dirs_scanned);
1143 last_scan_progress = *scan;
1146 /* Progress callback function passed to various wimlib functions. */
1147 static enum wimlib_progress_status
1148 imagex_progress_func(enum wimlib_progress_msg msg,
1149 union wimlib_progress_info *info,
1150 void *_ignored_context)
1152 unsigned percent_done;
1153 unsigned unit_shift;
1154 const tchar *unit_name;
1156 if (imagex_be_quiet)
1157 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1159 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1161 static bool started;
1163 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1164 imagex_printf(T("Using %"TS" compression "
1165 "with %u thread%"TS"\n"),
1166 wimlib_get_compression_type_string(
1167 info->write_streams.compression_type),
1168 info->write_streams.num_threads,
1169 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1174 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1175 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1176 info->write_streams.total_bytes);
1178 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1179 info->write_streams.completed_bytes >> unit_shift,
1181 info->write_streams.total_bytes >> unit_shift,
1184 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1185 imagex_printf(T("\n"));
1187 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1188 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1189 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1190 imagex_printf(T("\n"));
1192 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1193 info->scan.wim_target_path);
1195 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1197 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1198 switch (info->scan.status) {
1199 case WIMLIB_SCAN_DENTRY_OK:
1200 report_scan_progress(&info->scan, false);
1202 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1203 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1205 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1206 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1207 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1209 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1210 /* Symlink fixups are enabled by default. This is
1211 * mainly intended for Windows, which for some reason
1212 * uses absolute junctions (with drive letters!) in the
1213 * default installation. On UNIX-like systems, warn the
1214 * user when fixing the target of an absolute symbolic
1215 * link, so they know to disable this if they want. */
1217 imagex_printf(T("\nWARNING: Adjusted target of "
1218 "absolute symbolic link \"%"TS"\"\n"
1219 " (Use --norpfix to capture "
1220 "absolute symbolic links as-is)\n"),
1221 info->scan.cur_path);
1228 case WIMLIB_PROGRESS_MSG_SCAN_END:
1229 report_scan_progress(&info->scan, true);
1230 imagex_printf(T("\n"));
1232 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1233 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1234 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1235 info->integrity.total_bytes);
1236 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1237 "of %"PRIu64" %"TS" (%u%%) done"),
1238 info->integrity.filename,
1239 info->integrity.completed_bytes >> unit_shift,
1241 info->integrity.total_bytes >> unit_shift,
1244 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1245 imagex_printf(T("\n"));
1247 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1248 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1249 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1250 info->integrity.total_bytes);
1251 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1252 "of %"PRIu64" %"TS" (%u%%) done"),
1253 info->integrity.completed_bytes >> unit_shift,
1255 info->integrity.total_bytes >> unit_shift,
1258 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1259 imagex_printf(T("\n"));
1261 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1262 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1263 "to %"TS" \"%"TS"\"\n"),
1264 info->extract.image,
1265 info->extract.image_name,
1266 info->extract.wimfile_name,
1267 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1268 T("NTFS volume") : T("directory")),
1269 info->extract.target);
1271 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1272 if (info->extract.end_file_count >= 2000) {
1273 percent_done = TO_PERCENT(info->extract.current_file_count,
1274 info->extract.end_file_count);
1275 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1276 info->extract.current_file_count,
1277 info->extract.end_file_count, percent_done);
1278 if (info->extract.current_file_count == info->extract.end_file_count)
1279 imagex_printf(T("\n"));
1282 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1283 percent_done = TO_PERCENT(info->extract.completed_bytes,
1284 info->extract.total_bytes);
1285 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1286 imagex_printf(T("\rExtracting file data: "
1287 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1288 info->extract.completed_bytes >> unit_shift,
1290 info->extract.total_bytes >> unit_shift,
1293 if (info->extract.completed_bytes >= info->extract.total_bytes)
1294 imagex_printf(T("\n"));
1296 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1297 if (info->extract.end_file_count >= 2000) {
1298 percent_done = TO_PERCENT(info->extract.current_file_count,
1299 info->extract.end_file_count);
1300 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1301 info->extract.current_file_count,
1302 info->extract.end_file_count, percent_done);
1303 if (info->extract.current_file_count == info->extract.end_file_count)
1304 imagex_printf(T("\n"));
1307 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1308 if (info->extract.total_parts != 1) {
1309 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1310 info->extract.part_number,
1311 info->extract.total_parts);
1314 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1315 percent_done = TO_PERCENT(info->split.completed_bytes,
1316 info->split.total_bytes);
1317 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1318 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1319 "%"PRIu64" %"TS" (%u%%) written\n"),
1320 info->split.part_name,
1321 info->split.cur_part_number,
1322 info->split.total_parts,
1323 info->split.completed_bytes >> unit_shift,
1325 info->split.total_bytes >> unit_shift,
1329 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1330 if (info->split.completed_bytes == info->split.total_bytes) {
1331 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1332 info->split.cur_part_number,
1333 info->split.total_parts);
1336 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1337 switch (info->update.command->op) {
1338 case WIMLIB_UPDATE_OP_DELETE:
1339 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1340 info->update.command->delete_.wim_path);
1342 case WIMLIB_UPDATE_OP_RENAME:
1343 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1344 info->update.command->rename.wim_source_path,
1345 info->update.command->rename.wim_target_path);
1347 case WIMLIB_UPDATE_OP_ADD:
1352 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1353 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1354 info->replace.path_in_wim);
1356 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1357 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1358 info->wimboot_exclude.path_in_wim);
1360 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1361 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1362 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1363 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1364 info->unmount.mounted_wim,
1365 info->unmount.mounted_image);
1367 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1368 info->unmount.mounted_wim,
1369 info->unmount.mounted_image);
1370 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1374 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1375 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1376 info->verify_image.current_image,
1377 info->verify_image.total_images);
1379 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1380 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1381 info->verify_streams.total_bytes);
1382 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1383 imagex_printf(T("\rVerifying file data: "
1384 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1385 info->verify_streams.completed_bytes >> unit_shift,
1387 info->verify_streams.total_bytes >> unit_shift,
1390 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1391 imagex_printf(T("\n"));
1396 fflush(imagex_info_file);
1397 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1401 parse_num_threads(const tchar *optarg)
1404 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1405 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1406 imagex_error(T("Number of threads must be a non-negative integer!"));
1414 parse_chunk_size(const tchar *optarg)
1417 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1418 if (chunk_size == 0) {
1419 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1420 " with optional K, M, or G suffix"));
1424 if (*tmp == T('k') || *tmp == T('K')) {
1427 } else if (*tmp == T('m') || *tmp == T('M')) {
1430 } else if (*tmp == T('g') || *tmp == T('G')) {
1434 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1435 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1439 if (chunk_size >= UINT32_MAX) {
1440 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1448 * Parse an option passed to an update command.
1450 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1453 * @option: Text string for the option (beginning with --)
1455 * @cmd: `struct wimlib_update_command' that is being constructed for
1458 * Returns true if the option was recognized; false if not.
1461 update_command_add_option(int op, const tchar *option,
1462 struct wimlib_update_command *cmd)
1464 bool recognized = true;
1466 case WIMLIB_UPDATE_OP_ADD:
1467 if (!tstrcmp(option, T("--verbose")))
1468 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1469 else if (!tstrcmp(option, T("--unix-data")))
1470 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1471 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1472 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1473 else if (!tstrcmp(option, T("--strict-acls")))
1474 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1475 else if (!tstrcmp(option, T("--dereference")))
1476 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1477 else if (!tstrcmp(option, T("--no-replace")))
1478 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1482 case WIMLIB_UPDATE_OP_DELETE:
1483 if (!tstrcmp(option, T("--force")))
1484 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1485 else if (!tstrcmp(option, T("--recursive")))
1486 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1497 /* How many nonoption arguments each `imagex update' command expects */
1498 static const unsigned update_command_num_nonoptions[] = {
1499 [WIMLIB_UPDATE_OP_ADD] = 2,
1500 [WIMLIB_UPDATE_OP_DELETE] = 1,
1501 [WIMLIB_UPDATE_OP_RENAME] = 2,
1505 update_command_add_nonoption(int op, const tchar *nonoption,
1506 struct wimlib_update_command *cmd,
1507 unsigned num_nonoptions)
1510 case WIMLIB_UPDATE_OP_ADD:
1511 if (num_nonoptions == 0)
1512 cmd->add.fs_source_path = (tchar*)nonoption;
1514 cmd->add.wim_target_path = (tchar*)nonoption;
1516 case WIMLIB_UPDATE_OP_DELETE:
1517 cmd->delete_.wim_path = (tchar*)nonoption;
1519 case WIMLIB_UPDATE_OP_RENAME:
1520 if (num_nonoptions == 0)
1521 cmd->rename.wim_source_path = (tchar*)nonoption;
1523 cmd->rename.wim_target_path = (tchar*)nonoption;
1529 * Parse a command passed on stdin to `imagex update'.
1531 * @line: Text of the command.
1532 * @len: Length of the line, including a null terminator
1535 * @command: A `struct wimlib_update_command' to fill in from the parsed
1538 * @line_number: Line number of the command, for diagnostics.
1540 * Returns true on success; returns false on parse error.
1543 parse_update_command(tchar *line, size_t len,
1544 struct wimlib_update_command *command,
1548 tchar *command_name;
1550 size_t num_nonoptions;
1552 /* Get the command name ("add", "delete", "rename") */
1553 ret = parse_string(&line, &len, &command_name);
1554 if (ret != PARSE_STRING_SUCCESS)
1557 if (!tstrcasecmp(command_name, T("add"))) {
1558 op = WIMLIB_UPDATE_OP_ADD;
1559 } else if (!tstrcasecmp(command_name, T("delete"))) {
1560 op = WIMLIB_UPDATE_OP_DELETE;
1561 } else if (!tstrcasecmp(command_name, T("rename"))) {
1562 op = WIMLIB_UPDATE_OP_RENAME;
1564 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1565 command_name, line_number);
1570 /* Parse additional options and non-options as needed */
1575 ret = parse_string(&line, &len, &next_string);
1576 if (ret == PARSE_STRING_NONE) /* End of line */
1578 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1580 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1582 if (!update_command_add_option(op, next_string, command))
1584 imagex_error(T("Unrecognized option \"%"TS"\" to "
1585 "update command \"%"TS"\" on line %zu"),
1586 next_string, command_name, line_number);
1592 if (num_nonoptions == update_command_num_nonoptions[op])
1594 imagex_error(T("Unexpected argument \"%"TS"\" in "
1595 "update command on line %zu\n"
1596 " (The \"%"TS"\" command only "
1597 "takes %zu nonoption arguments!)\n"),
1598 next_string, line_number,
1599 command_name, num_nonoptions);
1602 update_command_add_nonoption(op, next_string,
1603 command, num_nonoptions);
1608 if (num_nonoptions != update_command_num_nonoptions[op]) {
1609 imagex_error(T("Not enough arguments to update command "
1610 "\"%"TS"\" on line %zu"), command_name, line_number);
1616 static struct wimlib_update_command *
1617 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1618 size_t *num_cmds_ret)
1622 struct wimlib_update_command *cmds;
1625 nlines = text_file_count_lines(cmd_file_contents_p,
1630 /* Always allocate at least 1 slot, just in case the implementation of
1631 * calloc() returns NULL if 0 bytes are requested. */
1632 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1634 imagex_error(T("out of memory"));
1637 p = *cmd_file_contents_p;
1639 for (i = 0; i < nlines; i++) {
1640 /* XXX: Could use rawmemchr() here instead, but it may not be
1641 * available on all platforms. */
1642 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1643 size_t len = endp - p + 1;
1645 if (!is_comment_line(p, len)) {
1646 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1657 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1658 * one image from a WIM file to an NTFS volume. */
1660 imagex_apply(int argc, tchar **argv, int cmd)
1664 int image = WIMLIB_NO_IMAGE;
1666 struct wimlib_wim_info info;
1668 const tchar *wimfile;
1669 const tchar *target;
1670 const tchar *image_num_or_name = NULL;
1671 int extract_flags = 0;
1673 STRING_SET(refglobs);
1675 for_opt(c, apply_options) {
1677 case IMAGEX_CHECK_OPTION:
1678 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1680 case IMAGEX_VERBOSE_OPTION:
1681 /* No longer does anything. */
1683 case IMAGEX_REF_OPTION:
1684 ret = string_set_append(&refglobs, optarg);
1686 goto out_free_refglobs;
1688 case IMAGEX_UNIX_DATA_OPTION:
1689 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1691 case IMAGEX_NO_ACLS_OPTION:
1692 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1694 case IMAGEX_STRICT_ACLS_OPTION:
1695 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1697 case IMAGEX_NO_ATTRIBUTES_OPTION:
1698 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1700 case IMAGEX_NORPFIX_OPTION:
1701 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1703 case IMAGEX_RPFIX_OPTION:
1704 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1706 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1707 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1708 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1710 case IMAGEX_WIMBOOT_OPTION:
1711 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1713 case IMAGEX_COMPACT_OPTION:
1714 ret = set_compact_mode(optarg, &extract_flags);
1716 goto out_free_refglobs;
1724 if (argc != 2 && argc != 3)
1729 if (!tstrcmp(wimfile, T("-"))) {
1730 /* Attempt to apply pipable WIM from standard input. */
1732 image_num_or_name = NULL;
1735 image_num_or_name = argv[1];
1740 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1741 imagex_progress_func, NULL);
1743 goto out_free_refglobs;
1745 wimlib_get_wim_info(wim, &info);
1748 /* Image explicitly specified. */
1749 image_num_or_name = argv[1];
1750 image = wimlib_resolve_image(wim, image_num_or_name);
1751 ret = verify_image_exists(image, image_num_or_name, wimfile);
1753 goto out_wimlib_free;
1756 /* No image specified; default to image 1, but only if the WIM
1757 * contains exactly one image. */
1759 if (info.image_count != 1) {
1760 imagex_error(T("\"%"TS"\" contains %d images; "
1761 "Please select one (or all)."),
1762 wimfile, info.image_count);
1771 if (refglobs.num_strings) {
1773 imagex_error(T("Can't specify --ref when applying from stdin!"));
1775 goto out_wimlib_free;
1777 ret = wim_reference_globs(wim, &refglobs, open_flags);
1779 goto out_wimlib_free;
1784 /* Interpret a regular file or block device target as an NTFS
1788 if (tstat(target, &stbuf)) {
1789 if (errno != ENOENT) {
1790 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1793 goto out_wimlib_free;
1796 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1797 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1803 ret = wimlib_extract_image(wim, image, target, extract_flags);
1805 set_fd_to_binary_mode(STDIN_FILENO);
1806 ret = wimlib_extract_image_from_pipe_with_progress(
1811 imagex_progress_func,
1815 imagex_printf(T("Done applying WIM image.\n"));
1816 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1818 do_resource_not_found_warning(wimfile, &info, &refglobs);
1820 imagex_error(T( "If you are applying an image "
1821 "from a split pipable WIM,\n"
1822 " make sure you have "
1823 "concatenated together all parts."));
1825 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1826 do_metadata_not_found_warning(wimfile, &info);
1831 string_set_destroy(&refglobs);
1835 usage(CMD_APPLY, stderr);
1837 goto out_free_refglobs;
1840 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1841 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1842 * the desired image. 'wimlib-imagex append': add a new image to an existing
1845 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1849 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1850 WIMLIB_ADD_FLAG_WINCONFIG |
1851 WIMLIB_ADD_FLAG_VERBOSE;
1852 int write_flags = 0;
1853 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1854 uint32_t chunk_size = UINT32_MAX;
1855 uint32_t solid_chunk_size = UINT32_MAX;
1856 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1857 const tchar *wimfile;
1860 STRING_SET(image_properties);
1863 STRING_SET(base_wimfiles);
1864 WIMStruct **base_wims;
1866 WIMStruct *template_wim;
1867 const tchar *template_wimfile = NULL;
1868 const tchar *template_image_name_or_num = NULL;
1869 int template_image = WIMLIB_NO_IMAGE;
1872 unsigned num_threads = 0;
1877 tchar *config_file = NULL;
1879 bool source_list = false;
1880 size_t source_list_nchars = 0;
1881 tchar *source_list_contents;
1882 bool capture_sources_malloced;
1883 struct wimlib_capture_source *capture_sources;
1885 bool name_defaulted;
1887 for_opt(c, capture_or_append_options) {
1889 case IMAGEX_BOOT_OPTION:
1890 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1892 case IMAGEX_CHECK_OPTION:
1893 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1894 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1896 case IMAGEX_NOCHECK_OPTION:
1897 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1899 case IMAGEX_CONFIG_OPTION:
1900 config_file = optarg;
1901 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1903 case IMAGEX_COMPRESS_OPTION:
1904 compression_type = get_compression_type(optarg, false);
1905 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1908 case IMAGEX_COMPRESS_SLOW_OPTION:
1909 set_compress_slow();
1911 case IMAGEX_CHUNK_SIZE_OPTION:
1912 chunk_size = parse_chunk_size(optarg);
1913 if (chunk_size == UINT32_MAX)
1916 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1917 solid_chunk_size = parse_chunk_size(optarg);
1918 if (solid_chunk_size == UINT32_MAX)
1921 case IMAGEX_SOLID_COMPRESS_OPTION:
1922 solid_ctype = get_compression_type(optarg, true);
1923 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1926 case IMAGEX_SOLID_OPTION:
1927 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1929 case IMAGEX_NO_SOLID_SORT_OPTION:
1930 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1932 case IMAGEX_FLAGS_OPTION: {
1933 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1934 tsprintf(p, T("FLAGS=%"TS), optarg);
1935 ret = string_set_append(&image_properties, p);
1940 case IMAGEX_IMAGE_PROPERTY_OPTION:
1941 ret = append_image_property_argument(&image_properties);
1945 case IMAGEX_DEREFERENCE_OPTION:
1946 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1948 case IMAGEX_VERBOSE_OPTION:
1949 /* No longer does anything. */
1951 case IMAGEX_THREADS_OPTION:
1952 num_threads = parse_num_threads(optarg);
1953 if (num_threads == UINT_MAX)
1956 case IMAGEX_REBUILD_OPTION:
1957 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1959 case IMAGEX_UNIX_DATA_OPTION:
1960 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1962 case IMAGEX_SOURCE_LIST_OPTION:
1965 case IMAGEX_NO_ACLS_OPTION:
1966 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1968 case IMAGEX_STRICT_ACLS_OPTION:
1969 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1971 case IMAGEX_RPFIX_OPTION:
1972 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1974 case IMAGEX_NORPFIX_OPTION:
1975 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1977 case IMAGEX_PIPABLE_OPTION:
1978 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1980 case IMAGEX_NOT_PIPABLE_OPTION:
1981 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1983 case IMAGEX_UPDATE_OF_OPTION:
1984 if (template_image_name_or_num) {
1985 imagex_error(T("'--update-of' can only be "
1986 "specified one time!"));
1990 colon = tstrrchr(optarg, T(':'));
1993 template_wimfile = optarg;
1995 template_image_name_or_num = colon + 1;
1997 template_wimfile = NULL;
1998 template_image_name_or_num = optarg;
2002 case IMAGEX_DELTA_FROM_OPTION:
2003 if (cmd != CMD_CAPTURE) {
2004 imagex_error(T("'--delta-from' is only "
2005 "valid for capture!"));
2008 ret = string_set_append(&base_wimfiles, optarg);
2011 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2013 case IMAGEX_WIMBOOT_OPTION:
2014 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2016 case IMAGEX_UNSAFE_COMPACT_OPTION:
2017 if (cmd != CMD_APPEND) {
2018 imagex_error(T("'--unsafe-compact' is only "
2019 "valid for append!"));
2022 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2024 case IMAGEX_SNAPSHOT_OPTION:
2025 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2034 if (argc < 2 || argc > 4)
2040 /* Set default compression type and parameters. */
2043 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2044 /* No compression type specified. Use the default. */
2046 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2047 /* With --wimboot, default to XPRESS compression. */
2048 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2049 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2050 /* With --solid, default to LZMS compression. (However,
2051 * this will not affect solid resources!) */
2052 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2054 /* Otherwise, default to LZX compression. */
2055 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2059 if (!tstrcmp(wimfile, T("-"))) {
2060 /* Writing captured WIM to standard output. */
2062 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2063 imagex_error("Can't write a non-pipable WIM to "
2064 "standard output! Specify --pipable\n"
2065 " if you want to create a pipable WIM "
2066 "(but read the docs first).");
2070 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2072 if (cmd == CMD_APPEND) {
2073 imagex_error(T("Using standard output for append does "
2074 "not make sense."));
2077 wim_fd = STDOUT_FILENO;
2079 imagex_info_file = stderr;
2080 set_fd_to_binary_mode(wim_fd);
2083 /* If template image was specified using --update-of=IMAGE rather
2084 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2085 if (template_image_name_or_num && !template_wimfile) {
2086 if (base_wimfiles.num_strings == 1) {
2087 /* Capturing delta WIM based on single WIM: default to
2089 template_wimfile = base_wimfiles.strings[0];
2090 } else if (cmd == CMD_APPEND) {
2091 /* Appending to WIM: default to WIM being appended to.
2093 template_wimfile = wimfile;
2095 /* Capturing a normal (non-delta) WIM, so the WIM file
2096 * *must* be explicitly specified. */
2097 if (base_wimfiles.num_strings > 1) {
2098 imagex_error(T("For capture of delta WIM "
2099 "based on multiple existing "
2101 " '--update-of' must "
2102 "specify WIMFILE:IMAGE!"));
2104 imagex_error(T("For capture of non-delta WIM, "
2105 "'--update-of' must specify "
2114 name_defaulted = false;
2116 /* Set default name to SOURCE argument, omitting any directory
2117 * prefixes and trailing slashes. This requires making a copy
2118 * of @source. Leave some free characters at the end in case we
2119 * append a number to keep the name unique. */
2120 size_t source_name_len;
2122 source_name_len = tstrlen(source);
2123 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2124 name = tbasename(tstrcpy(source_copy, source));
2125 name_defaulted = true;
2128 /* Image description (if given). */
2130 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2131 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2132 ret = string_set_append(&image_properties, p);
2138 /* Set up capture sources in source list mode */
2139 if (source[0] == T('-') && source[1] == T('\0')) {
2140 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2142 source_list_contents = file_get_text_contents(source,
2143 &source_list_nchars);
2145 if (!source_list_contents)
2148 capture_sources = parse_source_list(&source_list_contents,
2151 if (!capture_sources) {
2153 goto out_free_source_list_contents;
2155 capture_sources_malloced = true;
2157 /* Set up capture source in non-source-list mode. */
2158 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2159 capture_sources[0].fs_source_path = source;
2160 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2161 capture_sources[0].reserved = 0;
2163 capture_sources_malloced = false;
2164 source_list_contents = NULL;
2167 /* Open the existing WIM, or create a new one. */
2168 if (cmd == CMD_APPEND) {
2169 ret = wimlib_open_wim_with_progress(wimfile,
2170 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2172 imagex_progress_func,
2175 goto out_free_capture_sources;
2177 ret = wimlib_create_new_wim(compression_type, &wim);
2179 goto out_free_capture_sources;
2180 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2183 /* Set chunk size if non-default. */
2184 if (chunk_size != UINT32_MAX) {
2185 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2188 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2190 int ctype = compression_type;
2192 if (cmd == CMD_APPEND) {
2193 struct wimlib_wim_info info;
2194 wimlib_get_wim_info(wim, &info);
2195 ctype = info.compression_type;
2198 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2199 ret = wimlib_set_output_chunk_size(wim, 4096);
2204 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2205 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2209 if (solid_chunk_size != UINT32_MAX) {
2210 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2216 /* Detect if source is regular file or block device and set NTFS volume
2221 if (tstat(source, &stbuf) == 0) {
2222 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2223 imagex_printf(T("Capturing WIM image from NTFS "
2224 "filesystem on \"%"TS"\"\n"), source);
2225 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2228 if (errno != ENOENT) {
2229 imagex_error_with_errno(T("Failed to stat "
2230 "\"%"TS"\""), source);
2238 /* If the user did not specify an image name, and the basename of the
2239 * source already exists as an image name in the WIM file, append a
2240 * suffix to make it unique. */
2241 if (cmd == CMD_APPEND && name_defaulted) {
2242 unsigned long conflict_idx;
2243 tchar *name_end = tstrchr(name, T('\0'));
2244 for (conflict_idx = 1;
2245 wimlib_image_name_in_use(wim, name);
2248 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2252 /* If capturing a delta WIM, reference resources from the base WIMs
2253 * before adding the new image. */
2254 if (base_wimfiles.num_strings) {
2255 base_wims = calloc(base_wimfiles.num_strings,
2256 sizeof(base_wims[0]));
2257 if (base_wims == NULL) {
2258 imagex_error(T("Out of memory!"));
2263 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2264 ret = wimlib_open_wim_with_progress(
2265 base_wimfiles.strings[i], open_flags,
2266 &base_wims[i], imagex_progress_func, NULL);
2268 goto out_free_base_wims;
2272 ret = wimlib_reference_resources(wim, base_wims,
2273 base_wimfiles.num_strings, 0);
2275 goto out_free_base_wims;
2277 if (base_wimfiles.num_strings == 1) {
2278 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2279 base_wimfiles.strings[0]);
2281 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2282 base_wimfiles.num_strings);
2289 /* If capturing or appending as an update of an existing (template) image,
2290 * open the WIM if needed and parse the image index. */
2291 if (template_image_name_or_num) {
2294 if (base_wimfiles.num_strings == 1 &&
2295 template_wimfile == base_wimfiles.strings[0]) {
2296 template_wim = base_wims[0];
2297 } else if (template_wimfile == wimfile) {
2300 ret = wimlib_open_wim_with_progress(template_wimfile,
2303 imagex_progress_func,
2306 goto out_free_base_wims;
2309 template_image = wimlib_resolve_image(template_wim,
2310 template_image_name_or_num);
2312 if (template_image_name_or_num[0] == T('-')) {
2315 struct wimlib_wim_info info;
2317 wimlib_get_wim_info(template_wim, &info);
2318 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2319 if (n >= 1 && n <= info.image_count &&
2321 tmp != template_image_name_or_num + 1)
2323 template_image = info.image_count - (n - 1);
2326 ret = verify_image_exists_and_is_single(template_image,
2327 template_image_name_or_num,
2330 goto out_free_template_wim;
2332 template_wim = NULL;
2335 ret = wimlib_add_image_multisource(wim,
2342 goto out_free_template_wim;
2344 if (image_properties.num_strings || template_image_name_or_num) {
2345 /* User asked to set additional image properties, or an image on
2346 * which the added one is to be based has been specified with
2348 struct wimlib_wim_info info;
2350 wimlib_get_wim_info(wim, &info);
2352 ret = apply_image_properties(&image_properties, wim,
2353 info.image_count, NULL);
2355 goto out_free_template_wim;
2357 /* Reference template image if the user provided one. */
2358 if (template_image_name_or_num) {
2359 imagex_printf(T("Using image %d "
2360 "from \"%"TS"\" as template\n"),
2361 template_image, template_wimfile);
2362 ret = wimlib_reference_template_image(wim,
2368 goto out_free_template_wim;
2372 /* Write the new WIM or overwrite the existing WIM with the new image
2374 if (cmd == CMD_APPEND) {
2375 ret = wimlib_overwrite(wim, write_flags, num_threads);
2376 } else if (wimfile) {
2377 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2378 write_flags, num_threads);
2380 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2381 write_flags, num_threads);
2383 out_free_template_wim:
2384 /* template_wim may alias base_wims[0] or wim. */
2385 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2386 template_wim != wim)
2387 wimlib_free(template_wim);
2389 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2390 wimlib_free(base_wims[i]);
2394 out_free_capture_sources:
2395 if (capture_sources_malloced)
2396 free(capture_sources);
2397 out_free_source_list_contents:
2398 free(source_list_contents);
2400 string_set_destroy(&image_properties);
2401 string_set_destroy(&base_wimfiles);
2411 /* Remove image(s) from a WIM. */
2413 imagex_delete(int argc, tchar **argv, int cmd)
2416 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2417 int write_flags = 0;
2418 const tchar *wimfile;
2419 const tchar *image_num_or_name;
2424 for_opt(c, delete_options) {
2426 case IMAGEX_CHECK_OPTION:
2427 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2428 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2430 case IMAGEX_SOFT_OPTION:
2431 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2433 case IMAGEX_UNSAFE_COMPACT_OPTION:
2434 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2445 imagex_error(T("Must specify a WIM file"));
2447 imagex_error(T("Must specify an image"));
2451 image_num_or_name = argv[1];
2453 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2454 imagex_progress_func, NULL);
2458 image = wimlib_resolve_image(wim, image_num_or_name);
2460 ret = verify_image_exists(image, image_num_or_name, wimfile);
2462 goto out_wimlib_free;
2464 ret = wimlib_delete_image(wim, image);
2466 imagex_error(T("Failed to delete image from \"%"TS"\""),
2468 goto out_wimlib_free;
2471 ret = wimlib_overwrite(wim, write_flags, 0);
2473 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2474 "deleted"), wimfile);
2482 usage(CMD_DELETE, stderr);
2487 struct print_dentry_options {
2492 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2494 tprintf(T("%"TS"\n"), dentry->full_path);
2497 static const struct {
2500 } file_attr_flags[] = {
2501 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2502 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2503 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2504 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2505 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2506 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2507 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2508 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2509 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2510 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2511 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2512 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2513 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2514 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2515 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2518 #define TIMESTR_MAX 100
2521 timespec_to_string(const struct timespec *spec, tchar *buf)
2523 time_t t = spec->tv_sec;
2526 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2527 buf[TIMESTR_MAX - 1] = '\0';
2531 print_time(const tchar *type, const struct timespec *spec)
2533 tchar timestr[TIMESTR_MAX];
2535 timespec_to_string(spec, timestr);
2537 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2540 static void print_byte_field(const uint8_t field[], size_t len)
2543 tprintf(T("%02hhx"), *field++);
2547 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2549 tchar attr_string[256];
2552 tputs(T("WIM Information:"));
2553 tputs(T("----------------"));
2554 tprintf(T("Path: %"TS"\n"), wimfile);
2555 tprintf(T("GUID: 0x"));
2556 print_byte_field(info->guid, sizeof(info->guid));
2558 tprintf(T("Version: %u\n"), info->wim_version);
2559 tprintf(T("Image Count: %d\n"), info->image_count);
2560 tprintf(T("Compression: %"TS"\n"),
2561 wimlib_get_compression_type_string(info->compression_type));
2562 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2564 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2565 tprintf(T("Boot Index: %d\n"), info->boot_index);
2566 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2568 attr_string[0] = T('\0');
2571 tstrcat(attr_string, T("Pipable, "));
2573 if (info->has_integrity_table)
2574 tstrcat(attr_string, T("Integrity info, "));
2576 if (info->has_rpfix)
2577 tstrcat(attr_string, T("Relative path junction, "));
2579 if (info->resource_only)
2580 tstrcat(attr_string, T("Resource only, "));
2582 if (info->metadata_only)
2583 tstrcat(attr_string, T("Metadata only, "));
2585 if (info->is_marked_readonly)
2586 tstrcat(attr_string, T("Readonly, "));
2588 p = tstrchr(attr_string, T('\0'));
2589 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2592 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2596 print_resource(const struct wimlib_resource_entry *resource,
2599 tprintf(T("Hash = 0x"));
2600 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2603 if (!resource->is_missing) {
2604 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2605 resource->uncompressed_size);
2606 if (resource->packed) {
2607 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2608 "bytes @ offset %"PRIu64"\n"),
2609 resource->raw_resource_uncompressed_size,
2610 resource->raw_resource_compressed_size,
2611 resource->raw_resource_offset_in_wim);
2613 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2616 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2617 resource->compressed_size);
2619 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2623 tprintf(T("Part Number = %u\n"), resource->part_number);
2624 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2626 tprintf(T("Flags = "));
2627 if (resource->is_compressed)
2628 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2629 if (resource->is_metadata)
2630 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2631 if (resource->is_free)
2632 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2633 if (resource->is_spanned)
2634 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2635 if (resource->packed)
2636 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2644 print_blobs(WIMStruct *wim)
2646 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2650 default_print_security_descriptor(const uint8_t *sd, size_t size)
2652 tprintf(T("Security Descriptor = "));
2653 print_byte_field(sd, size);
2658 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2662 "----------------------------------------------------------------------------\n"));
2663 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2664 if (dentry->dos_name)
2665 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2666 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2667 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2668 if (file_attr_flags[i].flag & dentry->attributes)
2669 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2670 file_attr_flags[i].name);
2672 if (dentry->security_descriptor) {
2673 print_security_descriptor(dentry->security_descriptor,
2674 dentry->security_descriptor_size);
2677 print_time(T("Creation Time"), &dentry->creation_time);
2678 print_time(T("Last Write Time"), &dentry->last_write_time);
2679 print_time(T("Last Access Time"), &dentry->last_access_time);
2682 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2683 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2685 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2686 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2688 if (dentry->unix_mode != 0) {
2689 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2690 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2691 dentry->unix_uid, dentry->unix_gid,
2692 dentry->unix_mode, dentry->unix_rdev);
2695 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2696 if (dentry->streams[i].stream_name) {
2697 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2698 dentry->streams[i].stream_name);
2699 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2700 tprintf(T("\tRaw encrypted data stream:\n"));
2701 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2702 tprintf(T("\tReparse point stream:\n"));
2704 tprintf(T("\tUnnamed data stream:\n"));
2706 print_resource(&dentry->streams[i].resource, NULL);
2711 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2713 const struct print_dentry_options *options = _options;
2714 if (!options->detailed)
2715 print_dentry_full_path(dentry);
2717 print_dentry_detailed(dentry);
2721 /* Print the files contained in an image(s) in a WIM file. */
2723 imagex_dir(int argc, tchar **argv, int cmd)
2725 const tchar *wimfile;
2726 WIMStruct *wim = NULL;
2729 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2731 struct print_dentry_options options = {
2734 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2736 STRING_SET(refglobs);
2738 for_opt(c, dir_options) {
2740 case IMAGEX_PATH_OPTION:
2743 case IMAGEX_DETAILED_OPTION:
2744 options.detailed = true;
2746 case IMAGEX_ONE_FILE_ONLY_OPTION:
2747 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2749 case IMAGEX_REF_OPTION:
2750 ret = string_set_append(&refglobs, optarg);
2752 goto out_free_refglobs;
2762 imagex_error(T("Must specify a WIM file"));
2766 imagex_error(T("Too many arguments"));
2771 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2772 imagex_progress_func, NULL);
2774 goto out_free_refglobs;
2777 image = wimlib_resolve_image(wim, argv[1]);
2778 ret = verify_image_exists(image, argv[1], wimfile);
2780 goto out_wimlib_free;
2782 /* No image specified; default to image 1, but only if the WIM
2783 * contains exactly one image. */
2785 struct wimlib_wim_info info;
2787 wimlib_get_wim_info(wim, &info);
2788 if (info.image_count != 1) {
2789 imagex_error(T("\"%"TS"\" contains %d images; Please "
2790 "select one (or all)."),
2791 wimfile, info.image_count);
2798 if (refglobs.num_strings) {
2799 ret = wim_reference_globs(wim, &refglobs, 0);
2801 goto out_wimlib_free;
2804 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2805 print_dentry, &options);
2806 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2807 struct wimlib_wim_info info;
2809 wimlib_get_wim_info(wim, &info);
2810 do_metadata_not_found_warning(wimfile, &info);
2815 string_set_destroy(&refglobs);
2819 usage(CMD_DIR, stderr);
2821 goto out_free_refglobs;
2824 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2827 imagex_export(int argc, tchar **argv, int cmd)
2831 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2832 int write_flags = 0;
2833 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2834 const tchar *src_wimfile;
2835 const tchar *src_image_num_or_name;
2836 const tchar *dest_wimfile;
2838 const tchar *dest_name;
2839 const tchar *dest_desc;
2841 struct wimlib_wim_info src_info;
2842 WIMStruct *dest_wim;
2847 STRING_SET(refglobs);
2848 unsigned num_threads = 0;
2849 uint32_t chunk_size = UINT32_MAX;
2850 uint32_t solid_chunk_size = UINT32_MAX;
2851 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2853 for_opt(c, export_options) {
2855 case IMAGEX_BOOT_OPTION:
2856 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2858 case IMAGEX_CHECK_OPTION:
2859 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2860 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2862 case IMAGEX_NOCHECK_OPTION:
2863 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2865 case IMAGEX_COMPRESS_OPTION:
2866 compression_type = get_compression_type(optarg, false);
2867 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2870 case IMAGEX_COMPRESS_SLOW_OPTION:
2871 set_compress_slow();
2872 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2874 case IMAGEX_RECOMPRESS_OPTION:
2875 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2877 case IMAGEX_SOLID_OPTION:
2878 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2880 case IMAGEX_NO_SOLID_SORT_OPTION:
2881 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2883 case IMAGEX_CHUNK_SIZE_OPTION:
2884 chunk_size = parse_chunk_size(optarg);
2885 if (chunk_size == UINT32_MAX)
2888 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2889 solid_chunk_size = parse_chunk_size(optarg);
2890 if (solid_chunk_size == UINT32_MAX)
2893 case IMAGEX_SOLID_COMPRESS_OPTION:
2894 solid_ctype = get_compression_type(optarg, true);
2895 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2898 case IMAGEX_REF_OPTION:
2899 ret = string_set_append(&refglobs, optarg);
2901 goto out_free_refglobs;
2903 case IMAGEX_THREADS_OPTION:
2904 num_threads = parse_num_threads(optarg);
2905 if (num_threads == UINT_MAX)
2908 case IMAGEX_REBUILD_OPTION:
2909 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2911 case IMAGEX_PIPABLE_OPTION:
2912 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2914 case IMAGEX_NOT_PIPABLE_OPTION:
2915 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2917 case IMAGEX_WIMBOOT_OPTION:
2918 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2920 case IMAGEX_UNSAFE_COMPACT_OPTION:
2921 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2929 if (argc < 3 || argc > 5)
2931 src_wimfile = argv[0];
2932 src_image_num_or_name = argv[1];
2933 dest_wimfile = argv[2];
2934 dest_name = (argc >= 4) ? argv[3] : NULL;
2935 dest_desc = (argc >= 5) ? argv[4] : NULL;
2936 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2937 imagex_progress_func, NULL);
2939 goto out_free_refglobs;
2941 wimlib_get_wim_info(src_wim, &src_info);
2943 /* Determine if the destination is an existing file or not. If so, we
2944 * try to append the exported image(s) to it; otherwise, we create a new
2945 * WIM containing the exported image(s). Furthermore, determine if we
2946 * need to write a pipable WIM directly to standard output. */
2948 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2950 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2951 imagex_error("Can't write a non-pipable WIM to "
2952 "standard output! Specify --pipable\n"
2953 " if you want to create a pipable WIM "
2954 "(but read the docs first).");
2956 goto out_free_src_wim;
2959 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2961 dest_wimfile = NULL;
2962 dest_wim_fd = STDOUT_FILENO;
2963 imagex_info_file = stderr;
2964 set_fd_to_binary_mode(dest_wim_fd);
2967 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2969 /* Destination file exists. */
2971 if (!S_ISREG(stbuf.st_mode)) {
2972 imagex_error(T("\"%"TS"\" is not a regular file"),
2975 goto out_free_src_wim;
2977 ret = wimlib_open_wim_with_progress(dest_wimfile,
2979 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2981 imagex_progress_func,
2984 goto out_free_src_wim;
2986 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2987 /* The user specified a compression type, but we're
2988 * exporting to an existing WIM. Make sure the
2989 * specified compression type is the same as the
2990 * compression type of the existing destination WIM. */
2991 struct wimlib_wim_info dest_info;
2993 wimlib_get_wim_info(dest_wim, &dest_info);
2994 if (compression_type != dest_info.compression_type) {
2995 imagex_error(T("Cannot specify a compression type that is "
2996 "not the same as that used in the "
2997 "destination WIM"));
2999 goto out_free_dest_wim;
3005 if (errno != ENOENT) {
3006 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3009 goto out_free_src_wim;
3012 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3013 imagex_error(T("'--unsafe-compact' is only valid when "
3014 "exporting to an existing WIM file!"));
3016 goto out_free_src_wim;
3019 /* dest_wimfile is not an existing file, so create a new WIM. */
3021 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3022 /* The user did not specify a compression type; default
3023 * to that of the source WIM, unless --solid or
3024 * --wimboot was specified. */
3026 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3027 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3028 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3029 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3031 compression_type = src_info.compression_type;
3033 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3035 goto out_free_src_wim;
3037 wimlib_register_progress_function(dest_wim,
3038 imagex_progress_func, NULL);
3040 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3041 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3043 /* For --wimboot export, use small XPRESS chunks. */
3044 wimlib_set_output_chunk_size(dest_wim, 4096);
3045 } else if (compression_type == src_info.compression_type &&
3046 chunk_size == UINT32_MAX)
3048 /* Use same chunk size if compression type is the same. */
3049 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3053 if (chunk_size != UINT32_MAX) {
3054 /* Set destination chunk size. */
3055 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3057 goto out_free_dest_wim;
3059 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3060 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3062 goto out_free_dest_wim;
3064 if (solid_chunk_size != UINT32_MAX) {
3065 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3067 goto out_free_dest_wim;
3070 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3071 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3073 goto out_free_dest_wim;
3075 if (refglobs.num_strings) {
3076 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3078 goto out_free_dest_wim;
3081 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3082 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3084 imagex_error(T("--boot specified for all-images export, but source WIM "
3085 "has no bootable image."));
3087 goto out_free_dest_wim;
3090 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3091 dest_desc, export_flags);
3093 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3094 do_resource_not_found_warning(src_wimfile,
3095 &src_info, &refglobs);
3096 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3097 do_metadata_not_found_warning(src_wimfile, &src_info);
3099 goto out_free_dest_wim;
3103 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3104 else if (dest_wimfile)
3105 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3106 write_flags, num_threads);
3108 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3109 WIMLIB_ALL_IMAGES, write_flags,
3112 wimlib_free(dest_wim);
3114 wimlib_free(src_wim);
3116 string_set_destroy(&refglobs);
3120 usage(CMD_EXPORT, stderr);
3123 goto out_free_refglobs;
3126 /* Extract files or directories from a WIM image */
3128 imagex_extract(int argc, tchar **argv, int cmd)
3135 const tchar *wimfile;
3136 const tchar *image_num_or_name;
3137 tchar *dest_dir = T(".");
3138 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3139 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3140 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3141 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3143 STRING_SET(refglobs);
3145 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3147 for_opt(c, extract_options) {
3149 case IMAGEX_CHECK_OPTION:
3150 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3152 case IMAGEX_VERBOSE_OPTION:
3153 /* No longer does anything. */
3155 case IMAGEX_REF_OPTION:
3156 ret = string_set_append(&refglobs, optarg);
3158 goto out_free_refglobs;
3160 case IMAGEX_UNIX_DATA_OPTION:
3161 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3163 case IMAGEX_NO_ACLS_OPTION:
3164 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3166 case IMAGEX_STRICT_ACLS_OPTION:
3167 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3169 case IMAGEX_NO_ATTRIBUTES_OPTION:
3170 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3172 case IMAGEX_DEST_DIR_OPTION:
3175 case IMAGEX_TO_STDOUT_OPTION:
3176 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3177 imagex_info_file = stderr;
3178 imagex_be_quiet = true;
3179 set_fd_to_binary_mode(STDOUT_FILENO);
3181 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3182 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3183 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3185 case IMAGEX_NO_GLOBS_OPTION:
3186 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3188 case IMAGEX_NULLGLOB_OPTION:
3189 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3191 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3192 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3194 case IMAGEX_WIMBOOT_OPTION:
3195 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3197 case IMAGEX_COMPACT_OPTION:
3198 ret = set_compact_mode(optarg, &extract_flags);
3200 goto out_free_refglobs;
3212 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3213 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3215 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3220 image_num_or_name = argv[1];
3225 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3226 imagex_progress_func, NULL);
3228 goto out_free_refglobs;
3230 image = wimlib_resolve_image(wim, image_num_or_name);
3231 ret = verify_image_exists_and_is_single(image,
3235 goto out_wimlib_free;
3237 if (refglobs.num_strings) {
3238 ret = wim_reference_globs(wim, &refglobs, open_flags);
3240 goto out_wimlib_free;
3246 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3249 while (argc != 0 && ret == 0) {
3253 num_paths < argc && argv[num_paths][0] != T('@');
3258 ret = wimlib_extract_paths(wim, image, dest_dir,
3259 (const tchar **)argv,
3261 extract_flags | notlist_extract_flags);
3265 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3274 if (!imagex_be_quiet)
3275 imagex_printf(T("Done extracting files.\n"));
3276 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3277 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3278 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3279 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3280 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3283 T("Note: You can use the '--nullglob' "
3284 "option to ignore missing files.\n"));
3286 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3287 "files and directories\n"
3288 " are in the WIM image.\n"),
3289 get_cmd_string(CMD_DIR, false));
3290 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3291 struct wimlib_wim_info info;
3293 wimlib_get_wim_info(wim, &info);
3294 do_resource_not_found_warning(wimfile, &info, &refglobs);
3295 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3296 struct wimlib_wim_info info;
3298 wimlib_get_wim_info(wim, &info);
3299 do_metadata_not_found_warning(wimfile, &info);
3304 string_set_destroy(&refglobs);
3308 usage(CMD_EXTRACT, stderr);
3311 goto out_free_refglobs;
3314 /* Prints information about a WIM file; also can mark an image as bootable,
3315 * change the name of an image, or change the description of an image. */
3317 imagex_info(int argc, tchar **argv, int cmd)
3322 bool nocheck = false;
3323 bool header = false;
3326 bool short_header = true;
3327 const tchar *xml_out_file = NULL;
3328 const tchar *wimfile;
3329 const tchar *image_num_or_name;
3330 STRING_SET(image_properties);
3335 struct wimlib_wim_info info;
3337 for_opt(c, info_options) {
3339 case IMAGEX_BOOT_OPTION:
3342 case IMAGEX_CHECK_OPTION:
3345 case IMAGEX_NOCHECK_OPTION:
3348 case IMAGEX_HEADER_OPTION:
3350 short_header = false;
3352 case IMAGEX_BLOBS_OPTION:
3354 short_header = false;
3356 case IMAGEX_XML_OPTION:
3358 short_header = false;
3360 case IMAGEX_EXTRACT_XML_OPTION:
3361 xml_out_file = optarg;
3362 short_header = false;
3364 case IMAGEX_METADATA_OPTION:
3365 imagex_error(T("The --metadata option has been removed. "
3366 "Use 'wimdir --detail' instead."));
3368 case IMAGEX_IMAGE_PROPERTY_OPTION:
3369 ret = append_image_property_argument(&image_properties);
3380 if (argc < 1 || argc > 4)
3384 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3388 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3389 tsprintf(p, T("NAME=%"TS), argv[2]);
3390 ret = string_set_append(&image_properties, p);
3397 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3398 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3399 ret = string_set_append(&image_properties, p);
3404 if (check && nocheck) {
3405 imagex_error(T("Can't specify both --check and --nocheck"));
3410 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3412 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3413 imagex_progress_func, NULL);
3417 wimlib_get_wim_info(wim, &info);
3419 image = wimlib_resolve_image(wim, image_num_or_name);
3420 ret = WIMLIB_ERR_INVALID_IMAGE;
3421 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3422 verify_image_exists(image, image_num_or_name, wimfile);
3424 imagex_error(T("If you would like to set the boot "
3425 "index to 0, specify image \"0\" with "
3426 "the --boot flag."));
3428 goto out_wimlib_free;
3431 if (boot && info.image_count == 0) {
3432 imagex_error(T("--boot is meaningless on a WIM with no images"));
3433 goto out_wimlib_free;
3436 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3438 imagex_error(T("Cannot specify the --boot flag "
3439 "without specifying a specific "
3440 "image in a multi-image WIM"));
3441 goto out_wimlib_free;
3443 if (image_properties.num_strings) {
3444 imagex_error(T("Can't change image properties without "
3445 "specifying a specific image in a "
3446 "multi-image WIM"));
3447 goto out_wimlib_free;
3451 /* Operations that print information are separated from operations that
3452 * recreate the WIM file. */
3453 if (!image_properties.num_strings && !boot) {
3455 /* Read-only operations */
3457 if (image == WIMLIB_NO_IMAGE) {
3458 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3459 image_num_or_name, wimfile);
3460 goto out_wimlib_free;
3463 if (image == WIMLIB_ALL_IMAGES && short_header)
3464 print_wim_information(wimfile, &info);
3467 wimlib_print_header(wim);
3470 if (info.total_parts != 1) {
3471 tfprintf(stderr, T("Warning: Only showing the blobs "
3472 "for part %d of a %d-part WIM.\n"),
3473 info.part_number, info.total_parts);
3479 ret = wimlib_extract_xml_data(wim, stdout);
3481 goto out_wimlib_free;
3487 fp = tfopen(xml_out_file, T("wb"));
3489 imagex_error_with_errno(T("Failed to open the "
3490 "file \"%"TS"\" for "
3494 goto out_wimlib_free;
3496 ret = wimlib_extract_xml_data(wim, fp);
3498 imagex_error(T("Failed to close the file "
3504 goto out_wimlib_free;
3508 wimlib_print_available_images(wim, image);
3512 /* Modification operations */
3513 bool any_property_changes;
3515 if (image == WIMLIB_ALL_IMAGES)
3518 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3519 imagex_error(T("Cannot change image properties "
3520 "when using image 0"));
3522 goto out_wimlib_free;
3526 if (image == info.boot_index) {
3527 imagex_printf(T("Image %d is already marked as "
3528 "bootable.\n"), image);
3531 imagex_printf(T("Marking image %d as bootable.\n"),
3533 info.boot_index = image;
3534 ret = wimlib_set_wim_info(wim, &info,
3535 WIMLIB_CHANGE_BOOT_INDEX);
3537 goto out_wimlib_free;
3541 ret = apply_image_properties(&image_properties, wim, image,
3542 &any_property_changes);
3544 goto out_wimlib_free;
3546 /* Only call wimlib_overwrite() if something actually needs to
3548 if (boot || any_property_changes ||
3549 (check && !info.has_integrity_table) ||
3550 (nocheck && info.has_integrity_table))
3552 int write_flags = 0;
3555 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3557 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3558 ret = wimlib_overwrite(wim, write_flags, 1);
3560 imagex_printf(T("The file \"%"TS"\" was not modified "
3561 "because nothing needed to be done.\n"),
3569 string_set_destroy(&image_properties);
3573 usage(CMD_INFO, stderr);
3579 /* Join split WIMs into one part WIM */
3581 imagex_join(int argc, tchar **argv, int cmd)
3584 int swm_open_flags = 0;
3585 int wim_write_flags = 0;
3586 const tchar *output_path;
3589 for_opt(c, join_options) {
3591 case IMAGEX_CHECK_OPTION:
3592 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3593 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3603 imagex_error(T("Must specify one or more split WIM (.swm) "
3607 output_path = argv[0];
3608 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3613 imagex_progress_func,
3619 usage(CMD_JOIN, stderr);
3624 #if WIM_MOUNTING_SUPPORTED
3626 /* Mounts a WIM image. */
3628 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3631 int mount_flags = 0;
3633 const tchar *staging_dir = NULL;
3634 const tchar *wimfile;
3637 struct wimlib_wim_info info;
3641 STRING_SET(refglobs);
3643 if (cmd == CMD_MOUNTRW) {
3644 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3645 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3648 for_opt(c, mount_options) {
3650 case IMAGEX_ALLOW_OTHER_OPTION:
3651 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3653 case IMAGEX_CHECK_OPTION:
3654 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3656 case IMAGEX_DEBUG_OPTION:
3657 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3659 case IMAGEX_STREAMS_INTERFACE_OPTION:
3660 if (!tstrcasecmp(optarg, T("none")))
3661 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3662 else if (!tstrcasecmp(optarg, T("xattr")))
3663 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3664 else if (!tstrcasecmp(optarg, T("windows")))
3665 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3667 imagex_error(T("Unknown stream interface \"%"TS"\""),
3672 case IMAGEX_REF_OPTION:
3673 ret = string_set_append(&refglobs, optarg);
3675 goto out_free_refglobs;
3677 case IMAGEX_STAGING_DIR_OPTION:
3678 staging_dir = optarg;
3680 case IMAGEX_UNIX_DATA_OPTION:
3681 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3689 if (argc != 2 && argc != 3)
3694 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3695 imagex_progress_func, NULL);
3697 goto out_free_refglobs;
3699 wimlib_get_wim_info(wim, &info);
3702 /* Image explicitly specified. */
3703 image = wimlib_resolve_image(wim, argv[1]);
3705 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3709 /* No image specified; default to image 1, but only if the WIM
3710 * contains exactly one image. */
3712 if (info.image_count != 1) {
3713 imagex_error(T("\"%"TS"\" contains %d images; Please "
3714 "select one."), wimfile, info.image_count);
3722 if (refglobs.num_strings) {
3723 ret = wim_reference_globs(wim, &refglobs, open_flags);
3728 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3730 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3731 do_metadata_not_found_warning(wimfile, &info);
3733 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3735 image, wimfile, dir);
3741 string_set_destroy(&refglobs);
3747 goto out_free_refglobs;
3749 #endif /* WIM_MOUNTING_SUPPORTED */
3751 /* Rebuild a WIM file */
3753 imagex_optimize(int argc, tchar **argv, int cmd)
3756 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3757 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3758 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3759 uint32_t chunk_size = UINT32_MAX;
3760 uint32_t solid_chunk_size = UINT32_MAX;
3761 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3764 const tchar *wimfile;
3767 unsigned num_threads = 0;
3769 for_opt(c, optimize_options) {
3771 case IMAGEX_CHECK_OPTION:
3772 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3773 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3775 case IMAGEX_NOCHECK_OPTION:
3776 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3778 case IMAGEX_COMPRESS_OPTION:
3779 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3780 compression_type = get_compression_type(optarg, false);
3781 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3784 case IMAGEX_COMPRESS_SLOW_OPTION:
3785 set_compress_slow();
3786 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3788 case IMAGEX_RECOMPRESS_OPTION:
3789 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3791 case IMAGEX_CHUNK_SIZE_OPTION:
3792 chunk_size = parse_chunk_size(optarg);
3793 if (chunk_size == UINT32_MAX)
3796 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3797 solid_chunk_size = parse_chunk_size(optarg);
3798 if (solid_chunk_size == UINT32_MAX)
3801 case IMAGEX_SOLID_COMPRESS_OPTION:
3802 solid_ctype = get_compression_type(optarg, true);
3803 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3806 case IMAGEX_SOLID_OPTION:
3807 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3808 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3810 case IMAGEX_NO_SOLID_SORT_OPTION:
3811 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3813 case IMAGEX_THREADS_OPTION:
3814 num_threads = parse_num_threads(optarg);
3815 if (num_threads == UINT_MAX)
3818 case IMAGEX_PIPABLE_OPTION:
3819 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3821 case IMAGEX_NOT_PIPABLE_OPTION:
3822 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3824 case IMAGEX_UNSAFE_COMPACT_OPTION:
3825 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3839 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3840 imagex_progress_func, NULL);
3844 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3845 /* Change compression type. */
3846 ret = wimlib_set_output_compression_type(wim, compression_type);
3848 goto out_wimlib_free;
3851 if (chunk_size != UINT32_MAX) {
3852 /* Change chunk size. */
3853 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3855 goto out_wimlib_free;
3857 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3858 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3860 goto out_wimlib_free;
3862 if (solid_chunk_size != UINT32_MAX) {
3863 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3865 goto out_wimlib_free;
3868 old_size = file_get_size(wimfile);
3869 tprintf(T("\"%"TS"\" original size: "), wimfile);
3871 tputs(T("Unknown"));
3873 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3875 ret = wimlib_overwrite(wim, write_flags, num_threads);
3877 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3878 goto out_wimlib_free;
3881 new_size = file_get_size(wimfile);
3882 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3884 tputs(T("Unknown"));
3886 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3888 tfputs(T("Space saved: "), stdout);
3889 if (new_size != -1 && old_size != -1) {
3890 tprintf(T("%lld KiB\n"),
3891 ((long long)old_size - (long long)new_size) >> 10);
3893 tputs(T("Unknown"));
3902 usage(CMD_OPTIMIZE, stderr);
3908 /* Split a WIM into a spanned set */
3910 imagex_split(int argc, tchar **argv, int cmd)
3914 int write_flags = 0;
3915 unsigned long part_size;
3920 for_opt(c, split_options) {
3922 case IMAGEX_CHECK_OPTION:
3923 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3924 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3936 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3937 if (tmp == argv[2] || *tmp) {
3938 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3939 imagex_error(T("The part size must be an integer or "
3940 "floating-point number of megabytes."));
3943 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3944 imagex_progress_func, NULL);
3948 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3954 usage(CMD_SPLIT, stderr);
3960 #if WIM_MOUNTING_SUPPORTED
3961 /* Unmounts a mounted WIM image. */
3963 imagex_unmount(int argc, tchar **argv, int cmd)
3966 int unmount_flags = 0;
3969 for_opt(c, unmount_options) {
3971 case IMAGEX_COMMIT_OPTION:
3972 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3974 case IMAGEX_CHECK_OPTION:
3975 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3977 case IMAGEX_REBUILD_OPTION:
3978 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3980 case IMAGEX_LAZY_OPTION:
3981 case IMAGEX_FORCE_OPTION:
3982 /* Now, unmount is lazy by default. However, committing
3983 * the image will fail with
3984 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3985 * file descriptors on the WIM image. The
3986 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3987 * descriptors to be closed. */
3988 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3990 case IMAGEX_NEW_IMAGE_OPTION:
3991 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4002 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4003 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4004 imagex_error(T("--new-image is meaningless "
4005 "without --commit also specified!"));
4010 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4011 imagex_progress_func, NULL);
4013 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4014 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4016 "\tNote: Use --commit --force to force changes "
4017 "to be committed, regardless\n"
4018 "\t of open files.\n"));
4025 usage(CMD_UNMOUNT, stderr);
4030 #endif /* WIM_MOUNTING_SUPPORTED */
4033 * Add, delete, or rename files in a WIM image.
4036 imagex_update(int argc, tchar **argv, int cmd)
4038 const tchar *wimfile;
4042 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4043 int write_flags = 0;
4044 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4045 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4046 WIMLIB_ADD_FLAG_VERBOSE |
4047 WIMLIB_ADD_FLAG_WINCONFIG;
4048 int default_delete_flags = 0;
4049 unsigned num_threads = 0;
4051 tchar *cmd_file_contents;
4052 size_t cmd_file_nchars;
4053 struct wimlib_update_command *cmds;
4055 tchar *command_str = NULL;
4056 tchar *config_file = NULL;
4057 tchar *wimboot_config = NULL;
4059 for_opt(c, update_options) {
4061 /* Generic or write options */
4062 case IMAGEX_THREADS_OPTION:
4063 num_threads = parse_num_threads(optarg);
4064 if (num_threads == UINT_MAX)
4067 case IMAGEX_CHECK_OPTION:
4068 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4069 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4071 case IMAGEX_REBUILD_OPTION:
4072 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4074 case IMAGEX_COMMAND_OPTION:
4076 imagex_error(T("--command may only be specified "
4077 "one time. Please provide\n"
4078 " the update commands "
4079 "on standard input instead."));
4082 command_str = tstrdup(optarg);
4084 imagex_error(T("Out of memory!"));
4088 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4089 wimboot_config = optarg;
4091 /* Default delete options */
4092 case IMAGEX_FORCE_OPTION:
4093 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4095 case IMAGEX_RECURSIVE_OPTION:
4096 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4099 /* Global add option */
4100 case IMAGEX_CONFIG_OPTION:
4101 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4102 config_file = optarg;
4105 /* Default add options */
4106 case IMAGEX_VERBOSE_OPTION:
4107 /* No longer does anything. */
4109 case IMAGEX_DEREFERENCE_OPTION:
4110 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4112 case IMAGEX_UNIX_DATA_OPTION:
4113 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4115 case IMAGEX_NO_ACLS_OPTION:
4116 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4118 case IMAGEX_STRICT_ACLS_OPTION:
4119 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4121 case IMAGEX_NO_REPLACE_OPTION:
4122 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4124 case IMAGEX_UNSAFE_COMPACT_OPTION:
4125 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4134 if (argc != 1 && argc != 2)
4138 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4139 imagex_progress_func, NULL);
4141 goto out_free_command_str;
4144 /* Image explicitly specified. */
4145 image = wimlib_resolve_image(wim, argv[1]);
4146 ret = verify_image_exists_and_is_single(image, argv[1],
4149 goto out_wimlib_free;
4151 /* No image specified; default to image 1, but only if the WIM
4152 * contains exactly one image. */
4153 struct wimlib_wim_info info;
4155 wimlib_get_wim_info(wim, &info);
4156 if (info.image_count != 1) {
4157 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4158 wimfile, info.image_count);
4165 /* Read update commands from standard input, or the command string if
4168 cmd_file_contents = NULL;
4169 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4173 goto out_free_cmd_file_contents;
4175 } else if (!wimboot_config) {
4176 if (isatty(STDIN_FILENO)) {
4177 tputs(T("Reading update commands from standard input..."));
4178 recommend_man_page(CMD_UPDATE, stdout);
4180 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4181 if (!cmd_file_contents) {
4183 goto out_wimlib_free;
4186 /* Parse the update commands */
4187 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4191 goto out_free_cmd_file_contents;
4194 cmd_file_contents = NULL;
4199 /* Set default flags and capture config on the update commands */
4200 for (size_t i = 0; i < num_cmds; i++) {
4201 switch (cmds[i].op) {
4202 case WIMLIB_UPDATE_OP_ADD:
4203 cmds[i].add.add_flags |= default_add_flags;
4204 cmds[i].add.config_file = config_file;
4206 case WIMLIB_UPDATE_OP_DELETE:
4207 cmds[i].delete_.delete_flags |= default_delete_flags;
4214 /* Execute the update commands */
4215 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4219 if (wimboot_config) {
4220 /* --wimboot-config=FILE is short for an
4221 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4223 struct wimlib_update_command cmd;
4225 cmd.op = WIMLIB_UPDATE_OP_ADD;
4226 cmd.add.fs_source_path = wimboot_config;
4227 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4228 cmd.add.config_file = NULL;
4229 cmd.add.add_flags = 0;
4231 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4236 /* Overwrite the updated WIM */
4237 ret = wimlib_overwrite(wim, write_flags, num_threads);
4240 out_free_cmd_file_contents:
4241 free(cmd_file_contents);
4244 out_free_command_str:
4249 usage(CMD_UPDATE, stderr);
4252 goto out_free_command_str;
4255 /* Verify a WIM file. */
4257 imagex_verify(int argc, tchar **argv, int cmd)
4260 const tchar *wimfile;
4262 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4263 int verify_flags = 0;
4264 STRING_SET(refglobs);
4267 for_opt(c, verify_options) {
4269 case IMAGEX_REF_OPTION:
4270 ret = string_set_append(&refglobs, optarg);
4272 goto out_free_refglobs;
4274 case IMAGEX_NOCHECK_OPTION:
4275 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4287 imagex_error(T("Must specify a WIM file!"));
4289 imagex_error(T("At most one WIM file can be specified!"));
4295 ret = wimlib_open_wim_with_progress(wimfile,
4298 imagex_progress_func,
4301 goto out_free_refglobs;
4303 ret = wim_reference_globs(wim, &refglobs, open_flags);
4305 goto out_wimlib_free;
4307 ret = wimlib_verify_wim(wim, verify_flags);
4309 tputc(T('\n'), stderr);
4310 imagex_error(T("\"%"TS"\" failed verification!"),
4312 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4313 refglobs.num_strings == 0)
4315 imagex_printf(T("Note: if this WIM file is not standalone, "
4316 "use the --ref option to specify the other parts.\n"));
4319 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4326 string_set_destroy(&refglobs);
4330 usage(CMD_VERIFY, stderr);
4332 goto out_free_refglobs;
4335 struct imagex_command {
4337 int (*func)(int argc, tchar **argv, int cmd);
4340 static const struct imagex_command imagex_commands[] = {
4341 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4342 [CMD_APPLY] = {T("apply"), imagex_apply},
4343 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4344 [CMD_DELETE] = {T("delete"), imagex_delete},
4345 [CMD_DIR ] = {T("dir"), imagex_dir},
4346 [CMD_EXPORT] = {T("export"), imagex_export},
4347 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4348 [CMD_INFO] = {T("info"), imagex_info},
4349 [CMD_JOIN] = {T("join"), imagex_join},
4350 #if WIM_MOUNTING_SUPPORTED
4351 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4352 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4354 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4355 [CMD_SPLIT] = {T("split"), imagex_split},
4356 #if WIM_MOUNTING_SUPPORTED
4357 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4359 [CMD_UPDATE] = {T("update"), imagex_update},
4360 [CMD_VERIFY] = {T("verify"), imagex_verify},
4365 /* Can be a directory or source list file. But source list file is probably
4366 * a rare use case, so just say directory. */
4367 # define SOURCE_STR T("DIRECTORY")
4369 /* Can only be a directory */
4370 # define TARGET_STR T("DIRECTORY")
4373 /* Can be a directory, NTFS volume, or source list file. */
4374 # define SOURCE_STR T("SOURCE")
4376 /* Can be a directory or NTFS volume. */
4377 # define TARGET_STR T("TARGET")
4381 static const tchar *usage_strings[] = {
4384 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4385 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4386 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4387 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4388 " [--wimboot] [--unix-data] [--dereference] [--snapshot]\n"
4392 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4393 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4394 " [--no-attributes] [--rpfix] [--norpfix]\n"
4395 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4396 " [--compact=FORMAT]\n"
4400 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4401 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4402 " [--config=FILE] [--threads=NUM_THREADS]\n"
4403 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4404 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4405 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4410 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4414 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4418 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4419 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4420 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4421 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4422 " [--wimboot] [--solid]\n"
4426 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4427 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4428 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4429 " [--no-attributes] [--include-invalid-names]\n"
4430 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4434 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4435 " [--boot] [--check] [--nocheck] [--xml]\n"
4436 " [--extract-xml FILE] [--header] [--blobs]\n"
4437 " [--image-property NAME=VALUE]\n"
4441 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4443 #if WIM_MOUNTING_SUPPORTED
4446 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4447 " [--check] [--streams-interface=INTERFACE]\n"
4448 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4452 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4453 " [--check] [--streams-interface=INTERFACE]\n"
4454 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4460 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4461 " [--check] [--nocheck] [--solid]\n"
4466 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4468 #if WIM_MOUNTING_SUPPORTED
4471 " %"TS" DIRECTORY\n"
4472 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4477 " %"TS" WIMFILE [IMAGE]\n"
4478 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4479 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4480 " [--command=STRING] [--wimboot-config=FILE]\n"
4485 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4489 static const tchar *invocation_name;
4490 static int invocation_cmd = CMD_NONE;
4492 static const tchar *get_cmd_string(int cmd, bool nospace)
4494 static tchar buf[50];
4495 if (cmd == CMD_NONE) {
4496 return T("wimlib-imagex");
4497 } else if (invocation_cmd != CMD_NONE) {
4498 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4500 const tchar *format;
4503 format = T("%"TS"-%"TS"");
4505 format = T("%"TS" %"TS"");
4506 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4514 static const tchar *s =
4516 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4517 "Copyright (C) 2012-2016 Eric Biggers\n"
4518 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4519 "This is free software: you are free to change and redistribute it.\n"
4520 "There is NO WARRANTY, to the extent permitted by law.\n"
4522 "Report bugs to "PACKAGE_BUGREPORT".\n"
4529 help_or_version(int argc, tchar **argv, int cmd)
4534 for (i = 1; i < argc; i++) {
4536 if (p[0] == T('-') && p[1] == T('-')) {
4538 if (!tstrcmp(p, T("help"))) {
4539 if (cmd == CMD_NONE)
4544 } else if (!tstrcmp(p, T("version"))) {
4553 print_usage_string(int cmd, FILE *fp)
4555 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4559 recommend_man_page(int cmd, FILE *fp)
4561 const tchar *format_str;
4563 format_str = T("Some uncommon options are not listed;\n"
4564 "See %"TS".pdf in the doc directory for more details.\n");
4566 format_str = T("Some uncommon options are not listed;\n"
4567 "Try `man %"TS"' for more details.\n");
4569 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4573 usage(int cmd, FILE *fp)
4575 tfprintf(fp, T("Usage:\n"));
4576 print_usage_string(cmd, fp);
4577 tfprintf(fp, T("\n"));
4578 recommend_man_page(cmd, fp);
4584 tfprintf(fp, T("Usage:\n"));
4585 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4586 print_usage_string(cmd, fp);
4587 tfprintf(fp, T("\n"));
4589 static const tchar *extra =
4592 " %"TS" --version\n"
4595 tfprintf(fp, extra, invocation_name, invocation_name);
4597 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4598 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4599 "For some commands IMAGE may be \"all\".\n"
4601 recommend_man_page(CMD_NONE, fp);
4604 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4605 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4606 * something else), while on Windows the command arguments will be UTF-16LE
4607 * encoded 'wchar_t' strings. */
4610 wmain(int argc, wchar_t **argv, wchar_t **envp)
4612 main(int argc, char **argv)
4619 imagex_info_file = stdout;
4620 invocation_name = tbasename(argv[0]);
4623 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4624 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4628 setlocale(LC_ALL, "");
4629 codeset = nl_langinfo(CODESET);
4630 if (!strstr(codeset, "UTF-8") &&
4631 !strstr(codeset, "UTF8") &&
4632 !strstr(codeset, "utf-8") &&
4633 !strstr(codeset, "utf8"))
4636 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4637 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4638 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4639 " to any value to force wimlib to use UTF-8.\n",
4645 #endif /* !__WIN32__ */
4648 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4649 if (igcase != NULL) {
4650 if (!tstrcmp(igcase, T("no")) ||
4651 !tstrcmp(igcase, T("0")))
4652 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4653 else if (!tstrcmp(igcase, T("yes")) ||
4654 !tstrcmp(igcase, T("1")))
4655 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4658 "WARNING: Ignoring unknown setting of "
4659 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4664 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4666 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4667 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4668 for (int i = 0; i < CMD_MAX; i++) {
4669 if (!tstrcmp(invocation_name + 3,
4670 imagex_commands[i].name))
4679 /* Unless already known from the invocation name, determine which
4680 * command was specified. */
4681 if (cmd == CMD_NONE) {
4683 imagex_error(T("No command specified!\n"));
4687 for (int i = 0; i < CMD_MAX; i++) {
4688 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4693 if (cmd != CMD_NONE) {
4699 /* Handle --help and --version. --help can be either for the program as
4700 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4701 * CMD_NONE). Note: help_or_version() will not return if a --help or
4702 * --version argument was found. */
4703 help_or_version(argc, argv, cmd);
4705 /* Bail if a valid command was not specified. */
4706 if (cmd == CMD_NONE) {
4707 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4712 /* Enable warning and error messages in wimlib to be more user-friendly.
4714 wimlib_set_print_errors(true);
4716 /* Initialize wimlib. */
4717 ret = wimlib_global_init(init_flags);
4719 goto out_check_status;
4721 /* Call the command handler function. */
4722 ret = imagex_commands[cmd].func(argc, argv, cmd);
4724 /* Check for error writing to standard output, especially since for some
4725 * commands, writing to standard output is part of the program's actual
4726 * behavior and not just for informational purposes. */
4727 if (ferror(stdout) || fclose(stdout)) {
4728 imagex_error_with_errno(T("error writing to standard output"));
4733 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4734 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4735 * error code from which an error message can be printed. */
4737 imagex_error(T("Exiting with error code %d:\n"
4739 wimlib_get_error_string(ret));
4740 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4741 imagex_error_with_errno(T("errno"));
4743 /* Make wimlib free any resources it's holding (although this is not
4744 * strictly necessary because the process is ending anyway). */
4745 wimlib_global_cleanup();