4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012, 2013, 2014, 2015 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_INCLUDE_INVALID_NAMES_OPTION,
165 IMAGEX_METADATA_OPTION,
166 IMAGEX_NEW_IMAGE_OPTION,
167 IMAGEX_NOCHECK_OPTION,
168 IMAGEX_NORPFIX_OPTION,
169 IMAGEX_NOT_PIPABLE_OPTION,
170 IMAGEX_NO_ACLS_OPTION,
171 IMAGEX_NO_ATTRIBUTES_OPTION,
172 IMAGEX_NO_GLOBS_OPTION,
173 IMAGEX_NO_REPLACE_OPTION,
174 IMAGEX_NO_SOLID_SORT_OPTION,
175 IMAGEX_NULLGLOB_OPTION,
176 IMAGEX_ONE_FILE_ONLY_OPTION,
178 IMAGEX_PIPABLE_OPTION,
179 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
180 IMAGEX_REBUILD_OPTION,
181 IMAGEX_RECOMPRESS_OPTION,
182 IMAGEX_RECURSIVE_OPTION,
184 IMAGEX_RESUME_OPTION,
187 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
188 IMAGEX_SOLID_COMPRESS_OPTION,
190 IMAGEX_SOURCE_LIST_OPTION,
191 IMAGEX_STAGING_DIR_OPTION,
192 IMAGEX_STREAMS_INTERFACE_OPTION,
193 IMAGEX_STRICT_ACLS_OPTION,
194 IMAGEX_THREADS_OPTION,
195 IMAGEX_TO_STDOUT_OPTION,
196 IMAGEX_UNIX_DATA_OPTION,
197 IMAGEX_UNSAFE_COMPACT_OPTION,
198 IMAGEX_UPDATE_OF_OPTION,
199 IMAGEX_VERBOSE_OPTION,
200 IMAGEX_WIMBOOT_CONFIG_OPTION,
201 IMAGEX_WIMBOOT_OPTION,
205 static const struct option apply_options[] = {
206 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
207 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
208 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
209 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
210 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
211 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
212 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
213 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
214 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
215 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
216 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
218 /* --resume is undocumented for now as it needs improvement. */
219 {T("resume"), no_argument, NULL, IMAGEX_RESUME_OPTION},
220 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
221 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
225 static const struct option capture_or_append_options[] = {
226 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
227 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
228 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
229 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
230 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
231 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
232 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
233 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
234 {T("pack-streams"), no_argument, NULL, IMAGEX_SOLID_OPTION},
235 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
236 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
237 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
238 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
239 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
240 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
241 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
242 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
243 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
244 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
245 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
246 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
247 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
248 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
249 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
250 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
251 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
252 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
253 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
254 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
255 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
256 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
257 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
258 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
262 static const struct option delete_options[] = {
263 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
264 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
265 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
269 static const struct option dir_options[] = {
270 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
271 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
272 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
273 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
277 static const struct option export_options[] = {
278 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
279 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
280 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
281 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
282 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
283 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
284 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
285 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
286 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
287 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
288 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
289 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
290 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
291 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
292 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
293 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
294 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
295 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
296 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
297 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
298 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
299 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
303 static const struct option extract_options[] = {
304 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
305 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
306 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
307 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
308 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
309 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
310 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
311 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
312 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
313 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
314 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
315 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
316 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
317 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
318 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
319 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
320 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
324 static const struct option info_options[] = {
325 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
326 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
327 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
328 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
329 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
330 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
331 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
332 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
333 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
334 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
338 static const struct option join_options[] = {
339 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
343 static const struct option mount_options[] = {
344 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
345 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
346 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
347 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
348 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
349 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
350 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
354 static const struct option optimize_options[] = {
355 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
356 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
357 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
358 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
359 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
360 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
361 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
362 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
363 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
364 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
365 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
366 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
367 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
368 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
369 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
370 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
371 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
372 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
373 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
377 static const struct option split_options[] = {
378 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
382 static const struct option unmount_options[] = {
383 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
384 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
385 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
386 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
387 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
388 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
392 static const struct option update_options[] = {
393 /* Careful: some of the options here set the defaults for update
394 * commands, but the flags given to an actual update command (and not to
395 * `imagex update' itself are also handled in
396 * update_command_add_option(). */
397 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
398 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
399 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
400 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
401 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
403 /* Default delete options */
404 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
405 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
407 /* Global add option */
408 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
410 /* Default add options */
411 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
412 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
413 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
414 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
415 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
416 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
417 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
418 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
423 static const struct option verify_options[] = {
424 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
425 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
431 # define _format_attribute(type, format_str, args_start) \
432 __attribute__((format(type, format_str, args_start)))
434 # define _format_attribute(type, format_str, args_start)
437 /* Print formatted error message to stderr. */
438 static void _format_attribute(printf, 1, 2)
439 imagex_error(const tchar *format, ...)
442 va_start(va, format);
443 tfputs(T("ERROR: "), stderr);
444 tvfprintf(stderr, format, va);
445 tputc(T('\n'), stderr);
449 /* Print formatted error message to stderr. */
450 static void _format_attribute(printf, 1, 2)
451 imagex_error_with_errno(const tchar *format, ...)
453 int errno_save = errno;
455 va_start(va, format);
456 tfputs(T("ERROR: "), stderr);
457 tvfprintf(stderr, format, va);
458 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
463 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
465 if (image == WIMLIB_NO_IMAGE) {
466 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
467 " Please specify a 1-based image index or "
468 "image name. To list the images\n"
469 " contained in the WIM archive, run\n"
471 " %"TS" \"%"TS"\"\n"),
472 image_name, wim_name,
473 get_cmd_string(CMD_INFO, false), wim_name);
474 return WIMLIB_ERR_INVALID_IMAGE;
480 verify_image_is_single(int image)
482 if (image == WIMLIB_ALL_IMAGES) {
483 imagex_error(T("Cannot specify all images for this action!"));
484 return WIMLIB_ERR_INVALID_IMAGE;
490 verify_image_exists_and_is_single(int image, const tchar *image_name,
491 const tchar *wim_name)
494 ret = verify_image_exists(image, image_name, wim_name);
496 ret = verify_image_is_single(image);
501 print_available_compression_types(FILE *fp)
503 static const tchar *s =
505 "Available compression types:\n"
508 " xpress (alias: \"fast\")\n"
509 " lzx (alias: \"maximum\") (default for capture)\n"
510 " lzms (alias: \"recovery\")\n"
516 /* Parse the argument to --compress */
518 get_compression_type(tchar *optarg)
521 unsigned int compression_level = 0;
524 plevel = tstrchr(optarg, T(':'));
530 ultmp = tstrtoul(plevel, &ptmp, 10);
531 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
532 imagex_error(T("Compression level must be a positive integer! "
533 "e.g. --compress=lzx:80"));
534 return WIMLIB_COMPRESSION_TYPE_INVALID;
536 compression_level = ultmp;
539 if (!tstrcasecmp(optarg, T("maximum")) ||
540 !tstrcasecmp(optarg, T("lzx")) ||
541 !tstrcasecmp(optarg, T("max")))
542 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
543 else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress")))
544 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
545 else if (!tstrcasecmp(optarg, T("recovery")) || !tstrcasecmp(optarg, T("lzms")))
546 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
547 else if (!tstrcasecmp(optarg, T("none")))
548 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
550 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
551 print_available_compression_types(stderr);
552 return WIMLIB_COMPRESSION_TYPE_INVALID;
555 if (compression_level != 0)
556 wimlib_set_default_compression_level(ctype, compression_level);
560 /* Parse the argument to --compact */
562 set_compact_mode(const tchar *arg, int *extract_flags)
565 if (!tstrcasecmp(arg, T("xpress4k")))
566 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
567 else if (!tstrcasecmp(arg, T("xpress8k")))
568 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
569 else if (!tstrcasecmp(arg, T("xpress16k")))
570 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
571 else if (!tstrcasecmp(arg, T("lzx")))
572 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
575 *extract_flags |= flag;
580 "\"%"TS"\" is not a recognized System Compression format. The options are:"
582 " --compact=xpress4k\n"
583 " --compact=xpress8k\n"
584 " --compact=xpress16k\n"
592 set_compress_slow(void)
595 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
596 " Use the '--compress=TYPE:LEVEL' option instead.\n");
598 wimlib_set_default_compression_level(-1, 100);
602 const tchar **strings;
603 unsigned num_strings;
604 unsigned num_alloc_strings;
607 #define STRING_SET_INITIALIZER \
608 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
610 #define STRING_SET(_strings) \
611 struct string_set _strings = STRING_SET_INITIALIZER
614 string_set_append(struct string_set *set, const tchar *glob)
616 unsigned num_alloc_strings = set->num_alloc_strings;
618 if (set->num_strings == num_alloc_strings) {
619 const tchar **new_strings;
621 num_alloc_strings += 4;
622 new_strings = realloc(set->strings,
623 sizeof(set->strings[0]) * num_alloc_strings);
625 imagex_error(T("Out of memory!"));
628 set->strings = new_strings;
629 set->num_alloc_strings = num_alloc_strings;
631 set->strings[set->num_strings++] = glob;
636 string_set_destroy(struct string_set *set)
642 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
644 return wimlib_reference_resource_files(wim, set->strings,
646 WIMLIB_REF_FLAG_GLOB_ENABLE,
651 do_resource_not_found_warning(const tchar *wimfile,
652 const struct wimlib_wim_info *info,
653 const struct string_set *refglobs)
655 if (info->total_parts > 1) {
656 if (refglobs->num_strings == 0) {
657 imagex_error(T("\"%"TS"\" is part of a split WIM. "
658 "Use --ref to specify the other parts."),
661 imagex_error(T("Perhaps the '--ref' argument did not "
662 "specify all other parts of the split "
666 imagex_error(T("If this is a delta WIM, use the --ref argument "
667 "to specify the WIM(s) on which it is based."));
672 do_metadata_not_found_warning(const tchar *wimfile,
673 const struct wimlib_wim_info *info)
675 if (info->part_number != 1) {
676 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
677 " You must specify the first part."),
682 /* Returns the size of a file given its name, or -1 if the file does not exist
683 * or its size cannot be determined. */
685 file_get_size(const tchar *filename)
688 if (tstat(filename, &st) == 0)
695 PARSE_STRING_SUCCESS = 0,
696 PARSE_STRING_FAILURE = 1,
697 PARSE_STRING_NONE = 2,
701 * Parses a string token from an array of characters.
703 * Tokens are either whitespace-delimited, or double or single-quoted.
705 * @line_p: Pointer to the pointer to the line of data. Will be updated
706 * to point past the string token iff the return value is
707 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
710 * @len_p: @len_p initially stores the length of the line of data, which may
711 * be 0, and it will be updated to the number of bytes remaining in
712 * the line iff the return value is PARSE_STRING_SUCCESS.
714 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
715 * parsed string token will be returned here.
717 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
718 * PARSE_STRING_FAILURE if the data was invalid due to a missing
719 * closing quote; or PARSE_STRING_NONE if the line ended before the
720 * beginning of a string token was found.
723 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
726 tchar *line = *line_p;
730 /* Skip leading whitespace */
733 return PARSE_STRING_NONE;
734 if (!istspace(*line) && *line != T('\0'))
740 if (quote_char == T('"') || quote_char == T('\'')) {
745 line = tmemchr(line, quote_char, len);
747 imagex_error(T("Missing closing quote: %"TS), fn - 1);
748 return PARSE_STRING_FAILURE;
751 /* Unquoted string. Go until whitespace. Line is terminated
752 * by '\0', so no need to check 'len'. */
756 } while (!istspace(*line) && *line != T('\0'));
763 return PARSE_STRING_SUCCESS;
766 /* Parses a line of data (not an empty line or comment) in the source list file
767 * format. (See the man page for 'wimlib-imagex capture' for details on this
768 * format and the meaning.)
770 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
771 * len == 0. The data in @line will be modified by this function call.
773 * @len: Length of the line of data.
775 * @source: On success, the capture source and target described by the line is
776 * written into this destination. Note that it will contain pointers
777 * to data in the @line array.
779 * Returns true if the line was valid; false otherwise. */
781 parse_source_list_line(tchar *line, size_t len,
782 struct wimlib_capture_source *source)
786 ret = parse_string(&line, &len, &source->fs_source_path);
787 if (ret != PARSE_STRING_SUCCESS)
789 ret = parse_string(&line, &len, &source->wim_target_path);
790 if (ret == PARSE_STRING_NONE)
791 source->wim_target_path = source->fs_source_path;
792 return ret != PARSE_STRING_FAILURE;
795 /* Returns %true if the given line of length @len > 0 is a comment or empty line
796 * in the source list file format. */
798 is_comment_line(const tchar *line, size_t len)
801 if (*line == T('#') || *line == T(';'))
803 if (!istspace(*line) && *line != T('\0'))
813 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
816 tchar *contents = *contents_p;
817 size_t nchars = *nchars_p;
820 for (i = 0; i < nchars; i++)
821 if (contents[i] == T('\n'))
824 /* Handle last line not terminated by a newline */
825 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
826 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
828 imagex_error(T("Out of memory!"));
831 contents[nchars] = T('\n');
832 *contents_p = contents;
840 /* Parses a file in the source list format. (See the man page for
841 * 'wimlib-imagex capture' for details on this format and the meaning.)
843 * @source_list_contents: Contents of the source list file. Note that this
844 * buffer will be modified to save memory allocations,
845 * and cannot be freed until the returned array of
846 * wimlib_capture_source's has also been freed.
848 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
851 * @nsources_ret: On success, the length of the returned array is
854 * Returns: An array of `struct wimlib_capture_source's that can be passed to
855 * the wimlib_add_image_multisource() function to specify how a WIM image is to
857 static struct wimlib_capture_source *
858 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
859 size_t *nsources_ret)
863 struct wimlib_capture_source *sources;
866 nlines = text_file_count_lines(source_list_contents_p,
867 &source_list_nchars);
871 /* Always allocate at least 1 slot, just in case the implementation of
872 * calloc() returns NULL if 0 bytes are requested. */
873 sources = calloc(nlines ?: 1, sizeof(*sources));
875 imagex_error(T("out of memory"));
878 p = *source_list_contents_p;
880 for (i = 0; i < nlines; i++) {
881 /* XXX: Could use rawmemchr() here instead, but it may not be
882 * available on all platforms. */
883 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
884 size_t len = endp - p + 1;
886 if (!is_comment_line(p, len)) {
887 if (!parse_source_list_line(p, len, &sources[j++])) {
899 /* Reads the contents of a file into memory. */
901 file_get_contents(const tchar *filename, size_t *len_ret)
908 if (tstat(filename, &stbuf) != 0) {
909 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
914 fp = tfopen(filename, T("rb"));
916 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
920 buf = malloc(len ? len : 1);
922 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
923 "contents of file \"%"TS"\""), len, filename);
926 if (fread(buf, 1, len, fp) != len) {
927 imagex_error_with_errno(T("Failed to read %zu bytes from the "
928 "file \"%"TS"\""), len, filename);
942 /* Read standard input until EOF and return the full contents in a malloc()ed
943 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
946 stdin_get_contents(size_t *len_ret)
948 /* stdin can, of course, be a pipe or other non-seekable file, so the
949 * total length of the data cannot be pre-determined */
951 size_t newlen = 1024;
955 char *p = realloc(buf, newlen);
956 size_t bytes_read, bytes_to_read;
958 imagex_error(T("out of memory while reading stdin"));
962 bytes_to_read = newlen - pos;
963 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
965 if (bytes_read != bytes_to_read) {
970 imagex_error_with_errno(T("error reading stdin"));
984 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
987 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
989 *num_tchars_ret = num_bytes;
991 #else /* !__WIN32__ */
992 /* On Windows, translate the text to UTF-16LE */
996 if (num_bytes >= 2 &&
997 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
998 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1000 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1001 * with something that looks like an ASCII character encoded as
1002 * a UTF-16LE code unit. Assume the file is encoded as
1003 * UTF-16LE. This is not a 100% reliable check. */
1004 num_wchars = num_bytes / 2;
1005 text_wstr = (wchar_t*)text;
1007 /* File does not look like UTF-16LE. Assume it is encoded in
1008 * the current Windows code page. I think these are always
1009 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1010 * should work as expected. */
1011 text_wstr = win32_mbs_to_wcs(text,
1016 *num_tchars_ret = num_wchars;
1018 #endif /* __WIN32__ */
1022 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1027 contents = file_get_contents(filename, &num_bytes);
1030 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1034 stdin_get_text_contents(size_t *num_tchars_ret)
1039 contents = stdin_get_contents(&num_bytes);
1042 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1045 #define TO_PERCENT(numerator, denominator) \
1046 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1048 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1049 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1050 #define KIBIBYTE_MIN_NBYTES 10000ULL
1053 get_unit(uint64_t total_bytes, const tchar **name_ret)
1055 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1056 *name_ret = T("GiB");
1058 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1059 *name_ret = T("MiB");
1061 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1062 *name_ret = T("KiB");
1065 *name_ret = T("bytes");
1070 static struct wimlib_progress_info_scan last_scan_progress;
1073 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1075 uint64_t prev_count, cur_count;
1077 prev_count = last_scan_progress.num_nondirs_scanned +
1078 last_scan_progress.num_dirs_scanned;
1079 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1081 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1082 cur_count % 128 == 0)
1084 unsigned unit_shift;
1085 const tchar *unit_name;
1087 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1088 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1089 "%"PRIu64" directories) "),
1090 scan->num_bytes_scanned >> unit_shift,
1092 scan->num_nondirs_scanned,
1093 scan->num_dirs_scanned);
1094 last_scan_progress = *scan;
1097 /* Progress callback function passed to various wimlib functions. */
1098 static enum wimlib_progress_status
1099 imagex_progress_func(enum wimlib_progress_msg msg,
1100 union wimlib_progress_info *info,
1101 void *_ignored_context)
1103 unsigned percent_done;
1104 unsigned unit_shift;
1105 const tchar *unit_name;
1107 if (imagex_be_quiet)
1108 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1110 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1112 static bool first = true;
1114 imagex_printf(T("Writing %"TS"-compressed data "
1115 "using %u thread%"TS"\n"),
1116 wimlib_get_compression_type_string(
1117 info->write_streams.compression_type),
1118 info->write_streams.num_threads,
1119 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1123 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1124 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1125 info->write_streams.total_bytes);
1127 imagex_printf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
1128 "written (%u%% done)"),
1129 info->write_streams.completed_bytes >> unit_shift,
1131 info->write_streams.total_bytes >> unit_shift,
1134 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1135 imagex_printf(T("\n"));
1137 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1138 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1139 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1140 imagex_printf(T("\n"));
1142 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1143 info->scan.wim_target_path);
1145 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1147 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1148 switch (info->scan.status) {
1149 case WIMLIB_SCAN_DENTRY_OK:
1150 report_scan_progress(&info->scan, false);
1152 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1153 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1155 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1156 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1157 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1159 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1160 /* Symlink fixups are enabled by default. This is
1161 * mainly intended for Windows, which for some reason
1162 * uses absolute junctions (with drive letters!) in the
1163 * default installation. On UNIX-like systems, warn the
1164 * user when fixing the target of an absolute symbolic
1165 * link, so they know to disable this if they want. */
1167 imagex_printf(T("\nWARNING: Adjusted target of "
1168 "absolute symbolic link \"%"TS"\"\n"
1169 " (Use --norpfix to capture "
1170 "absolute symbolic links as-is)\n"),
1171 info->scan.cur_path);
1178 case WIMLIB_PROGRESS_MSG_SCAN_END:
1179 report_scan_progress(&info->scan, true);
1180 imagex_printf(T("\n"));
1182 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1183 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1184 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1185 info->integrity.total_bytes);
1186 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1187 "of %"PRIu64" %"TS" (%u%%) done"),
1188 info->integrity.filename,
1189 info->integrity.completed_bytes >> unit_shift,
1191 info->integrity.total_bytes >> unit_shift,
1194 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1195 imagex_printf(T("\n"));
1197 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1198 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1199 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1200 info->integrity.total_bytes);
1201 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1202 "of %"PRIu64" %"TS" (%u%%) done"),
1203 info->integrity.completed_bytes >> unit_shift,
1205 info->integrity.total_bytes >> unit_shift,
1208 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1209 imagex_printf(T("\n"));
1211 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1212 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1213 "to %"TS" \"%"TS"\"\n"),
1214 info->extract.image,
1215 info->extract.image_name,
1216 info->extract.wimfile_name,
1217 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1218 T("NTFS volume") : T("directory")),
1219 info->extract.target);
1221 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1222 if (info->extract.end_file_count >= 2000) {
1223 percent_done = TO_PERCENT(info->extract.current_file_count,
1224 info->extract.end_file_count);
1225 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1226 info->extract.current_file_count,
1227 info->extract.end_file_count, percent_done);
1228 if (info->extract.current_file_count == info->extract.end_file_count)
1229 imagex_printf(T("\n"));
1232 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1233 percent_done = TO_PERCENT(info->extract.completed_bytes,
1234 info->extract.total_bytes);
1235 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1236 imagex_printf(T("\rExtracting file data: "
1237 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1238 info->extract.completed_bytes >> unit_shift,
1240 info->extract.total_bytes >> unit_shift,
1243 if (info->extract.completed_bytes >= info->extract.total_bytes)
1244 imagex_printf(T("\n"));
1246 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1247 if (info->extract.end_file_count >= 2000) {
1248 percent_done = TO_PERCENT(info->extract.current_file_count,
1249 info->extract.end_file_count);
1250 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1251 info->extract.current_file_count,
1252 info->extract.end_file_count, percent_done);
1253 if (info->extract.current_file_count == info->extract.end_file_count)
1254 imagex_printf(T("\n"));
1257 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1258 if (info->extract.total_parts != 1) {
1259 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1260 info->extract.part_number,
1261 info->extract.total_parts);
1264 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1265 percent_done = TO_PERCENT(info->split.completed_bytes,
1266 info->split.total_bytes);
1267 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1268 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1269 "%"PRIu64" %"TS" (%u%%) written\n"),
1270 info->split.part_name,
1271 info->split.cur_part_number,
1272 info->split.total_parts,
1273 info->split.completed_bytes >> unit_shift,
1275 info->split.total_bytes >> unit_shift,
1279 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1280 if (info->split.completed_bytes == info->split.total_bytes) {
1281 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1282 info->split.cur_part_number,
1283 info->split.total_parts);
1286 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1287 switch (info->update.command->op) {
1288 case WIMLIB_UPDATE_OP_DELETE:
1289 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1290 info->update.command->delete_.wim_path);
1292 case WIMLIB_UPDATE_OP_RENAME:
1293 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1294 info->update.command->rename.wim_source_path,
1295 info->update.command->rename.wim_target_path);
1297 case WIMLIB_UPDATE_OP_ADD:
1302 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1303 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1304 info->replace.path_in_wim);
1306 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1307 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1308 info->wimboot_exclude.path_in_wim);
1310 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1311 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1312 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1313 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1314 info->unmount.mounted_wim,
1315 info->unmount.mounted_image);
1317 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1318 info->unmount.mounted_wim,
1319 info->unmount.mounted_image);
1320 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1324 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1325 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1326 info->verify_image.current_image,
1327 info->verify_image.total_images);
1329 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1330 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1331 info->verify_streams.total_bytes);
1332 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1333 imagex_printf(T("\rVerifying file data: "
1334 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1335 info->verify_streams.completed_bytes >> unit_shift,
1337 info->verify_streams.total_bytes >> unit_shift,
1340 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1341 imagex_printf(T("\n"));
1346 fflush(imagex_info_file);
1347 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1351 parse_num_threads(const tchar *optarg)
1354 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1355 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1356 imagex_error(T("Number of threads must be a non-negative integer!"));
1364 parse_chunk_size(const tchar *optarg)
1367 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1368 if (chunk_size == 0) {
1369 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1370 " with optional K, M, or G suffix"));
1374 if (*tmp == T('k') || *tmp == T('K')) {
1377 } else if (*tmp == T('m') || *tmp == T('M')) {
1380 } else if (*tmp == T('g') || *tmp == T('G')) {
1384 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1385 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1389 if (chunk_size >= UINT32_MAX) {
1390 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1398 * Parse an option passed to an update command.
1400 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1403 * @option: Text string for the option (beginning with --)
1405 * @cmd: `struct wimlib_update_command' that is being constructed for
1408 * Returns true if the option was recognized; false if not.
1411 update_command_add_option(int op, const tchar *option,
1412 struct wimlib_update_command *cmd)
1414 bool recognized = true;
1416 case WIMLIB_UPDATE_OP_ADD:
1417 if (!tstrcmp(option, T("--verbose")))
1418 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1419 else if (!tstrcmp(option, T("--unix-data")))
1420 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1421 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1422 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1423 else if (!tstrcmp(option, T("--strict-acls")))
1424 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1425 else if (!tstrcmp(option, T("--dereference")))
1426 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1427 else if (!tstrcmp(option, T("--no-replace")))
1428 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1432 case WIMLIB_UPDATE_OP_DELETE:
1433 if (!tstrcmp(option, T("--force")))
1434 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1435 else if (!tstrcmp(option, T("--recursive")))
1436 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1447 /* How many nonoption arguments each `imagex update' command expects */
1448 static const unsigned update_command_num_nonoptions[] = {
1449 [WIMLIB_UPDATE_OP_ADD] = 2,
1450 [WIMLIB_UPDATE_OP_DELETE] = 1,
1451 [WIMLIB_UPDATE_OP_RENAME] = 2,
1455 update_command_add_nonoption(int op, const tchar *nonoption,
1456 struct wimlib_update_command *cmd,
1457 unsigned num_nonoptions)
1460 case WIMLIB_UPDATE_OP_ADD:
1461 if (num_nonoptions == 0)
1462 cmd->add.fs_source_path = (tchar*)nonoption;
1464 cmd->add.wim_target_path = (tchar*)nonoption;
1466 case WIMLIB_UPDATE_OP_DELETE:
1467 cmd->delete_.wim_path = (tchar*)nonoption;
1469 case WIMLIB_UPDATE_OP_RENAME:
1470 if (num_nonoptions == 0)
1471 cmd->rename.wim_source_path = (tchar*)nonoption;
1473 cmd->rename.wim_target_path = (tchar*)nonoption;
1479 * Parse a command passed on stdin to `imagex update'.
1481 * @line: Text of the command.
1482 * @len: Length of the line, including a null terminator
1485 * @command: A `struct wimlib_update_command' to fill in from the parsed
1488 * @line_number: Line number of the command, for diagnostics.
1490 * Returns true on success; returns false on parse error.
1493 parse_update_command(tchar *line, size_t len,
1494 struct wimlib_update_command *command,
1498 tchar *command_name;
1500 size_t num_nonoptions;
1502 /* Get the command name ("add", "delete", "rename") */
1503 ret = parse_string(&line, &len, &command_name);
1504 if (ret != PARSE_STRING_SUCCESS)
1507 if (!tstrcasecmp(command_name, T("add"))) {
1508 op = WIMLIB_UPDATE_OP_ADD;
1509 } else if (!tstrcasecmp(command_name, T("delete"))) {
1510 op = WIMLIB_UPDATE_OP_DELETE;
1511 } else if (!tstrcasecmp(command_name, T("rename"))) {
1512 op = WIMLIB_UPDATE_OP_RENAME;
1514 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1515 command_name, line_number);
1520 /* Parse additional options and non-options as needed */
1525 ret = parse_string(&line, &len, &next_string);
1526 if (ret == PARSE_STRING_NONE) /* End of line */
1528 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1530 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1532 if (!update_command_add_option(op, next_string, command))
1534 imagex_error(T("Unrecognized option \"%"TS"\" to "
1535 "update command \"%"TS"\" on line %zu"),
1536 next_string, command_name, line_number);
1542 if (num_nonoptions == update_command_num_nonoptions[op])
1544 imagex_error(T("Unexpected argument \"%"TS"\" in "
1545 "update command on line %zu\n"
1546 " (The \"%"TS"\" command only "
1547 "takes %zu nonoption arguments!)\n"),
1548 next_string, line_number,
1549 command_name, num_nonoptions);
1552 update_command_add_nonoption(op, next_string,
1553 command, num_nonoptions);
1558 if (num_nonoptions != update_command_num_nonoptions[op]) {
1559 imagex_error(T("Not enough arguments to update command "
1560 "\"%"TS"\" on line %zu"), command_name, line_number);
1566 static struct wimlib_update_command *
1567 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1568 size_t *num_cmds_ret)
1572 struct wimlib_update_command *cmds;
1575 nlines = text_file_count_lines(cmd_file_contents_p,
1580 /* Always allocate at least 1 slot, just in case the implementation of
1581 * calloc() returns NULL if 0 bytes are requested. */
1582 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1584 imagex_error(T("out of memory"));
1587 p = *cmd_file_contents_p;
1589 for (i = 0; i < nlines; i++) {
1590 /* XXX: Could use rawmemchr() here instead, but it may not be
1591 * available on all platforms. */
1592 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1593 size_t len = endp - p + 1;
1595 if (!is_comment_line(p, len)) {
1596 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1607 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1608 * one image from a WIM file to an NTFS volume. */
1610 imagex_apply(int argc, tchar **argv, int cmd)
1614 int image = WIMLIB_NO_IMAGE;
1616 struct wimlib_wim_info info;
1618 const tchar *wimfile;
1619 const tchar *target;
1620 const tchar *image_num_or_name = NULL;
1621 int extract_flags = 0;
1623 STRING_SET(refglobs);
1625 for_opt(c, apply_options) {
1627 case IMAGEX_CHECK_OPTION:
1628 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1630 case IMAGEX_VERBOSE_OPTION:
1631 /* No longer does anything. */
1633 case IMAGEX_REF_OPTION:
1634 ret = string_set_append(&refglobs, optarg);
1636 goto out_free_refglobs;
1638 case IMAGEX_UNIX_DATA_OPTION:
1639 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1641 case IMAGEX_NO_ACLS_OPTION:
1642 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1644 case IMAGEX_STRICT_ACLS_OPTION:
1645 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1647 case IMAGEX_NO_ATTRIBUTES_OPTION:
1648 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1650 case IMAGEX_NORPFIX_OPTION:
1651 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1653 case IMAGEX_RPFIX_OPTION:
1654 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1656 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1657 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1658 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1660 case IMAGEX_RESUME_OPTION:
1661 extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME;
1663 case IMAGEX_WIMBOOT_OPTION:
1664 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1666 case IMAGEX_COMPACT_OPTION:
1667 ret = set_compact_mode(optarg, &extract_flags);
1669 goto out_free_refglobs;
1677 if (argc != 2 && argc != 3)
1682 if (!tstrcmp(wimfile, T("-"))) {
1683 /* Attempt to apply pipable WIM from standard input. */
1685 image_num_or_name = NULL;
1688 image_num_or_name = argv[1];
1693 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1694 imagex_progress_func, NULL);
1696 goto out_free_refglobs;
1698 wimlib_get_wim_info(wim, &info);
1701 /* Image explicitly specified. */
1702 image_num_or_name = argv[1];
1703 image = wimlib_resolve_image(wim, image_num_or_name);
1704 ret = verify_image_exists(image, image_num_or_name, wimfile);
1706 goto out_wimlib_free;
1709 /* No image specified; default to image 1, but only if the WIM
1710 * contains exactly one image. */
1712 if (info.image_count != 1) {
1713 imagex_error(T("\"%"TS"\" contains %d images; "
1714 "Please select one (or all)."),
1715 wimfile, info.image_count);
1724 if (refglobs.num_strings) {
1726 imagex_error(T("Can't specify --ref when applying from stdin!"));
1728 goto out_wimlib_free;
1730 ret = wim_reference_globs(wim, &refglobs, open_flags);
1732 goto out_wimlib_free;
1737 /* Interpret a regular file or block device target as an NTFS
1741 if (tstat(target, &stbuf)) {
1742 if (errno != ENOENT) {
1743 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1746 goto out_wimlib_free;
1749 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1750 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1756 ret = wimlib_extract_image(wim, image, target, extract_flags);
1758 set_fd_to_binary_mode(STDIN_FILENO);
1759 ret = wimlib_extract_image_from_pipe_with_progress(
1764 imagex_progress_func,
1768 imagex_printf(T("Done applying WIM image.\n"));
1769 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1771 do_resource_not_found_warning(wimfile, &info, &refglobs);
1773 imagex_error(T( "If you are applying an image "
1774 "from a split pipable WIM,\n"
1775 " make sure you have "
1776 "concatenated together all parts."));
1778 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1779 do_metadata_not_found_warning(wimfile, &info);
1784 string_set_destroy(&refglobs);
1788 usage(CMD_APPLY, stderr);
1790 goto out_free_refglobs;
1793 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1794 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1795 * the desired image. 'wimlib-imagex append': add a new image to an existing
1798 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1802 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1803 WIMLIB_ADD_FLAG_WINCONFIG |
1804 WIMLIB_ADD_FLAG_VERBOSE;
1805 int write_flags = 0;
1806 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1807 uint32_t chunk_size = UINT32_MAX;
1808 uint32_t solid_chunk_size = UINT32_MAX;
1809 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1810 const tchar *wimfile;
1814 const tchar *flags_element = NULL;
1817 STRING_SET(base_wimfiles);
1818 WIMStruct **base_wims;
1820 WIMStruct *template_wim;
1821 const tchar *template_wimfile = NULL;
1822 const tchar *template_image_name_or_num = NULL;
1823 int template_image = WIMLIB_NO_IMAGE;
1826 unsigned num_threads = 0;
1831 tchar *config_file = NULL;
1833 bool source_list = false;
1834 size_t source_list_nchars = 0;
1835 tchar *source_list_contents;
1836 bool capture_sources_malloced;
1837 struct wimlib_capture_source *capture_sources;
1839 bool name_defaulted;
1841 for_opt(c, capture_or_append_options) {
1843 case IMAGEX_BOOT_OPTION:
1844 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1846 case IMAGEX_CHECK_OPTION:
1847 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1848 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1850 case IMAGEX_NOCHECK_OPTION:
1851 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1853 case IMAGEX_CONFIG_OPTION:
1854 config_file = optarg;
1855 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1857 case IMAGEX_COMPRESS_OPTION:
1858 compression_type = get_compression_type(optarg);
1859 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1862 case IMAGEX_COMPRESS_SLOW_OPTION:
1863 set_compress_slow();
1865 case IMAGEX_CHUNK_SIZE_OPTION:
1866 chunk_size = parse_chunk_size(optarg);
1867 if (chunk_size == UINT32_MAX)
1870 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1871 solid_chunk_size = parse_chunk_size(optarg);
1872 if (solid_chunk_size == UINT32_MAX)
1875 case IMAGEX_SOLID_COMPRESS_OPTION:
1876 solid_ctype = get_compression_type(optarg);
1877 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1880 case IMAGEX_SOLID_OPTION:
1881 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1883 case IMAGEX_NO_SOLID_SORT_OPTION:
1884 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1886 case IMAGEX_FLAGS_OPTION:
1887 flags_element = optarg;
1889 case IMAGEX_DEREFERENCE_OPTION:
1890 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1892 case IMAGEX_VERBOSE_OPTION:
1893 /* No longer does anything. */
1895 case IMAGEX_THREADS_OPTION:
1896 num_threads = parse_num_threads(optarg);
1897 if (num_threads == UINT_MAX)
1900 case IMAGEX_REBUILD_OPTION:
1901 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1903 case IMAGEX_UNIX_DATA_OPTION:
1904 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1906 case IMAGEX_SOURCE_LIST_OPTION:
1909 case IMAGEX_NO_ACLS_OPTION:
1910 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1912 case IMAGEX_STRICT_ACLS_OPTION:
1913 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1915 case IMAGEX_RPFIX_OPTION:
1916 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1918 case IMAGEX_NORPFIX_OPTION:
1919 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1921 case IMAGEX_PIPABLE_OPTION:
1922 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1924 case IMAGEX_NOT_PIPABLE_OPTION:
1925 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1927 case IMAGEX_UPDATE_OF_OPTION:
1928 if (template_image_name_or_num) {
1929 imagex_error(T("'--update-of' can only be "
1930 "specified one time!"));
1934 colon = tstrrchr(optarg, T(':'));
1937 template_wimfile = optarg;
1939 template_image_name_or_num = colon + 1;
1941 template_wimfile = NULL;
1942 template_image_name_or_num = optarg;
1946 case IMAGEX_DELTA_FROM_OPTION:
1947 if (cmd != CMD_CAPTURE) {
1948 imagex_error(T("'--delta-from' is only "
1949 "valid for capture!"));
1952 ret = string_set_append(&base_wimfiles, optarg);
1954 goto out_free_base_wimfiles;
1955 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
1957 case IMAGEX_WIMBOOT_OPTION:
1958 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
1960 case IMAGEX_UNSAFE_COMPACT_OPTION:
1961 if (cmd != CMD_APPEND) {
1962 imagex_error(T("'--unsafe-compact' is only "
1963 "valid for append!"));
1966 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
1975 if (argc < 2 || argc > 4)
1981 /* Set default compression type and parameters. */
1984 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
1985 /* No compression type specified. Use the default. */
1987 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
1988 /* With --wimboot, default to XPRESS compression. */
1989 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
1990 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
1991 /* With --solid, default to LZMS compression. (However,
1992 * this will not affect solid resources!) */
1993 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
1995 /* Otherwise, default to LZX compression. */
1996 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2000 if (!tstrcmp(wimfile, T("-"))) {
2001 /* Writing captured WIM to standard output. */
2003 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2004 imagex_error("Can't write a non-pipable WIM to "
2005 "standard output! Specify --pipable\n"
2006 " if you want to create a pipable WIM "
2007 "(but read the docs first).");
2011 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2013 if (cmd == CMD_APPEND) {
2014 imagex_error(T("Using standard output for append does "
2015 "not make sense."));
2018 wim_fd = STDOUT_FILENO;
2020 imagex_info_file = stderr;
2021 set_fd_to_binary_mode(wim_fd);
2024 /* If template image was specified using --update-of=IMAGE rather
2025 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2026 if (template_image_name_or_num && !template_wimfile) {
2027 if (base_wimfiles.num_strings == 1) {
2028 /* Capturing delta WIM based on single WIM: default to
2030 template_wimfile = base_wimfiles.strings[0];
2031 } else if (cmd == CMD_APPEND) {
2032 /* Appending to WIM: default to WIM being appended to.
2034 template_wimfile = wimfile;
2036 /* Capturing a normal (non-delta) WIM, so the WIM file
2037 * *must* be explicitly specified. */
2038 if (base_wimfiles.num_strings > 1) {
2039 imagex_error(T("For capture of delta WIM "
2040 "based on multiple existing "
2042 " '--update-of' must "
2043 "specify WIMFILE:IMAGE!"));
2045 imagex_error(T("For capture of non-delta WIM, "
2046 "'--update-of' must specify "
2055 name_defaulted = false;
2057 /* Set default name to SOURCE argument, omitting any directory
2058 * prefixes and trailing slashes. This requires making a copy
2059 * of @source. Leave some free characters at the end in case we
2060 * append a number to keep the name unique. */
2061 size_t source_name_len;
2063 source_name_len = tstrlen(source);
2064 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2065 name = tbasename(tstrcpy(source_copy, source));
2066 name_defaulted = true;
2068 /* Image description defaults to NULL if not given. */
2075 /* Set up capture sources in source list mode */
2076 if (source[0] == T('-') && source[1] == T('\0')) {
2077 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2079 source_list_contents = file_get_text_contents(source,
2080 &source_list_nchars);
2082 if (!source_list_contents)
2085 capture_sources = parse_source_list(&source_list_contents,
2088 if (!capture_sources) {
2090 goto out_free_source_list_contents;
2092 capture_sources_malloced = true;
2094 /* Set up capture source in non-source-list mode. */
2095 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2096 capture_sources[0].fs_source_path = source;
2097 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2098 capture_sources[0].reserved = 0;
2100 capture_sources_malloced = false;
2101 source_list_contents = NULL;
2104 /* Open the existing WIM, or create a new one. */
2105 if (cmd == CMD_APPEND) {
2106 ret = wimlib_open_wim_with_progress(wimfile,
2107 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2109 imagex_progress_func,
2112 goto out_free_capture_sources;
2114 ret = wimlib_create_new_wim(compression_type, &wim);
2116 goto out_free_capture_sources;
2117 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2120 /* Set chunk size if non-default. */
2121 if (chunk_size != UINT32_MAX) {
2122 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2125 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2127 int ctype = compression_type;
2129 if (cmd == CMD_APPEND) {
2130 struct wimlib_wim_info info;
2131 wimlib_get_wim_info(wim, &info);
2132 ctype = info.compression_type;
2135 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2136 ret = wimlib_set_output_chunk_size(wim, 4096);
2141 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2142 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2146 if (solid_chunk_size != UINT32_MAX) {
2147 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2153 /* Detect if source is regular file or block device and set NTFS volume
2158 if (tstat(source, &stbuf) == 0) {
2159 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2160 imagex_printf(T("Capturing WIM image from NTFS "
2161 "filesystem on \"%"TS"\"\n"), source);
2162 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2165 if (errno != ENOENT) {
2166 imagex_error_with_errno(T("Failed to stat "
2167 "\"%"TS"\""), source);
2175 /* If the user did not specify an image name, and the basename of the
2176 * source already exists as an image name in the WIM file, append a
2177 * suffix to make it unique. */
2178 if (cmd == CMD_APPEND && name_defaulted) {
2179 unsigned long conflict_idx;
2180 tchar *name_end = tstrchr(name, T('\0'));
2181 for (conflict_idx = 1;
2182 wimlib_image_name_in_use(wim, name);
2185 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2189 /* If capturing a delta WIM, reference resources from the base WIMs
2190 * before adding the new image. */
2191 if (base_wimfiles.num_strings) {
2192 base_wims = calloc(base_wimfiles.num_strings,
2193 sizeof(base_wims[0]));
2194 if (base_wims == NULL) {
2195 imagex_error(T("Out of memory!"));
2200 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2201 ret = wimlib_open_wim_with_progress(
2202 base_wimfiles.strings[i], open_flags,
2203 &base_wims[i], imagex_progress_func, NULL);
2205 goto out_free_base_wims;
2209 ret = wimlib_reference_resources(wim, base_wims,
2210 base_wimfiles.num_strings, 0);
2212 goto out_free_base_wims;
2214 if (base_wimfiles.num_strings == 1) {
2215 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2216 base_wimfiles.strings[0]);
2218 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2219 base_wimfiles.num_strings);
2226 /* If capturing or appending as an update of an existing (template) image,
2227 * open the WIM if needed and parse the image index. */
2228 if (template_image_name_or_num) {
2231 if (base_wimfiles.num_strings == 1 &&
2232 template_wimfile == base_wimfiles.strings[0]) {
2233 template_wim = base_wims[0];
2234 } else if (template_wimfile == wimfile) {
2237 ret = wimlib_open_wim_with_progress(template_wimfile,
2240 imagex_progress_func,
2243 goto out_free_base_wims;
2246 template_image = wimlib_resolve_image(template_wim,
2247 template_image_name_or_num);
2249 if (template_image_name_or_num[0] == T('-')) {
2252 struct wimlib_wim_info info;
2254 wimlib_get_wim_info(template_wim, &info);
2255 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2256 if (n >= 1 && n <= info.image_count &&
2258 tmp != template_image_name_or_num + 1)
2260 template_image = info.image_count - (n - 1);
2263 ret = verify_image_exists_and_is_single(template_image,
2264 template_image_name_or_num,
2267 goto out_free_template_wim;
2269 template_wim = NULL;
2272 ret = wimlib_add_image_multisource(wim,
2279 goto out_free_template_wim;
2281 if (desc || flags_element || template_image_name_or_num) {
2282 /* User provided <DESCRIPTION> or <FLAGS> element, or an image
2283 * on which the added one is to be based has been specified with
2284 * --update-of. Get the index of the image we just
2285 * added, then use it to call the appropriate functions. */
2286 struct wimlib_wim_info info;
2288 wimlib_get_wim_info(wim, &info);
2291 ret = wimlib_set_image_descripton(wim,
2295 goto out_free_template_wim;
2298 if (flags_element) {
2299 ret = wimlib_set_image_flags(wim, info.image_count,
2302 goto out_free_template_wim;
2305 /* Reference template image if the user provided one. */
2306 if (template_image_name_or_num) {
2307 imagex_printf(T("Using image %d "
2308 "from \"%"TS"\" as template\n"),
2309 template_image, template_wimfile);
2310 ret = wimlib_reference_template_image(wim,
2316 goto out_free_template_wim;
2320 /* Write the new WIM or overwrite the existing WIM with the new image
2322 if (cmd == CMD_APPEND) {
2323 ret = wimlib_overwrite(wim, write_flags, num_threads);
2324 } else if (wimfile) {
2325 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2326 write_flags, num_threads);
2328 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2329 write_flags, num_threads);
2331 out_free_template_wim:
2332 /* template_wim may alias base_wims[0] or wim. */
2333 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2334 template_wim != wim)
2335 wimlib_free(template_wim);
2337 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2338 wimlib_free(base_wims[i]);
2342 out_free_capture_sources:
2343 if (capture_sources_malloced)
2344 free(capture_sources);
2345 out_free_source_list_contents:
2346 free(source_list_contents);
2347 out_free_base_wimfiles:
2348 string_set_destroy(&base_wimfiles);
2355 goto out_free_base_wimfiles;
2358 /* Remove image(s) from a WIM. */
2360 imagex_delete(int argc, tchar **argv, int cmd)
2363 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2364 int write_flags = 0;
2365 const tchar *wimfile;
2366 const tchar *image_num_or_name;
2371 for_opt(c, delete_options) {
2373 case IMAGEX_CHECK_OPTION:
2374 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2375 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2377 case IMAGEX_SOFT_OPTION:
2378 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2380 case IMAGEX_UNSAFE_COMPACT_OPTION:
2381 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2392 imagex_error(T("Must specify a WIM file"));
2394 imagex_error(T("Must specify an image"));
2398 image_num_or_name = argv[1];
2400 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2401 imagex_progress_func, NULL);
2405 image = wimlib_resolve_image(wim, image_num_or_name);
2407 ret = verify_image_exists(image, image_num_or_name, wimfile);
2409 goto out_wimlib_free;
2411 ret = wimlib_delete_image(wim, image);
2413 imagex_error(T("Failed to delete image from \"%"TS"\""),
2415 goto out_wimlib_free;
2418 ret = wimlib_overwrite(wim, write_flags, 0);
2420 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2421 "deleted"), wimfile);
2429 usage(CMD_DELETE, stderr);
2434 struct print_dentry_options {
2439 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2441 tprintf(T("%"TS"\n"), dentry->full_path);
2444 static const struct {
2447 } file_attr_flags[] = {
2448 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2449 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2450 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2451 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2452 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2453 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2454 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2455 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2456 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2457 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2458 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2459 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2460 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2461 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2462 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2465 #define TIMESTR_MAX 100
2468 timespec_to_string(const struct timespec *spec, tchar *buf)
2470 time_t t = spec->tv_sec;
2473 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2474 buf[TIMESTR_MAX - 1] = '\0';
2478 print_time(const tchar *type, const struct timespec *spec)
2480 tchar timestr[TIMESTR_MAX];
2482 timespec_to_string(spec, timestr);
2484 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2487 static void print_byte_field(const uint8_t field[], size_t len)
2490 tprintf(T("%02hhx"), *field++);
2494 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2496 tchar attr_string[256];
2499 tputs(T("WIM Information:"));
2500 tputs(T("----------------"));
2501 tprintf(T("Path: %"TS"\n"), wimfile);
2502 tprintf(T("GUID: 0x"));
2503 print_byte_field(info->guid, sizeof(info->guid));
2505 tprintf(T("Version: %u\n"), info->wim_version);
2506 tprintf(T("Image Count: %d\n"), info->image_count);
2507 tprintf(T("Compression: %"TS"\n"),
2508 wimlib_get_compression_type_string(info->compression_type));
2509 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2511 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2512 tprintf(T("Boot Index: %d\n"), info->boot_index);
2513 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2515 attr_string[0] = T('\0');
2518 tstrcat(attr_string, T("Pipable, "));
2520 if (info->has_integrity_table)
2521 tstrcat(attr_string, T("Integrity info, "));
2523 if (info->has_rpfix)
2524 tstrcat(attr_string, T("Relative path junction, "));
2526 if (info->resource_only)
2527 tstrcat(attr_string, T("Resource only, "));
2529 if (info->metadata_only)
2530 tstrcat(attr_string, T("Metadata only, "));
2532 if (info->is_marked_readonly)
2533 tstrcat(attr_string, T("Readonly, "));
2535 p = tstrchr(attr_string, T('\0'));
2536 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2539 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2543 print_resource(const struct wimlib_resource_entry *resource,
2546 tprintf(T("Hash = 0x"));
2547 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2550 if (!resource->is_missing) {
2551 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2552 resource->uncompressed_size);
2553 if (resource->packed) {
2554 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2555 "bytes @ offset %"PRIu64"\n"),
2556 resource->raw_resource_uncompressed_size,
2557 resource->raw_resource_compressed_size,
2558 resource->raw_resource_offset_in_wim);
2560 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2563 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2564 resource->compressed_size);
2566 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2570 tprintf(T("Part Number = %u\n"), resource->part_number);
2571 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2573 tprintf(T("Flags = "));
2574 if (resource->is_compressed)
2575 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2576 if (resource->is_metadata)
2577 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2578 if (resource->is_free)
2579 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2580 if (resource->is_spanned)
2581 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2582 if (resource->packed)
2583 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2591 print_blobs(WIMStruct *wim)
2593 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2597 default_print_security_descriptor(const uint8_t *sd, size_t size)
2599 tprintf(T("Security Descriptor = "));
2600 print_byte_field(sd, size);
2605 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2609 "----------------------------------------------------------------------------\n"));
2610 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2611 if (dentry->dos_name)
2612 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2613 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2614 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2615 if (file_attr_flags[i].flag & dentry->attributes)
2616 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2617 file_attr_flags[i].name);
2619 if (dentry->security_descriptor) {
2620 print_security_descriptor(dentry->security_descriptor,
2621 dentry->security_descriptor_size);
2624 print_time(T("Creation Time"), &dentry->creation_time);
2625 print_time(T("Last Write Time"), &dentry->last_write_time);
2626 print_time(T("Last Access Time"), &dentry->last_access_time);
2629 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2630 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2632 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2633 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2635 if (dentry->unix_mode != 0) {
2636 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2637 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2638 dentry->unix_uid, dentry->unix_gid,
2639 dentry->unix_mode, dentry->unix_rdev);
2642 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2643 if (dentry->streams[i].stream_name) {
2644 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2645 dentry->streams[i].stream_name);
2646 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2647 tprintf(T("\tRaw encrypted data stream:\n"));
2648 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2649 tprintf(T("\tReparse point stream:\n"));
2651 tprintf(T("\tUnnamed data stream:\n"));
2653 print_resource(&dentry->streams[i].resource, NULL);
2658 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2660 const struct print_dentry_options *options = _options;
2661 if (!options->detailed)
2662 print_dentry_full_path(dentry);
2664 print_dentry_detailed(dentry);
2668 /* Print the files contained in an image(s) in a WIM file. */
2670 imagex_dir(int argc, tchar **argv, int cmd)
2672 const tchar *wimfile;
2673 WIMStruct *wim = NULL;
2676 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2678 struct print_dentry_options options = {
2681 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2683 STRING_SET(refglobs);
2685 for_opt(c, dir_options) {
2687 case IMAGEX_PATH_OPTION:
2690 case IMAGEX_DETAILED_OPTION:
2691 options.detailed = true;
2693 case IMAGEX_ONE_FILE_ONLY_OPTION:
2694 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2696 case IMAGEX_REF_OPTION:
2697 ret = string_set_append(&refglobs, optarg);
2699 goto out_free_refglobs;
2709 imagex_error(T("Must specify a WIM file"));
2713 imagex_error(T("Too many arguments"));
2718 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2719 imagex_progress_func, NULL);
2721 goto out_free_refglobs;
2724 image = wimlib_resolve_image(wim, argv[1]);
2725 ret = verify_image_exists(image, argv[1], wimfile);
2727 goto out_wimlib_free;
2729 /* No image specified; default to image 1, but only if the WIM
2730 * contains exactly one image. */
2732 struct wimlib_wim_info info;
2734 wimlib_get_wim_info(wim, &info);
2735 if (info.image_count != 1) {
2736 imagex_error(T("\"%"TS"\" contains %d images; Please "
2737 "select one (or all)."),
2738 wimfile, info.image_count);
2745 if (refglobs.num_strings) {
2746 ret = wim_reference_globs(wim, &refglobs, 0);
2748 goto out_wimlib_free;
2751 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2752 print_dentry, &options);
2753 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2754 struct wimlib_wim_info info;
2756 wimlib_get_wim_info(wim, &info);
2757 do_metadata_not_found_warning(wimfile, &info);
2762 string_set_destroy(&refglobs);
2766 usage(CMD_DIR, stderr);
2768 goto out_free_refglobs;
2771 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2774 imagex_export(int argc, tchar **argv, int cmd)
2778 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2779 int write_flags = 0;
2780 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2781 const tchar *src_wimfile;
2782 const tchar *src_image_num_or_name;
2783 const tchar *dest_wimfile;
2785 const tchar *dest_name;
2786 const tchar *dest_desc;
2788 struct wimlib_wim_info src_info;
2789 WIMStruct *dest_wim;
2794 STRING_SET(refglobs);
2795 unsigned num_threads = 0;
2796 uint32_t chunk_size = UINT32_MAX;
2797 uint32_t solid_chunk_size = UINT32_MAX;
2798 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2800 for_opt(c, export_options) {
2802 case IMAGEX_BOOT_OPTION:
2803 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2805 case IMAGEX_CHECK_OPTION:
2806 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2807 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2809 case IMAGEX_NOCHECK_OPTION:
2810 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2812 case IMAGEX_COMPRESS_OPTION:
2813 compression_type = get_compression_type(optarg);
2814 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2817 case IMAGEX_COMPRESS_SLOW_OPTION:
2818 set_compress_slow();
2819 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2821 case IMAGEX_RECOMPRESS_OPTION:
2822 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2824 case IMAGEX_SOLID_OPTION:
2825 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2827 case IMAGEX_NO_SOLID_SORT_OPTION:
2828 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2830 case IMAGEX_CHUNK_SIZE_OPTION:
2831 chunk_size = parse_chunk_size(optarg);
2832 if (chunk_size == UINT32_MAX)
2835 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2836 solid_chunk_size = parse_chunk_size(optarg);
2837 if (solid_chunk_size == UINT32_MAX)
2840 case IMAGEX_SOLID_COMPRESS_OPTION:
2841 solid_ctype = get_compression_type(optarg);
2842 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2845 case IMAGEX_REF_OPTION:
2846 ret = string_set_append(&refglobs, optarg);
2848 goto out_free_refglobs;
2850 case IMAGEX_THREADS_OPTION:
2851 num_threads = parse_num_threads(optarg);
2852 if (num_threads == UINT_MAX)
2855 case IMAGEX_REBUILD_OPTION:
2856 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2858 case IMAGEX_PIPABLE_OPTION:
2859 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2861 case IMAGEX_NOT_PIPABLE_OPTION:
2862 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2864 case IMAGEX_WIMBOOT_OPTION:
2865 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2867 case IMAGEX_UNSAFE_COMPACT_OPTION:
2868 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2876 if (argc < 3 || argc > 5)
2878 src_wimfile = argv[0];
2879 src_image_num_or_name = argv[1];
2880 dest_wimfile = argv[2];
2881 dest_name = (argc >= 4) ? argv[3] : NULL;
2882 dest_desc = (argc >= 5) ? argv[4] : NULL;
2883 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2884 imagex_progress_func, NULL);
2886 goto out_free_refglobs;
2888 wimlib_get_wim_info(src_wim, &src_info);
2890 /* Determine if the destination is an existing file or not. If so, we
2891 * try to append the exported image(s) to it; otherwise, we create a new
2892 * WIM containing the exported image(s). Furthermore, determine if we
2893 * need to write a pipable WIM directly to standard output. */
2895 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2897 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2898 imagex_error("Can't write a non-pipable WIM to "
2899 "standard output! Specify --pipable\n"
2900 " if you want to create a pipable WIM "
2901 "(but read the docs first).");
2903 goto out_free_src_wim;
2906 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2908 dest_wimfile = NULL;
2909 dest_wim_fd = STDOUT_FILENO;
2910 imagex_info_file = stderr;
2911 set_fd_to_binary_mode(dest_wim_fd);
2914 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2916 /* Destination file exists. */
2918 if (!S_ISREG(stbuf.st_mode)) {
2919 imagex_error(T("\"%"TS"\" is not a regular file"),
2922 goto out_free_src_wim;
2924 ret = wimlib_open_wim_with_progress(dest_wimfile,
2926 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2928 imagex_progress_func,
2931 goto out_free_src_wim;
2933 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2934 /* The user specified a compression type, but we're
2935 * exporting to an existing WIM. Make sure the
2936 * specified compression type is the same as the
2937 * compression type of the existing destination WIM. */
2938 struct wimlib_wim_info dest_info;
2940 wimlib_get_wim_info(dest_wim, &dest_info);
2941 if (compression_type != dest_info.compression_type) {
2942 imagex_error(T("Cannot specify a compression type that is "
2943 "not the same as that used in the "
2944 "destination WIM"));
2946 goto out_free_dest_wim;
2952 if (errno != ENOENT) {
2953 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
2956 goto out_free_src_wim;
2959 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
2960 imagex_error(T("'--unsafe-compact' is only valid when "
2961 "exporting to an existing WIM file!"));
2963 goto out_free_src_wim;
2966 /* dest_wimfile is not an existing file, so create a new WIM. */
2968 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2969 /* The user did not specify a compression type; default
2970 * to that of the source WIM, unless --solid or
2971 * --wimboot was specified. */
2973 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
2974 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2975 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2976 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2978 compression_type = src_info.compression_type;
2980 ret = wimlib_create_new_wim(compression_type, &dest_wim);
2982 goto out_free_src_wim;
2984 wimlib_register_progress_function(dest_wim,
2985 imagex_progress_func, NULL);
2987 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2988 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
2990 /* For --wimboot export, use small XPRESS chunks. */
2991 wimlib_set_output_chunk_size(dest_wim, 4096);
2992 } else if (compression_type == src_info.compression_type &&
2993 chunk_size == UINT32_MAX)
2995 /* Use same chunk size if compression type is the same. */
2996 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3000 if (chunk_size != UINT32_MAX) {
3001 /* Set destination chunk size. */
3002 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3004 goto out_free_dest_wim;
3006 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3007 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3009 goto out_free_dest_wim;
3011 if (solid_chunk_size != UINT32_MAX) {
3012 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3014 goto out_free_dest_wim;
3017 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3018 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3020 goto out_free_dest_wim;
3022 if (refglobs.num_strings) {
3023 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3025 goto out_free_dest_wim;
3028 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3029 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3031 imagex_error(T("--boot specified for all-images export, but source WIM "
3032 "has no bootable image."));
3034 goto out_free_dest_wim;
3037 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3038 dest_desc, export_flags);
3040 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3041 do_resource_not_found_warning(src_wimfile,
3042 &src_info, &refglobs);
3043 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3044 do_metadata_not_found_warning(src_wimfile, &src_info);
3046 goto out_free_dest_wim;
3050 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3051 else if (dest_wimfile)
3052 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3053 write_flags, num_threads);
3055 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3056 WIMLIB_ALL_IMAGES, write_flags,
3059 wimlib_free(dest_wim);
3061 wimlib_free(src_wim);
3063 string_set_destroy(&refglobs);
3067 usage(CMD_EXPORT, stderr);
3070 goto out_free_refglobs;
3073 /* Extract files or directories from a WIM image */
3075 imagex_extract(int argc, tchar **argv, int cmd)
3082 const tchar *wimfile;
3083 const tchar *image_num_or_name;
3084 tchar *dest_dir = T(".");
3085 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3086 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3087 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3088 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3090 STRING_SET(refglobs);
3092 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3094 for_opt(c, extract_options) {
3096 case IMAGEX_CHECK_OPTION:
3097 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3099 case IMAGEX_VERBOSE_OPTION:
3100 /* No longer does anything. */
3102 case IMAGEX_REF_OPTION:
3103 ret = string_set_append(&refglobs, optarg);
3105 goto out_free_refglobs;
3107 case IMAGEX_UNIX_DATA_OPTION:
3108 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3110 case IMAGEX_NO_ACLS_OPTION:
3111 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3113 case IMAGEX_STRICT_ACLS_OPTION:
3114 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3116 case IMAGEX_NO_ATTRIBUTES_OPTION:
3117 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3119 case IMAGEX_DEST_DIR_OPTION:
3122 case IMAGEX_TO_STDOUT_OPTION:
3123 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3124 imagex_info_file = stderr;
3125 imagex_be_quiet = true;
3126 set_fd_to_binary_mode(STDOUT_FILENO);
3128 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3129 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3130 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3132 case IMAGEX_NO_GLOBS_OPTION:
3133 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3135 case IMAGEX_NULLGLOB_OPTION:
3136 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3138 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3139 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3141 case IMAGEX_WIMBOOT_OPTION:
3142 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3144 case IMAGEX_COMPACT_OPTION:
3145 ret = set_compact_mode(optarg, &extract_flags);
3147 goto out_free_refglobs;
3159 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3160 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3162 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3167 image_num_or_name = argv[1];
3172 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3173 imagex_progress_func, NULL);
3175 goto out_free_refglobs;
3177 image = wimlib_resolve_image(wim, image_num_or_name);
3178 ret = verify_image_exists_and_is_single(image,
3182 goto out_wimlib_free;
3184 if (refglobs.num_strings) {
3185 ret = wim_reference_globs(wim, &refglobs, open_flags);
3187 goto out_wimlib_free;
3193 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3196 while (argc != 0 && ret == 0) {
3200 num_paths < argc && argv[num_paths][0] != T('@');
3205 ret = wimlib_extract_paths(wim, image, dest_dir,
3206 (const tchar **)argv,
3208 extract_flags | notlist_extract_flags);
3212 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3221 if (!imagex_be_quiet)
3222 imagex_printf(T("Done extracting files.\n"));
3223 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3224 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3225 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3226 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3227 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3230 T("Note: You can use the '--nullglob' "
3231 "option to ignore missing files.\n"));
3233 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3234 "files and directories\n"
3235 " are in the WIM image.\n"),
3236 get_cmd_string(CMD_DIR, false));
3237 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3238 struct wimlib_wim_info info;
3240 wimlib_get_wim_info(wim, &info);
3241 do_resource_not_found_warning(wimfile, &info, &refglobs);
3242 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3243 struct wimlib_wim_info info;
3245 wimlib_get_wim_info(wim, &info);
3246 do_metadata_not_found_warning(wimfile, &info);
3251 string_set_destroy(&refglobs);
3255 usage(CMD_EXTRACT, stderr);
3258 goto out_free_refglobs;
3261 /* Prints information about a WIM file; also can mark an image as bootable,
3262 * change the name of an image, or change the description of an image. */
3264 imagex_info(int argc, tchar **argv, int cmd)
3269 bool nocheck = false;
3270 bool header = false;
3273 bool short_header = true;
3274 const tchar *xml_out_file = NULL;
3275 const tchar *wimfile;
3276 const tchar *image_num_or_name;
3277 const tchar *new_name;
3278 const tchar *new_desc;
3283 struct wimlib_wim_info info;
3285 for_opt(c, info_options) {
3287 case IMAGEX_BOOT_OPTION:
3290 case IMAGEX_CHECK_OPTION:
3293 case IMAGEX_NOCHECK_OPTION:
3296 case IMAGEX_HEADER_OPTION:
3298 short_header = false;
3300 case IMAGEX_BLOBS_OPTION:
3302 short_header = false;
3304 case IMAGEX_XML_OPTION:
3306 short_header = false;
3308 case IMAGEX_EXTRACT_XML_OPTION:
3309 xml_out_file = optarg;
3310 short_header = false;
3312 case IMAGEX_METADATA_OPTION:
3313 imagex_error(T("The --metadata option has been removed. "
3314 "Use 'wimdir --detail' instead."));
3323 if (argc < 1 || argc > 4)
3327 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3328 new_name = (argc >= 3) ? argv[2] : NULL;
3329 new_desc = (argc >= 4) ? argv[3] : NULL;
3331 if (check && nocheck) {
3332 imagex_error(T("Can't specify both --check and --nocheck"));
3337 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3339 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3340 imagex_progress_func, NULL);
3344 wimlib_get_wim_info(wim, &info);
3346 image = wimlib_resolve_image(wim, image_num_or_name);
3347 ret = WIMLIB_ERR_INVALID_IMAGE;
3348 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3349 verify_image_exists(image, image_num_or_name, wimfile);
3351 imagex_error(T("If you would like to set the boot "
3352 "index to 0, specify image \"0\" with "
3353 "the --boot flag."));
3355 goto out_wimlib_free;
3358 if (boot && info.image_count == 0) {
3359 imagex_error(T("--boot is meaningless on a WIM with no images"));
3360 goto out_wimlib_free;
3363 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3365 imagex_error(T("Cannot specify the --boot flag "
3366 "without specifying a specific "
3367 "image in a multi-image WIM"));
3368 goto out_wimlib_free;
3371 imagex_error(T("Cannot specify the NEW_NAME "
3372 "without specifying a specific "
3373 "image in a multi-image WIM"));
3374 goto out_wimlib_free;
3378 /* Operations that print information are separated from operations that
3379 * recreate the WIM file. */
3380 if (!new_name && !boot) {
3382 /* Read-only operations */
3384 if (image == WIMLIB_NO_IMAGE) {
3385 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3386 image_num_or_name, wimfile);
3387 goto out_wimlib_free;
3390 if (image == WIMLIB_ALL_IMAGES && short_header)
3391 print_wim_information(wimfile, &info);
3394 wimlib_print_header(wim);
3397 if (info.total_parts != 1) {
3398 tfprintf(stderr, T("Warning: Only showing the blobs "
3399 "for part %d of a %d-part WIM.\n"),
3400 info.part_number, info.total_parts);
3406 ret = wimlib_extract_xml_data(wim, stdout);
3408 goto out_wimlib_free;
3414 fp = tfopen(xml_out_file, T("wb"));
3416 imagex_error_with_errno(T("Failed to open the "
3417 "file \"%"TS"\" for "
3421 goto out_wimlib_free;
3423 ret = wimlib_extract_xml_data(wim, fp);
3425 imagex_error(T("Failed to close the file "
3431 goto out_wimlib_free;
3435 wimlib_print_available_images(wim, image);
3440 /* Modification operations */
3442 if (image == WIMLIB_ALL_IMAGES)
3445 if (image == WIMLIB_NO_IMAGE && new_name) {
3446 imagex_error(T("Cannot specify new_name (\"%"TS"\") "
3447 "when using image 0"), new_name);
3449 goto out_wimlib_free;
3453 if (image == info.boot_index) {
3454 imagex_printf(T("Image %d is already marked as "
3455 "bootable.\n"), image);
3458 imagex_printf(T("Marking image %d as bootable.\n"),
3460 info.boot_index = image;
3461 ret = wimlib_set_wim_info(wim, &info,
3462 WIMLIB_CHANGE_BOOT_INDEX);
3464 goto out_wimlib_free;
3468 if (!tstrcmp(wimlib_get_image_name(wim, image), new_name))
3470 imagex_printf(T("Image %d is already named \"%"TS"\".\n"),
3474 imagex_printf(T("Changing the name of image %d to "
3475 "\"%"TS"\".\n"), image, new_name);
3476 ret = wimlib_set_image_name(wim, image, new_name);
3478 goto out_wimlib_free;
3482 const tchar *old_desc;
3483 old_desc = wimlib_get_image_description(wim, image);
3484 if (old_desc && !tstrcmp(old_desc, new_desc)) {
3485 imagex_printf(T("The description of image %d is already "
3486 "\"%"TS"\".\n"), image, new_desc);
3489 imagex_printf(T("Changing the description of image %d "
3490 "to \"%"TS"\".\n"), image, new_desc);
3491 ret = wimlib_set_image_descripton(wim, image,
3494 goto out_wimlib_free;
3498 /* Only call wimlib_overwrite() if something actually needs to
3500 if (boot || new_name || new_desc ||
3501 (check && !info.has_integrity_table) ||
3502 (nocheck && info.has_integrity_table))
3504 int write_flags = 0;
3507 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3509 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3510 ret = wimlib_overwrite(wim, write_flags, 1);
3512 imagex_printf(T("The file \"%"TS"\" was not modified "
3513 "because nothing needed to be done.\n"),
3524 usage(CMD_INFO, stderr);
3530 /* Join split WIMs into one part WIM */
3532 imagex_join(int argc, tchar **argv, int cmd)
3535 int swm_open_flags = 0;
3536 int wim_write_flags = 0;
3537 const tchar *output_path;
3540 for_opt(c, join_options) {
3542 case IMAGEX_CHECK_OPTION:
3543 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3544 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3554 imagex_error(T("Must specify one or more split WIM (.swm) "
3558 output_path = argv[0];
3559 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3564 imagex_progress_func,
3570 usage(CMD_JOIN, stderr);
3575 #if WIM_MOUNTING_SUPPORTED
3577 /* Mounts a WIM image. */
3579 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3582 int mount_flags = 0;
3584 const tchar *staging_dir = NULL;
3585 const tchar *wimfile;
3588 struct wimlib_wim_info info;
3592 STRING_SET(refglobs);
3594 if (cmd == CMD_MOUNTRW) {
3595 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3596 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3599 for_opt(c, mount_options) {
3601 case IMAGEX_ALLOW_OTHER_OPTION:
3602 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3604 case IMAGEX_CHECK_OPTION:
3605 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3607 case IMAGEX_DEBUG_OPTION:
3608 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3610 case IMAGEX_STREAMS_INTERFACE_OPTION:
3611 if (!tstrcasecmp(optarg, T("none")))
3612 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3613 else if (!tstrcasecmp(optarg, T("xattr")))
3614 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3615 else if (!tstrcasecmp(optarg, T("windows")))
3616 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3618 imagex_error(T("Unknown stream interface \"%"TS"\""),
3623 case IMAGEX_REF_OPTION:
3624 ret = string_set_append(&refglobs, optarg);
3626 goto out_free_refglobs;
3628 case IMAGEX_STAGING_DIR_OPTION:
3629 staging_dir = optarg;
3631 case IMAGEX_UNIX_DATA_OPTION:
3632 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3640 if (argc != 2 && argc != 3)
3645 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3646 imagex_progress_func, NULL);
3648 goto out_free_refglobs;
3650 wimlib_get_wim_info(wim, &info);
3653 /* Image explicitly specified. */
3654 image = wimlib_resolve_image(wim, argv[1]);
3656 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3660 /* No image specified; default to image 1, but only if the WIM
3661 * contains exactly one image. */
3663 if (info.image_count != 1) {
3664 imagex_error(T("\"%"TS"\" contains %d images; Please "
3665 "select one."), wimfile, info.image_count);
3673 if (refglobs.num_strings) {
3674 ret = wim_reference_globs(wim, &refglobs, open_flags);
3679 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3681 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3682 do_metadata_not_found_warning(wimfile, &info);
3684 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3686 image, wimfile, dir);
3692 string_set_destroy(&refglobs);
3698 goto out_free_refglobs;
3700 #endif /* WIM_MOUNTING_SUPPORTED */
3702 /* Rebuild a WIM file */
3704 imagex_optimize(int argc, tchar **argv, int cmd)
3707 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3708 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3709 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3710 uint32_t chunk_size = UINT32_MAX;
3711 uint32_t solid_chunk_size = UINT32_MAX;
3712 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3715 const tchar *wimfile;
3718 unsigned num_threads = 0;
3720 for_opt(c, optimize_options) {
3722 case IMAGEX_CHECK_OPTION:
3723 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3724 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3726 case IMAGEX_NOCHECK_OPTION:
3727 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3729 case IMAGEX_COMPRESS_OPTION:
3730 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3731 compression_type = get_compression_type(optarg);
3732 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3735 case IMAGEX_COMPRESS_SLOW_OPTION:
3736 set_compress_slow();
3737 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3739 case IMAGEX_RECOMPRESS_OPTION:
3740 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3742 case IMAGEX_CHUNK_SIZE_OPTION:
3743 chunk_size = parse_chunk_size(optarg);
3744 if (chunk_size == UINT32_MAX)
3747 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3748 solid_chunk_size = parse_chunk_size(optarg);
3749 if (solid_chunk_size == UINT32_MAX)
3752 case IMAGEX_SOLID_COMPRESS_OPTION:
3753 solid_ctype = get_compression_type(optarg);
3754 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3757 case IMAGEX_SOLID_OPTION:
3758 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3759 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3761 case IMAGEX_NO_SOLID_SORT_OPTION:
3762 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3764 case IMAGEX_THREADS_OPTION:
3765 num_threads = parse_num_threads(optarg);
3766 if (num_threads == UINT_MAX)
3769 case IMAGEX_PIPABLE_OPTION:
3770 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3772 case IMAGEX_NOT_PIPABLE_OPTION:
3773 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3775 case IMAGEX_UNSAFE_COMPACT_OPTION:
3776 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3790 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3791 imagex_progress_func, NULL);
3795 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3796 /* Change compression type. */
3797 ret = wimlib_set_output_compression_type(wim, compression_type);
3799 goto out_wimlib_free;
3802 if (chunk_size != UINT32_MAX) {
3803 /* Change chunk size. */
3804 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3806 goto out_wimlib_free;
3808 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3809 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3811 goto out_wimlib_free;
3813 if (solid_chunk_size != UINT32_MAX) {
3814 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3816 goto out_wimlib_free;
3819 old_size = file_get_size(wimfile);
3820 tprintf(T("\"%"TS"\" original size: "), wimfile);
3822 tputs(T("Unknown"));
3824 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3826 ret = wimlib_overwrite(wim, write_flags, num_threads);
3828 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3829 goto out_wimlib_free;
3832 new_size = file_get_size(wimfile);
3833 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3835 tputs(T("Unknown"));
3837 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3839 tfputs(T("Space saved: "), stdout);
3840 if (new_size != -1 && old_size != -1) {
3841 tprintf(T("%lld KiB\n"),
3842 ((long long)old_size - (long long)new_size) >> 10);
3844 tputs(T("Unknown"));
3853 usage(CMD_OPTIMIZE, stderr);
3859 /* Split a WIM into a spanned set */
3861 imagex_split(int argc, tchar **argv, int cmd)
3865 int write_flags = 0;
3866 unsigned long part_size;
3871 for_opt(c, split_options) {
3873 case IMAGEX_CHECK_OPTION:
3874 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3875 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3887 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3888 if (tmp == argv[2] || *tmp) {
3889 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3890 imagex_error(T("The part size must be an integer or "
3891 "floating-point number of megabytes."));
3894 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3895 imagex_progress_func, NULL);
3899 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3905 usage(CMD_SPLIT, stderr);
3911 #if WIM_MOUNTING_SUPPORTED
3912 /* Unmounts a mounted WIM image. */
3914 imagex_unmount(int argc, tchar **argv, int cmd)
3917 int unmount_flags = 0;
3920 for_opt(c, unmount_options) {
3922 case IMAGEX_COMMIT_OPTION:
3923 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3925 case IMAGEX_CHECK_OPTION:
3926 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3928 case IMAGEX_REBUILD_OPTION:
3929 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3931 case IMAGEX_LAZY_OPTION:
3932 case IMAGEX_FORCE_OPTION:
3933 /* Now, unmount is lazy by default. However, committing
3934 * the image will fail with
3935 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3936 * file descriptors on the WIM image. The
3937 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3938 * descriptors to be closed. */
3939 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3941 case IMAGEX_NEW_IMAGE_OPTION:
3942 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
3953 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
3954 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
3955 imagex_error(T("--new-image is meaningless "
3956 "without --commit also specified!"));
3961 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
3962 imagex_progress_func, NULL);
3964 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
3965 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
3967 "\tNote: Use --commit --force to force changes "
3968 "to be committed, regardless\n"
3969 "\t of open files.\n"));
3976 usage(CMD_UNMOUNT, stderr);
3981 #endif /* WIM_MOUNTING_SUPPORTED */
3984 * Add, delete, or rename files in a WIM image.
3987 imagex_update(int argc, tchar **argv, int cmd)
3989 const tchar *wimfile;
3993 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3994 int write_flags = 0;
3995 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
3996 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
3997 WIMLIB_ADD_FLAG_VERBOSE |
3998 WIMLIB_ADD_FLAG_WINCONFIG;
3999 int default_delete_flags = 0;
4000 unsigned num_threads = 0;
4002 tchar *cmd_file_contents;
4003 size_t cmd_file_nchars;
4004 struct wimlib_update_command *cmds;
4006 tchar *command_str = NULL;
4007 tchar *config_file = NULL;
4008 tchar *wimboot_config = NULL;
4010 for_opt(c, update_options) {
4012 /* Generic or write options */
4013 case IMAGEX_THREADS_OPTION:
4014 num_threads = parse_num_threads(optarg);
4015 if (num_threads == UINT_MAX)
4018 case IMAGEX_CHECK_OPTION:
4019 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4020 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4022 case IMAGEX_REBUILD_OPTION:
4023 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4025 case IMAGEX_COMMAND_OPTION:
4027 imagex_error(T("--command may only be specified "
4028 "one time. Please provide\n"
4029 " the update commands "
4030 "on standard input instead."));
4033 command_str = tstrdup(optarg);
4035 imagex_error(T("Out of memory!"));
4039 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4040 wimboot_config = optarg;
4042 /* Default delete options */
4043 case IMAGEX_FORCE_OPTION:
4044 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4046 case IMAGEX_RECURSIVE_OPTION:
4047 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4050 /* Global add option */
4051 case IMAGEX_CONFIG_OPTION:
4052 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4053 config_file = optarg;
4056 /* Default add options */
4057 case IMAGEX_VERBOSE_OPTION:
4058 /* No longer does anything. */
4060 case IMAGEX_DEREFERENCE_OPTION:
4061 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4063 case IMAGEX_UNIX_DATA_OPTION:
4064 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4066 case IMAGEX_NO_ACLS_OPTION:
4067 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4069 case IMAGEX_STRICT_ACLS_OPTION:
4070 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4072 case IMAGEX_NO_REPLACE_OPTION:
4073 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4075 case IMAGEX_UNSAFE_COMPACT_OPTION:
4076 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4085 if (argc != 1 && argc != 2)
4089 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4090 imagex_progress_func, NULL);
4092 goto out_free_command_str;
4095 /* Image explicitly specified. */
4096 image = wimlib_resolve_image(wim, argv[1]);
4097 ret = verify_image_exists_and_is_single(image, argv[1],
4100 goto out_wimlib_free;
4102 /* No image specified; default to image 1, but only if the WIM
4103 * contains exactly one image. */
4104 struct wimlib_wim_info info;
4106 wimlib_get_wim_info(wim, &info);
4107 if (info.image_count != 1) {
4108 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4109 wimfile, info.image_count);
4116 /* Read update commands from standard input, or the command string if
4119 cmd_file_contents = NULL;
4120 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4124 goto out_free_cmd_file_contents;
4126 } else if (!wimboot_config) {
4127 if (isatty(STDIN_FILENO)) {
4128 tputs(T("Reading update commands from standard input..."));
4129 recommend_man_page(CMD_UPDATE, stdout);
4131 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4132 if (!cmd_file_contents) {
4134 goto out_wimlib_free;
4137 /* Parse the update commands */
4138 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4142 goto out_free_cmd_file_contents;
4145 cmd_file_contents = NULL;
4150 /* Set default flags and capture config on the update commands */
4151 for (size_t i = 0; i < num_cmds; i++) {
4152 switch (cmds[i].op) {
4153 case WIMLIB_UPDATE_OP_ADD:
4154 cmds[i].add.add_flags |= default_add_flags;
4155 cmds[i].add.config_file = config_file;
4157 case WIMLIB_UPDATE_OP_DELETE:
4158 cmds[i].delete_.delete_flags |= default_delete_flags;
4165 /* Execute the update commands */
4166 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4170 if (wimboot_config) {
4171 /* --wimboot-config=FILE is short for an
4172 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4174 struct wimlib_update_command cmd;
4176 cmd.op = WIMLIB_UPDATE_OP_ADD;
4177 cmd.add.fs_source_path = wimboot_config;
4178 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4179 cmd.add.config_file = NULL;
4180 cmd.add.add_flags = 0;
4182 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4187 /* Overwrite the updated WIM */
4188 ret = wimlib_overwrite(wim, write_flags, num_threads);
4191 out_free_cmd_file_contents:
4192 free(cmd_file_contents);
4195 out_free_command_str:
4200 usage(CMD_UPDATE, stderr);
4203 goto out_free_command_str;
4206 /* Verify a WIM file. */
4208 imagex_verify(int argc, tchar **argv, int cmd)
4211 const tchar *wimfile;
4213 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4214 int verify_flags = 0;
4215 STRING_SET(refglobs);
4218 for_opt(c, verify_options) {
4220 case IMAGEX_REF_OPTION:
4221 ret = string_set_append(&refglobs, optarg);
4223 goto out_free_refglobs;
4225 case IMAGEX_NOCHECK_OPTION:
4226 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4238 imagex_error(T("Must specify a WIM file!"));
4240 imagex_error(T("At most one WIM file can be specified!"));
4246 ret = wimlib_open_wim_with_progress(wimfile,
4249 imagex_progress_func,
4252 goto out_free_refglobs;
4254 ret = wim_reference_globs(wim, &refglobs, open_flags);
4256 goto out_wimlib_free;
4258 ret = wimlib_verify_wim(wim, verify_flags);
4260 tputc(T('\n'), stderr);
4261 imagex_error(T("\"%"TS"\" failed verification!"),
4263 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4264 refglobs.num_strings == 0)
4266 imagex_printf(T("Note: if this WIM file is not standalone, "
4267 "use the --ref option to specify the other parts.\n"));
4270 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4277 string_set_destroy(&refglobs);
4281 usage(CMD_VERIFY, stderr);
4283 goto out_free_refglobs;
4286 struct imagex_command {
4288 int (*func)(int argc, tchar **argv, int cmd);
4291 static const struct imagex_command imagex_commands[] = {
4292 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4293 [CMD_APPLY] = {T("apply"), imagex_apply},
4294 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4295 [CMD_DELETE] = {T("delete"), imagex_delete},
4296 [CMD_DIR ] = {T("dir"), imagex_dir},
4297 [CMD_EXPORT] = {T("export"), imagex_export},
4298 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4299 [CMD_INFO] = {T("info"), imagex_info},
4300 [CMD_JOIN] = {T("join"), imagex_join},
4301 #if WIM_MOUNTING_SUPPORTED
4302 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4303 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4305 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4306 [CMD_SPLIT] = {T("split"), imagex_split},
4307 #if WIM_MOUNTING_SUPPORTED
4308 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4310 [CMD_UPDATE] = {T("update"), imagex_update},
4311 [CMD_VERIFY] = {T("verify"), imagex_verify},
4316 /* Can be a directory or source list file. But source list file is probably
4317 * a rare use case, so just say directory. */
4318 # define SOURCE_STR T("DIRECTORY")
4320 /* Can only be a directory */
4321 # define TARGET_STR T("DIRECTORY")
4324 /* Can be a directory, NTFS volume, or source list file. */
4325 # define SOURCE_STR T("SOURCE")
4327 /* Can be a directory or NTFS volume. */
4328 # define TARGET_STR T("TARGET")
4332 static const tchar *usage_strings[] = {
4335 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4336 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4337 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4338 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4339 " [--wimboot] [--unix-data] [--dereference]\n"
4343 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4344 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4345 " [--no-attributes] [--rpfix] [--norpfix]\n"
4346 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4347 " [--compact=FORMAT]\n"
4351 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4352 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4353 " [--config=FILE] [--threads=NUM_THREADS]\n"
4354 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4355 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4356 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4360 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4364 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4368 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4369 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4370 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4371 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4372 " [--wimboot] [--solid]\n"
4376 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4377 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4378 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4379 " [--no-attributes] [--include-invalid-names]\n"
4380 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4384 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4385 " [--boot] [--check] [--nocheck] [--xml]\n"
4386 " [--extract-xml FILE] [--header] [--blobs]\n"
4390 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4392 #if WIM_MOUNTING_SUPPORTED
4395 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4396 " [--check] [--streams-interface=INTERFACE]\n"
4397 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4401 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4402 " [--check] [--streams-interface=INTERFACE]\n"
4403 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4409 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4410 " [--check] [--nocheck] [--solid]\n"
4415 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4417 #if WIM_MOUNTING_SUPPORTED
4420 " %"TS" DIRECTORY\n"
4421 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4426 " %"TS" WIMFILE [IMAGE]\n"
4427 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4428 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4429 " [--command=STRING] [--wimboot-config=FILE]\n"
4434 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4438 static const tchar *invocation_name;
4439 static int invocation_cmd = CMD_NONE;
4441 static const tchar *get_cmd_string(int cmd, bool nospace)
4443 static tchar buf[50];
4444 if (cmd == CMD_NONE) {
4445 return T("wimlib-imagex");
4446 } else if (invocation_cmd != CMD_NONE) {
4447 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4449 const tchar *format;
4452 format = T("%"TS"-%"TS"");
4454 format = T("%"TS" %"TS"");
4455 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4463 static const tchar *s =
4465 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4466 "Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers\n"
4467 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4468 "This is free software: you are free to change and redistribute it.\n"
4469 "There is NO WARRANTY, to the extent permitted by law.\n"
4471 "Report bugs to "PACKAGE_BUGREPORT".\n"
4478 help_or_version(int argc, tchar **argv, int cmd)
4483 for (i = 1; i < argc; i++) {
4485 if (p[0] == T('-') && p[1] == T('-')) {
4487 if (!tstrcmp(p, T("help"))) {
4488 if (cmd == CMD_NONE)
4493 } else if (!tstrcmp(p, T("version"))) {
4502 print_usage_string(int cmd, FILE *fp)
4504 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4508 recommend_man_page(int cmd, FILE *fp)
4510 const tchar *format_str;
4512 format_str = T("Some uncommon options are not listed;\n"
4513 "See %"TS".pdf in the doc directory for more details.\n");
4515 format_str = T("Some uncommon options are not listed;\n"
4516 "Try `man %"TS"' for more details.\n");
4518 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4522 usage(int cmd, FILE *fp)
4524 tfprintf(fp, T("Usage:\n"));
4525 print_usage_string(cmd, fp);
4526 tfprintf(fp, T("\n"));
4527 recommend_man_page(cmd, fp);
4533 tfprintf(fp, T("Usage:\n"));
4534 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4535 print_usage_string(cmd, fp);
4536 tfprintf(fp, T("\n"));
4538 static const tchar *extra =
4541 " %"TS" --version\n"
4544 tfprintf(fp, extra, invocation_name, invocation_name);
4546 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4547 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4548 "For some commands IMAGE may be \"all\".\n"
4550 recommend_man_page(CMD_NONE, fp);
4553 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4554 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4555 * something else), while on Windows the command arguments will be UTF-16LE
4556 * encoded 'wchar_t' strings. */
4559 wmain(int argc, wchar_t **argv, wchar_t **envp)
4561 main(int argc, char **argv)
4568 imagex_info_file = stdout;
4569 invocation_name = tbasename(argv[0]);
4572 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4573 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4577 setlocale(LC_ALL, "");
4578 codeset = nl_langinfo(CODESET);
4579 if (!strstr(codeset, "UTF-8") &&
4580 !strstr(codeset, "UTF8") &&
4581 !strstr(codeset, "utf-8") &&
4582 !strstr(codeset, "utf8"))
4585 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4586 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4587 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4588 " to any value to force wimlib to use UTF-8.\n",
4594 #endif /* !__WIN32__ */
4597 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4598 if (igcase != NULL) {
4599 if (!tstrcmp(igcase, T("no")) ||
4600 !tstrcmp(igcase, T("0")))
4601 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4602 else if (!tstrcmp(igcase, T("yes")) ||
4603 !tstrcmp(igcase, T("1")))
4604 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4607 "WARNING: Ignoring unknown setting of "
4608 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4613 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4615 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4616 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4617 for (int i = 0; i < CMD_MAX; i++) {
4618 if (!tstrcmp(invocation_name + 3,
4619 imagex_commands[i].name))
4628 /* Unless already known from the invocation name, determine which
4629 * command was specified. */
4630 if (cmd == CMD_NONE) {
4632 imagex_error(T("No command specified!\n"));
4636 for (int i = 0; i < CMD_MAX; i++) {
4637 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4642 if (cmd != CMD_NONE) {
4648 /* Handle --help and --version. --help can be either for the program as
4649 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4650 * CMD_NONE). Note: help_or_version() will not return if a --help or
4651 * --version argument was found. */
4652 help_or_version(argc, argv, cmd);
4654 /* Bail if a valid command was not specified. */
4655 if (cmd == CMD_NONE) {
4656 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4661 /* Enable warning and error messages in wimlib to be more user-friendly.
4663 wimlib_set_print_errors(true);
4665 /* Initialize wimlib. */
4666 ret = wimlib_global_init(init_flags);
4668 goto out_check_status;
4670 /* Call the command handler function. */
4671 ret = imagex_commands[cmd].func(argc, argv, cmd);
4673 /* Check for error writing to standard output, especially since for some
4674 * commands, writing to standard output is part of the program's actual
4675 * behavior and not just for informational purposes. */
4676 if (ferror(stdout) || fclose(stdout)) {
4677 imagex_error_with_errno(T("error writing to standard output"));
4682 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4683 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4684 * error code from which an error message can be printed. */
4686 imagex_error(T("Exiting with error code %d:\n"
4688 wimlib_get_error_string(ret));
4689 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4690 imagex_error_with_errno(T("errno"));
4692 /* Make wimlib free any resources it's holding (although this is not
4693 * strictly necessary because the process is ending anyway). */
4694 wimlib_global_cleanup();