4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012-2018 Eric Biggers
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # include "config.h" /* Need for PACKAGE_VERSION, etc. */
30 #include "wimlib_tchar.h"
49 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
52 # include "imagex-win32.h"
53 # define print_security_descriptor win32_print_security_descriptor
56 # include <langinfo.h>
57 # define print_security_descriptor default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
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 only_short_form);
136 static FILE *imagex_info_file;
138 #define imagex_printf(format, ...) \
139 if (imagex_info_file) \
140 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
142 static void imagex_suppress_output(void)
144 imagex_info_file = NULL;
147 static void imagex_output_to_stderr(void)
149 if (imagex_info_file)
150 imagex_info_file = stderr;
153 static void imagex_flush_output(void)
155 if (imagex_info_file)
156 fflush(imagex_info_file);
160 IMAGEX_ALLOW_OTHER_OPTION,
164 IMAGEX_CHUNK_SIZE_OPTION,
165 IMAGEX_COMMAND_OPTION,
166 IMAGEX_COMMIT_OPTION,
167 IMAGEX_COMPACT_OPTION,
168 IMAGEX_COMPRESS_OPTION,
169 IMAGEX_CONFIG_OPTION,
171 IMAGEX_DELTA_FROM_OPTION,
172 IMAGEX_DEREFERENCE_OPTION,
173 IMAGEX_DEST_DIR_OPTION,
174 IMAGEX_DETAILED_OPTION,
175 IMAGEX_EXTRACT_XML_OPTION,
178 IMAGEX_HEADER_OPTION,
179 IMAGEX_IMAGE_PROPERTY_OPTION,
180 IMAGEX_INCLUDE_INTEGRITY_OPTION,
181 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
183 IMAGEX_METADATA_OPTION,
184 IMAGEX_NEW_IMAGE_OPTION,
185 IMAGEX_NOCHECK_OPTION,
186 IMAGEX_NORPFIX_OPTION,
187 IMAGEX_NOT_PIPABLE_OPTION,
188 IMAGEX_NO_ACLS_OPTION,
189 IMAGEX_NO_ATTRIBUTES_OPTION,
190 IMAGEX_NO_GLOBS_OPTION,
191 IMAGEX_NO_REPLACE_OPTION,
192 IMAGEX_NO_SOLID_SORT_OPTION,
193 IMAGEX_NULLGLOB_OPTION,
194 IMAGEX_ONE_FILE_ONLY_OPTION,
196 IMAGEX_PIPABLE_OPTION,
197 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
198 IMAGEX_REBUILD_OPTION,
199 IMAGEX_RECOMPRESS_OPTION,
200 IMAGEX_RECURSIVE_OPTION,
203 IMAGEX_SNAPSHOT_OPTION,
205 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
206 IMAGEX_SOLID_COMPRESS_OPTION,
208 IMAGEX_SOURCE_LIST_OPTION,
209 IMAGEX_STAGING_DIR_OPTION,
210 IMAGEX_STREAMS_INTERFACE_OPTION,
211 IMAGEX_STRICT_ACLS_OPTION,
212 IMAGEX_THREADS_OPTION,
213 IMAGEX_TO_STDOUT_OPTION,
214 IMAGEX_UNIX_DATA_OPTION,
215 IMAGEX_UNSAFE_COMPACT_OPTION,
216 IMAGEX_UPDATE_OF_OPTION,
217 IMAGEX_VERBOSE_OPTION,
218 IMAGEX_WIMBOOT_CONFIG_OPTION,
219 IMAGEX_WIMBOOT_OPTION,
223 static const struct option apply_options[] = {
224 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
225 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
226 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
227 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
228 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
229 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
230 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
231 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
232 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
233 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
234 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
235 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
236 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
240 static const struct option capture_or_append_options[] = {
241 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
242 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
243 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
244 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
245 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
246 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
247 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
248 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
249 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
250 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
251 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
252 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
253 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
254 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
255 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
256 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
257 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
258 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
259 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
260 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
261 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
262 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
263 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
264 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
265 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
266 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
267 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
268 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
269 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
270 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
271 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
272 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
276 static const struct option delete_options[] = {
277 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
278 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
279 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
280 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
284 static const struct option dir_options[] = {
285 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
286 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
287 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
288 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
292 static const struct option export_options[] = {
293 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
294 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
295 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
296 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
297 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
298 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
299 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
300 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
301 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
302 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
303 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
304 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
305 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
306 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
307 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
308 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
309 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
310 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
311 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
315 static const struct option extract_options[] = {
316 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
317 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
318 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
319 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
320 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
321 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
322 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
323 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
324 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
325 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
326 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
327 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
328 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
329 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
330 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
331 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
332 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
336 static const struct option info_options[] = {
337 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
338 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
339 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
340 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
341 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
342 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
343 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
344 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
345 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
346 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
347 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
351 static const struct option join_options[] = {
352 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
353 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
357 #if WIM_MOUNTING_SUPPORTED
358 static const struct option mount_options[] = {
359 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
360 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
361 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
362 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
363 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
364 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
365 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
370 static const struct option optimize_options[] = {
371 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
372 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
373 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
374 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
375 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
376 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
377 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
378 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
379 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
380 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
381 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
382 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
383 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
384 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
385 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
389 static const struct option split_options[] = {
390 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
391 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
395 #if WIM_MOUNTING_SUPPORTED
396 static const struct option unmount_options[] = {
397 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
398 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
399 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
400 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
401 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
402 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
407 static const struct option update_options[] = {
408 /* Careful: some of the options here set the defaults for update
409 * commands, but the flags given to an actual update command (and not to
410 * `imagex update' itself are also handled in
411 * update_command_add_option(). */
412 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
413 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
414 {T("include-integrity"), no_argument, NULL, IMAGEX_INCLUDE_INTEGRITY_OPTION},
415 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
416 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
417 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
419 /* Default delete options */
420 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
421 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
423 /* Global add option */
424 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
426 /* Default add options */
427 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
428 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
429 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
430 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
431 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
432 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
433 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
434 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
439 static const struct option verify_options[] = {
440 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
441 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
447 # define _format_attribute(type, format_str, args_start) \
448 __attribute__((format(type, format_str, args_start)))
450 # define _format_attribute(type, format_str, args_start)
453 /* Print formatted error message to stderr. */
454 static void _format_attribute(printf, 1, 2)
455 imagex_error(const tchar *format, ...)
458 va_start(va, format);
459 tfputs(T("ERROR: "), stderr);
460 tvfprintf(stderr, format, va);
461 tputc(T('\n'), stderr);
465 /* Print formatted error message to stderr. */
466 static void _format_attribute(printf, 1, 2)
467 imagex_error_with_errno(const tchar *format, ...)
469 int errno_save = errno;
471 va_start(va, format);
472 tfputs(T("ERROR: "), stderr);
473 tvfprintf(stderr, format, va);
474 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
479 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
481 if (image == WIMLIB_NO_IMAGE) {
482 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
483 " Please specify a 1-based image index or "
484 "image name. To list the images\n"
485 " contained in the WIM archive, run\n"
487 " %"TS" \"%"TS"\"\n"),
488 image_name, wim_name,
489 get_cmd_string(CMD_INFO, false), wim_name);
490 return WIMLIB_ERR_INVALID_IMAGE;
496 verify_image_is_single(int image)
498 if (image == WIMLIB_ALL_IMAGES) {
499 imagex_error(T("Cannot specify all images for this action!"));
500 return WIMLIB_ERR_INVALID_IMAGE;
506 verify_image_exists_and_is_single(int image, const tchar *image_name,
507 const tchar *wim_name)
510 ret = verify_image_exists(image, image_name, wim_name);
512 ret = verify_image_is_single(image);
517 print_available_compression_types(FILE *fp)
519 static const tchar * const s =
521 "Available compression types:\n"
524 " xpress (alias: \"fast\")\n"
525 " lzx (alias: \"maximum\") (default for capture)\n"
526 " lzms (alias: \"recovery\")\n"
528 " zstd (alias: \"zstandard\")\n"
535 /* Parse the argument to --compress or --solid-compress */
537 get_compression_type(tchar *optarg, bool solid)
540 unsigned int compression_level = 0;
543 plevel = tstrchr(optarg, T(':'));
549 ultmp = tstrtoul(plevel, &ptmp, 10);
550 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
551 imagex_error(T("Compression level must be a positive integer! "
552 "e.g. --compress=lzx:80"));
553 return WIMLIB_COMPRESSION_TYPE_INVALID;
555 compression_level = ultmp;
558 if (!tstrcasecmp(optarg, T("maximum")) ||
559 !tstrcasecmp(optarg, T("lzx")) ||
560 !tstrcasecmp(optarg, T("max"))) {
561 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
562 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
563 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
564 } else if (!tstrcasecmp(optarg, T("recovery"))) {
568 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
569 " differently from DISM. Instead, you typically want to use '--solid' to\n"
570 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
571 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
572 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
573 " of '--compress=recovery'.\n"));
575 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
576 } else if (!tstrcasecmp(optarg, T("lzms"))) {
577 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
579 } else if (!tstrcasecmp(optarg, T("zstd")) ||
580 !tstrcasecmp(optarg, T("zstandard"))) {
581 ctype = WIMLIB_COMPRESSION_TYPE_ZSTD;
583 } else if (!tstrcasecmp(optarg, T("none"))) {
584 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
586 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
587 print_available_compression_types(stderr);
588 return WIMLIB_COMPRESSION_TYPE_INVALID;
591 if (compression_level != 0)
592 wimlib_set_default_compression_level(ctype, compression_level);
596 /* Parse the argument to --compact */
598 set_compact_mode(const tchar *arg, int *extract_flags)
601 if (!tstrcasecmp(arg, T("xpress4k")))
602 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
603 else if (!tstrcasecmp(arg, T("xpress8k")))
604 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
605 else if (!tstrcasecmp(arg, T("xpress16k")))
606 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
607 else if (!tstrcasecmp(arg, T("lzx")))
608 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
611 *extract_flags |= flag;
616 "\"%"TS"\" is not a recognized System Compression format. The options are:"
618 " --compact=xpress4k\n"
619 " --compact=xpress8k\n"
620 " --compact=xpress16k\n"
629 unsigned num_strings;
630 unsigned num_alloc_strings;
633 #define STRING_LIST_INITIALIZER \
634 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
636 #define STRING_LIST(_strings) \
637 struct string_list _strings = STRING_LIST_INITIALIZER
640 string_list_append(struct string_list *list, tchar *glob)
642 unsigned num_alloc_strings = list->num_alloc_strings;
644 if (list->num_strings == num_alloc_strings) {
647 num_alloc_strings += 4;
648 new_strings = realloc(list->strings,
649 sizeof(list->strings[0]) * num_alloc_strings);
651 imagex_error(T("Out of memory!"));
654 list->strings = new_strings;
655 list->num_alloc_strings = num_alloc_strings;
657 list->strings[list->num_strings++] = glob;
662 string_list_destroy(struct string_list *list)
668 wim_reference_globs(WIMStruct *wim, struct string_list *list, int open_flags)
670 return wimlib_reference_resource_files(wim, (const tchar **)list->strings,
672 WIMLIB_REF_FLAG_GLOB_ENABLE,
677 append_image_property_argument(struct string_list *image_properties)
679 if (!tstrchr(optarg, '=')) {
680 imagex_error(T("'--image-property' argument "
681 "must be in the form NAME=VALUE"));
684 return string_list_append(image_properties, optarg);
688 apply_image_properties(struct string_list *image_properties,
689 WIMStruct *wim, int image, bool *any_changes_ret)
691 bool any_changes = false;
692 for (unsigned i = 0; i < image_properties->num_strings; i++) {
694 const tchar *current_value;
697 name = image_properties->strings[i];
698 value = tstrchr(name, '=');
701 current_value = wimlib_get_image_property(wim, image, name);
702 if (current_value && !tstrcmp(current_value, value)) {
703 imagex_printf(T("The %"TS" property of image %d "
704 "already has value \"%"TS"\".\n"),
707 imagex_printf(T("Setting the %"TS" property of image "
708 "%d to \"%"TS"\".\n"),
710 ret = wimlib_set_image_property(wim, image, name, value);
717 *any_changes_ret = any_changes;
722 do_resource_not_found_warning(const tchar *wimfile,
723 const struct wimlib_wim_info *info,
724 const struct string_list *refglobs)
726 if (info->total_parts > 1) {
727 if (refglobs->num_strings == 0) {
728 imagex_error(T("\"%"TS"\" is part of a split WIM. "
729 "Use --ref to specify the other parts."),
732 imagex_error(T("Perhaps the '--ref' argument did not "
733 "specify all other parts of the split "
737 imagex_error(T("If this is a delta WIM, use the --ref argument "
738 "to specify the WIM(s) on which it is based."));
743 do_metadata_not_found_warning(const tchar *wimfile,
744 const struct wimlib_wim_info *info)
746 if (info->part_number != 1) {
747 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
748 " You must specify the first part."),
753 /* Returns the size of a file given its name, or -1 if the file does not exist
754 * or its size cannot be determined. */
756 file_get_size(const tchar *filename)
759 if (tstat(filename, &st) == 0)
766 PARSE_STRING_SUCCESS = 0,
767 PARSE_STRING_FAILURE = 1,
768 PARSE_STRING_NONE = 2,
772 * Parses a string token from an array of characters.
774 * Tokens are either whitespace-delimited, or double or single-quoted.
776 * @line_p: Pointer to the pointer to the line of data. Will be updated
777 * to point past the string token iff the return value is
778 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
781 * @len_p: @len_p initially stores the length of the line of data, which may
782 * be 0, and it will be updated to the number of bytes remaining in
783 * the line iff the return value is PARSE_STRING_SUCCESS.
785 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
786 * parsed string token will be returned here.
788 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
789 * PARSE_STRING_FAILURE if the data was invalid due to a missing
790 * closing quote; or PARSE_STRING_NONE if the line ended before the
791 * beginning of a string token was found.
794 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
797 tchar *line = *line_p;
801 /* Skip leading whitespace */
804 return PARSE_STRING_NONE;
805 if (!istspace(*line) && *line != T('\0'))
811 if (quote_char == T('"') || quote_char == T('\'')) {
816 line = tmemchr(line, quote_char, len);
818 imagex_error(T("Missing closing quote: %"TS), fn - 1);
819 return PARSE_STRING_FAILURE;
822 /* Unquoted string. Go until whitespace. Line is terminated
823 * by '\0', so no need to check 'len'. */
827 } while (!istspace(*line) && *line != T('\0'));
834 return PARSE_STRING_SUCCESS;
837 /* Parses a line of data (not an empty line or comment) in the source list file
838 * format. (See the man page for 'wimlib-imagex capture' for details on this
839 * format and the meaning.)
841 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
842 * len == 0. The data in @line will be modified by this function call.
844 * @len: Length of the line of data.
846 * @source: On success, the capture source and target described by the line is
847 * written into this destination. Note that it will contain pointers
848 * to data in the @line array.
850 * Returns true if the line was valid; false otherwise. */
852 parse_source_list_line(tchar *line, size_t len,
853 struct wimlib_capture_source *source)
857 ret = parse_string(&line, &len, &source->fs_source_path);
858 if (ret != PARSE_STRING_SUCCESS)
860 ret = parse_string(&line, &len, &source->wim_target_path);
861 if (ret == PARSE_STRING_NONE)
862 source->wim_target_path = source->fs_source_path;
863 return ret != PARSE_STRING_FAILURE;
866 /* Returns %true if the given line of length @len > 0 is a comment or empty line
867 * in the source list file format. */
869 is_comment_line(const tchar *line, size_t len)
872 if (*line == T('#') || *line == T(';'))
874 if (!istspace(*line) && *line != T('\0'))
884 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
887 tchar *contents = *contents_p;
888 size_t nchars = *nchars_p;
891 for (i = 0; i < nchars; i++)
892 if (contents[i] == T('\n'))
895 /* Handle last line not terminated by a newline */
896 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
897 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
899 imagex_error(T("Out of memory!"));
902 contents[nchars] = T('\n');
903 *contents_p = contents;
911 /* Parses a file in the source list format. (See the man page for
912 * 'wimlib-imagex capture' for details on this format and the meaning.)
914 * @source_list_contents: Contents of the source list file. Note that this
915 * buffer will be modified to save memory allocations,
916 * and cannot be freed until the returned array of
917 * wimlib_capture_source's has also been freed.
919 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
922 * @nsources_ret: On success, the length of the returned array is
925 * Returns: An array of `struct wimlib_capture_source's that can be passed to
926 * the wimlib_add_image_multisource() function to specify how a WIM image is to
928 static struct wimlib_capture_source *
929 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
930 size_t *nsources_ret)
934 struct wimlib_capture_source *sources;
937 nlines = text_file_count_lines(source_list_contents_p,
938 &source_list_nchars);
942 /* Always allocate at least 1 slot, just in case the implementation of
943 * calloc() returns NULL if 0 bytes are requested. */
944 sources = calloc(nlines ?: 1, sizeof(*sources));
946 imagex_error(T("out of memory"));
949 p = *source_list_contents_p;
951 for (i = 0; i < nlines; i++) {
952 /* XXX: Could use rawmemchr() here instead, but it may not be
953 * available on all platforms. */
954 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
955 size_t len = endp - p + 1;
957 if (!is_comment_line(p, len)) {
958 if (!parse_source_list_line(p, len, &sources[j++])) {
970 /* Reads the contents of a file into memory. */
972 file_get_contents(const tchar *filename, size_t *len_ret)
979 if (tstat(filename, &stbuf) != 0) {
980 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
985 fp = tfopen(filename, T("rb"));
987 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
991 buf = malloc(len ? len : 1);
993 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
994 "contents of file \"%"TS"\""), len, filename);
997 if (fread(buf, 1, len, fp) != len) {
998 imagex_error_with_errno(T("Failed to read %zu bytes from the "
999 "file \"%"TS"\""), len, filename);
1013 /* Read standard input until EOF and return the full contents in a malloc()ed
1014 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1017 stdin_get_contents(size_t *len_ret)
1019 /* stdin can, of course, be a pipe or other non-seekable file, so the
1020 * total length of the data cannot be pre-determined */
1022 size_t newlen = 1024;
1026 char *p = realloc(buf, newlen);
1027 size_t bytes_read, bytes_to_read;
1029 imagex_error(T("out of memory while reading stdin"));
1033 bytes_to_read = newlen - pos;
1034 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1036 if (bytes_read != bytes_to_read) {
1041 imagex_error_with_errno(T("error reading stdin"));
1055 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1058 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1060 *num_tchars_ret = num_bytes;
1062 #else /* !__WIN32__ */
1063 /* On Windows, translate the text to UTF-16LE */
1067 if (num_bytes >= 2 &&
1068 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1069 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1071 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1072 * with something that looks like an ASCII character encoded as
1073 * a UTF-16LE code unit. Assume the file is encoded as
1074 * UTF-16LE. This is not a 100% reliable check. */
1075 num_wchars = num_bytes / 2;
1076 text_wstr = (wchar_t*)text;
1078 /* File does not look like UTF-16LE. Assume it is encoded in
1079 * the current Windows code page. I think these are always
1080 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1081 * should work as expected. */
1082 text_wstr = win32_mbs_to_wcs(text,
1087 *num_tchars_ret = num_wchars;
1089 #endif /* __WIN32__ */
1093 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1098 contents = file_get_contents(filename, &num_bytes);
1101 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1105 stdin_get_text_contents(size_t *num_tchars_ret)
1110 contents = stdin_get_contents(&num_bytes);
1113 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1116 #define TO_PERCENT(numerator, denominator) \
1117 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1119 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1120 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1121 #define KIBIBYTE_MIN_NBYTES 10000ULL
1124 get_unit(uint64_t total_bytes, const tchar **name_ret)
1126 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1127 *name_ret = T("GiB");
1129 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1130 *name_ret = T("MiB");
1132 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1133 *name_ret = T("KiB");
1136 *name_ret = T("bytes");
1141 static struct wimlib_progress_info_scan last_scan_progress;
1144 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1146 uint64_t prev_count, cur_count;
1148 prev_count = last_scan_progress.num_nondirs_scanned +
1149 last_scan_progress.num_dirs_scanned;
1150 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1152 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1153 cur_count % 128 == 0)
1155 unsigned unit_shift;
1156 const tchar *unit_name;
1158 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1159 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1160 "%"PRIu64" directories) "),
1161 scan->num_bytes_scanned >> unit_shift,
1163 scan->num_nondirs_scanned,
1164 scan->num_dirs_scanned);
1165 last_scan_progress = *scan;
1168 /* Progress callback function passed to various wimlib functions. */
1169 static enum wimlib_progress_status
1170 imagex_progress_func(enum wimlib_progress_msg msg,
1171 union wimlib_progress_info *info,
1172 void *_ignored_context)
1174 unsigned percent_done;
1175 unsigned unit_shift;
1176 const tchar *unit_name;
1179 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1181 static bool started;
1183 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1184 imagex_printf(T("Using %"TS" compression "
1185 "with %u thread%"TS"\n"),
1186 wimlib_get_compression_type_string(
1187 info->write_streams.compression_type),
1188 info->write_streams.num_threads,
1189 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1194 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1195 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1196 info->write_streams.total_bytes);
1198 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1199 info->write_streams.completed_bytes >> unit_shift,
1201 info->write_streams.total_bytes >> unit_shift,
1204 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1205 imagex_printf(T("\n"));
1207 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1208 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1209 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1210 imagex_printf(T("\n"));
1212 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1213 info->scan.wim_target_path);
1215 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1217 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1218 switch (info->scan.status) {
1219 case WIMLIB_SCAN_DENTRY_OK:
1220 report_scan_progress(&info->scan, false);
1222 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1223 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1225 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1226 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1227 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1229 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1230 /* Symlink fixups are enabled by default. This is
1231 * mainly intended for Windows, which for some reason
1232 * uses absolute junctions (with drive letters!) in the
1233 * default installation. On UNIX-like systems, warn the
1234 * user when fixing the target of an absolute symbolic
1235 * link, so they know to disable this if they want. */
1237 imagex_printf(T("\nWARNING: Adjusted target of "
1238 "absolute symbolic link \"%"TS"\"\n"
1239 " (Use --norpfix to capture "
1240 "absolute symbolic links as-is)\n"),
1241 info->scan.cur_path);
1248 case WIMLIB_PROGRESS_MSG_SCAN_END:
1249 report_scan_progress(&info->scan, true);
1250 imagex_printf(T("\n"));
1252 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1253 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1254 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1255 info->integrity.total_bytes);
1256 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1257 "of %"PRIu64" %"TS" (%u%%) done"),
1258 info->integrity.filename,
1259 info->integrity.completed_bytes >> unit_shift,
1261 info->integrity.total_bytes >> unit_shift,
1264 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1265 imagex_printf(T("\n"));
1267 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1268 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1269 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1270 info->integrity.total_bytes);
1271 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1272 "of %"PRIu64" %"TS" (%u%%) done"),
1273 info->integrity.completed_bytes >> unit_shift,
1275 info->integrity.total_bytes >> unit_shift,
1278 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1279 imagex_printf(T("\n"));
1281 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1282 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1283 "to %"TS" \"%"TS"\"\n"),
1284 info->extract.image,
1285 info->extract.image_name,
1286 info->extract.wimfile_name,
1287 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1288 T("NTFS volume") : T("directory")),
1289 info->extract.target);
1291 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1292 if (info->extract.end_file_count >= 2000) {
1293 percent_done = TO_PERCENT(info->extract.current_file_count,
1294 info->extract.end_file_count);
1295 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1296 info->extract.current_file_count,
1297 info->extract.end_file_count, percent_done);
1298 if (info->extract.current_file_count == info->extract.end_file_count)
1299 imagex_printf(T("\n"));
1302 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1303 percent_done = TO_PERCENT(info->extract.completed_bytes,
1304 info->extract.total_bytes);
1305 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1306 imagex_printf(T("\rExtracting file data: "
1307 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1308 info->extract.completed_bytes >> unit_shift,
1310 info->extract.total_bytes >> unit_shift,
1313 if (info->extract.completed_bytes >= info->extract.total_bytes)
1314 imagex_printf(T("\n"));
1316 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1317 if (info->extract.end_file_count >= 2000) {
1318 percent_done = TO_PERCENT(info->extract.current_file_count,
1319 info->extract.end_file_count);
1320 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1321 info->extract.current_file_count,
1322 info->extract.end_file_count, percent_done);
1323 if (info->extract.current_file_count == info->extract.end_file_count)
1324 imagex_printf(T("\n"));
1327 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1328 if (info->extract.total_parts != 1) {
1329 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1330 info->extract.part_number,
1331 info->extract.total_parts);
1334 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1335 percent_done = TO_PERCENT(info->split.completed_bytes,
1336 info->split.total_bytes);
1337 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1338 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1339 "%"PRIu64" %"TS" (%u%%) written\n"),
1340 info->split.part_name,
1341 info->split.cur_part_number,
1342 info->split.total_parts,
1343 info->split.completed_bytes >> unit_shift,
1345 info->split.total_bytes >> unit_shift,
1349 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1350 if (info->split.completed_bytes == info->split.total_bytes) {
1351 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1352 info->split.cur_part_number,
1353 info->split.total_parts);
1356 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1357 switch (info->update.command->op) {
1358 case WIMLIB_UPDATE_OP_DELETE:
1359 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1360 info->update.command->delete_.wim_path);
1362 case WIMLIB_UPDATE_OP_RENAME:
1363 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1364 info->update.command->rename.wim_source_path,
1365 info->update.command->rename.wim_target_path);
1367 case WIMLIB_UPDATE_OP_ADD:
1372 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1373 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1374 info->replace.path_in_wim);
1376 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1377 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1378 info->wimboot_exclude.path_in_wim);
1380 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1381 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1382 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1383 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1384 info->unmount.mounted_wim,
1385 info->unmount.mounted_image);
1387 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1388 info->unmount.mounted_wim,
1389 info->unmount.mounted_image);
1390 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1394 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1395 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1396 info->verify_image.current_image,
1397 info->verify_image.total_images);
1399 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1400 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1401 info->verify_streams.total_bytes);
1402 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1403 imagex_printf(T("\rVerifying file data: "
1404 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1405 info->verify_streams.completed_bytes >> unit_shift,
1407 info->verify_streams.total_bytes >> unit_shift,
1410 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1411 imagex_printf(T("\n"));
1416 imagex_flush_output();
1417 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1421 parse_num_threads(const tchar *optarg)
1424 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1425 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1426 imagex_error(T("Number of threads must be a non-negative integer!"));
1434 parse_chunk_size(const tchar *optarg)
1437 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1438 if (chunk_size == 0) {
1439 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1440 " with optional K, M, or G suffix"));
1444 if (*tmp == T('k') || *tmp == T('K')) {
1447 } else if (*tmp == T('m') || *tmp == T('M')) {
1450 } else if (*tmp == T('g') || *tmp == T('G')) {
1454 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1455 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1459 if (chunk_size >= UINT32_MAX) {
1460 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1468 * Parse an option passed to an update command.
1470 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1473 * @option: Text string for the option (beginning with --)
1475 * @cmd: `struct wimlib_update_command' that is being constructed for
1478 * Returns true if the option was recognized; false if not.
1481 update_command_add_option(int op, const tchar *option,
1482 struct wimlib_update_command *cmd)
1484 bool recognized = true;
1486 case WIMLIB_UPDATE_OP_ADD:
1487 if (!tstrcmp(option, T("--verbose")))
1488 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1489 else if (!tstrcmp(option, T("--unix-data")))
1490 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1491 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1492 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1493 else if (!tstrcmp(option, T("--strict-acls")))
1494 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1495 else if (!tstrcmp(option, T("--dereference")))
1496 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1497 else if (!tstrcmp(option, T("--no-replace")))
1498 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1502 case WIMLIB_UPDATE_OP_DELETE:
1503 if (!tstrcmp(option, T("--force")))
1504 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1505 else if (!tstrcmp(option, T("--recursive")))
1506 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1517 /* How many nonoption arguments each `imagex update' command expects */
1518 static const unsigned update_command_num_nonoptions[] = {
1519 [WIMLIB_UPDATE_OP_ADD] = 2,
1520 [WIMLIB_UPDATE_OP_DELETE] = 1,
1521 [WIMLIB_UPDATE_OP_RENAME] = 2,
1525 update_command_add_nonoption(int op, const tchar *nonoption,
1526 struct wimlib_update_command *cmd,
1527 unsigned num_nonoptions)
1530 case WIMLIB_UPDATE_OP_ADD:
1531 if (num_nonoptions == 0)
1532 cmd->add.fs_source_path = (tchar*)nonoption;
1534 cmd->add.wim_target_path = (tchar*)nonoption;
1536 case WIMLIB_UPDATE_OP_DELETE:
1537 cmd->delete_.wim_path = (tchar*)nonoption;
1539 case WIMLIB_UPDATE_OP_RENAME:
1540 if (num_nonoptions == 0)
1541 cmd->rename.wim_source_path = (tchar*)nonoption;
1543 cmd->rename.wim_target_path = (tchar*)nonoption;
1549 * Parse a command passed on stdin to `imagex update'.
1551 * @line: Text of the command.
1552 * @len: Length of the line, including a null terminator
1555 * @command: A `struct wimlib_update_command' to fill in from the parsed
1558 * @line_number: Line number of the command, for diagnostics.
1560 * Returns true on success; returns false on parse error.
1563 parse_update_command(tchar *line, size_t len,
1564 struct wimlib_update_command *command,
1568 tchar *command_name;
1570 size_t num_nonoptions;
1572 /* Get the command name ("add", "delete", "rename") */
1573 ret = parse_string(&line, &len, &command_name);
1574 if (ret != PARSE_STRING_SUCCESS)
1577 if (!tstrcasecmp(command_name, T("add"))) {
1578 op = WIMLIB_UPDATE_OP_ADD;
1579 } else if (!tstrcasecmp(command_name, T("delete"))) {
1580 op = WIMLIB_UPDATE_OP_DELETE;
1581 } else if (!tstrcasecmp(command_name, T("rename"))) {
1582 op = WIMLIB_UPDATE_OP_RENAME;
1584 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1585 command_name, line_number);
1590 /* Parse additional options and non-options as needed */
1595 ret = parse_string(&line, &len, &next_string);
1596 if (ret == PARSE_STRING_NONE) /* End of line */
1598 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1600 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1602 if (!update_command_add_option(op, next_string, command))
1604 imagex_error(T("Unrecognized option \"%"TS"\" to "
1605 "update command \"%"TS"\" on line %zu"),
1606 next_string, command_name, line_number);
1612 if (num_nonoptions == update_command_num_nonoptions[op])
1614 imagex_error(T("Unexpected argument \"%"TS"\" in "
1615 "update command on line %zu\n"
1616 " (The \"%"TS"\" command only "
1617 "takes %zu nonoption arguments!)\n"),
1618 next_string, line_number,
1619 command_name, num_nonoptions);
1622 update_command_add_nonoption(op, next_string,
1623 command, num_nonoptions);
1628 if (num_nonoptions != update_command_num_nonoptions[op]) {
1629 imagex_error(T("Not enough arguments to update command "
1630 "\"%"TS"\" on line %zu"), command_name, line_number);
1636 static struct wimlib_update_command *
1637 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1638 size_t *num_cmds_ret)
1642 struct wimlib_update_command *cmds;
1645 nlines = text_file_count_lines(cmd_file_contents_p,
1650 /* Always allocate at least 1 slot, just in case the implementation of
1651 * calloc() returns NULL if 0 bytes are requested. */
1652 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1654 imagex_error(T("out of memory"));
1657 p = *cmd_file_contents_p;
1659 for (i = 0; i < nlines; i++) {
1660 /* XXX: Could use rawmemchr() here instead, but it may not be
1661 * available on all platforms. */
1662 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1663 size_t len = endp - p + 1;
1665 if (!is_comment_line(p, len)) {
1666 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1677 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1678 * one image from a WIM file to an NTFS volume. */
1680 imagex_apply(int argc, tchar **argv, int cmd)
1684 int image = WIMLIB_NO_IMAGE;
1686 struct wimlib_wim_info info;
1688 const tchar *wimfile;
1689 const tchar *target;
1690 const tchar *image_num_or_name = NULL;
1691 int extract_flags = 0;
1693 STRING_LIST(refglobs);
1695 for_opt(c, apply_options) {
1697 case IMAGEX_CHECK_OPTION:
1698 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1700 case IMAGEX_VERBOSE_OPTION:
1701 /* No longer does anything. */
1703 case IMAGEX_REF_OPTION:
1704 ret = string_list_append(&refglobs, optarg);
1706 goto out_free_refglobs;
1708 case IMAGEX_UNIX_DATA_OPTION:
1709 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1711 case IMAGEX_NO_ACLS_OPTION:
1712 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1714 case IMAGEX_STRICT_ACLS_OPTION:
1715 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1717 case IMAGEX_NO_ATTRIBUTES_OPTION:
1718 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1720 case IMAGEX_NORPFIX_OPTION:
1721 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1723 case IMAGEX_RPFIX_OPTION:
1724 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1726 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1727 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1728 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1730 case IMAGEX_WIMBOOT_OPTION:
1731 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1733 case IMAGEX_COMPACT_OPTION:
1734 ret = set_compact_mode(optarg, &extract_flags);
1736 goto out_free_refglobs;
1744 if (argc != 2 && argc != 3)
1749 if (!tstrcmp(wimfile, T("-"))) {
1750 /* Attempt to apply pipable WIM from standard input. */
1752 image_num_or_name = NULL;
1755 image_num_or_name = argv[1];
1760 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1761 imagex_progress_func, NULL);
1763 goto out_free_refglobs;
1765 wimlib_get_wim_info(wim, &info);
1768 /* Image explicitly specified. */
1769 image_num_or_name = argv[1];
1770 image = wimlib_resolve_image(wim, image_num_or_name);
1771 ret = verify_image_exists(image, image_num_or_name, wimfile);
1773 goto out_wimlib_free;
1776 /* No image specified; default to image 1, but only if the WIM
1777 * contains exactly one image. */
1779 if (info.image_count != 1) {
1780 imagex_error(T("\"%"TS"\" contains %d images; "
1781 "Please select one (or all)."),
1782 wimfile, info.image_count);
1791 if (refglobs.num_strings) {
1793 imagex_error(T("Can't specify --ref when applying from stdin!"));
1795 goto out_wimlib_free;
1797 ret = wim_reference_globs(wim, &refglobs, open_flags);
1799 goto out_wimlib_free;
1804 /* Interpret a regular file or block device target as an NTFS
1808 if (tstat(target, &stbuf)) {
1809 if (errno != ENOENT) {
1810 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1813 goto out_wimlib_free;
1816 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1817 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1823 ret = wimlib_extract_image(wim, image, target, extract_flags);
1825 set_fd_to_binary_mode(STDIN_FILENO);
1826 ret = wimlib_extract_image_from_pipe_with_progress(
1831 imagex_progress_func,
1835 imagex_printf(T("Done applying WIM image.\n"));
1836 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1838 do_resource_not_found_warning(wimfile, &info, &refglobs);
1840 imagex_error(T( "If you are applying an image "
1841 "from a split pipable WIM,\n"
1842 " make sure you have "
1843 "concatenated together all parts."));
1845 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1846 do_metadata_not_found_warning(wimfile, &info);
1851 string_list_destroy(&refglobs);
1855 usage(CMD_APPLY, stderr);
1857 goto out_free_refglobs;
1860 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1861 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1862 * the desired image. 'wimlib-imagex append': add a new image to an existing
1865 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1869 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1870 WIMLIB_ADD_FLAG_WINCONFIG |
1871 WIMLIB_ADD_FLAG_VERBOSE |
1872 WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED;
1873 int write_flags = 0;
1874 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1875 uint32_t chunk_size = UINT32_MAX;
1876 uint32_t solid_chunk_size = UINT32_MAX;
1877 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1878 const tchar *wimfile;
1881 STRING_LIST(image_properties);
1884 STRING_LIST(base_wimfiles);
1885 WIMStruct **base_wims;
1887 WIMStruct *template_wim = NULL;
1888 const tchar *template_wimfile = NULL;
1889 const tchar *template_image_name_or_num = NULL;
1890 int template_image = WIMLIB_NO_IMAGE;
1893 unsigned num_threads = 0;
1898 tchar *config_file = NULL;
1900 bool source_list = false;
1901 size_t source_list_nchars = 0;
1902 tchar *source_list_contents;
1903 bool capture_sources_malloced;
1904 struct wimlib_capture_source *capture_sources;
1906 bool name_defaulted;
1908 for_opt(c, capture_or_append_options) {
1910 case IMAGEX_BOOT_OPTION:
1911 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1913 case IMAGEX_CHECK_OPTION:
1914 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1916 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
1917 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1919 case IMAGEX_NOCHECK_OPTION:
1920 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1922 case IMAGEX_CONFIG_OPTION:
1923 config_file = optarg;
1924 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1926 case IMAGEX_COMPRESS_OPTION:
1927 compression_type = get_compression_type(optarg, false);
1928 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1931 case IMAGEX_CHUNK_SIZE_OPTION:
1932 chunk_size = parse_chunk_size(optarg);
1933 if (chunk_size == UINT32_MAX)
1936 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1937 solid_chunk_size = parse_chunk_size(optarg);
1938 if (solid_chunk_size == UINT32_MAX)
1941 case IMAGEX_SOLID_COMPRESS_OPTION:
1942 solid_ctype = get_compression_type(optarg, true);
1943 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1946 case IMAGEX_SOLID_OPTION:
1947 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1949 case IMAGEX_NO_SOLID_SORT_OPTION:
1950 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1952 case IMAGEX_FLAGS_OPTION: {
1953 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1954 tsprintf(p, T("FLAGS=%"TS), optarg);
1955 ret = string_list_append(&image_properties, p);
1960 case IMAGEX_IMAGE_PROPERTY_OPTION:
1961 ret = append_image_property_argument(&image_properties);
1965 case IMAGEX_DEREFERENCE_OPTION:
1966 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1968 case IMAGEX_VERBOSE_OPTION:
1969 /* No longer does anything. */
1971 case IMAGEX_THREADS_OPTION:
1972 num_threads = parse_num_threads(optarg);
1973 if (num_threads == UINT_MAX)
1976 case IMAGEX_REBUILD_OPTION:
1977 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1979 case IMAGEX_UNIX_DATA_OPTION:
1980 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1982 case IMAGEX_SOURCE_LIST_OPTION:
1985 case IMAGEX_NO_ACLS_OPTION:
1986 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1988 case IMAGEX_STRICT_ACLS_OPTION:
1989 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1991 case IMAGEX_RPFIX_OPTION:
1992 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1994 case IMAGEX_NORPFIX_OPTION:
1995 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1997 case IMAGEX_PIPABLE_OPTION:
1998 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2000 case IMAGEX_NOT_PIPABLE_OPTION:
2001 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2003 case IMAGEX_UPDATE_OF_OPTION:
2004 if (template_image_name_or_num) {
2005 imagex_error(T("'--update-of' can only be "
2006 "specified one time!"));
2010 colon = tstrrchr(optarg, T(':'));
2013 template_wimfile = optarg;
2015 template_image_name_or_num = colon + 1;
2017 template_wimfile = NULL;
2018 template_image_name_or_num = optarg;
2022 case IMAGEX_DELTA_FROM_OPTION:
2023 ret = string_list_append(&base_wimfiles, optarg);
2026 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2028 case IMAGEX_WIMBOOT_OPTION:
2029 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2031 case IMAGEX_UNSAFE_COMPACT_OPTION:
2032 if (cmd != CMD_APPEND) {
2033 imagex_error(T("'--unsafe-compact' is only "
2034 "valid for append!"));
2037 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2039 case IMAGEX_SNAPSHOT_OPTION:
2040 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2049 if (argc < 2 || argc > 4)
2055 /* Set default compression type and parameters. */
2058 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2059 /* No compression type specified. Use the default. */
2061 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2062 /* With --wimboot, default to XPRESS compression. */
2063 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2064 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2065 /* With --solid, default to LZMS compression. (However,
2066 * this will not affect solid resources!) */
2067 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2069 /* Otherwise, default to LZX compression. */
2070 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2074 if (!tstrcmp(wimfile, T("-"))) {
2075 /* Writing captured WIM to standard output. */
2077 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2078 imagex_error("Can't write a non-pipable WIM to "
2079 "standard output! Specify --pipable\n"
2080 " if you want to create a pipable WIM "
2081 "(but read the docs first).");
2085 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2087 if (cmd == CMD_APPEND) {
2088 imagex_error(T("Using standard output for append does "
2089 "not make sense."));
2092 wim_fd = STDOUT_FILENO;
2094 imagex_output_to_stderr();
2095 set_fd_to_binary_mode(wim_fd);
2098 /* If template image was specified using --update-of=IMAGE rather
2099 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2100 if (template_image_name_or_num && !template_wimfile) {
2101 if (base_wimfiles.num_strings == 1) {
2102 /* Capturing delta WIM based on single WIM: default to
2104 template_wimfile = base_wimfiles.strings[0];
2105 } else if (cmd == CMD_APPEND) {
2106 /* Appending to WIM: default to WIM being appended to.
2108 template_wimfile = wimfile;
2110 /* Capturing a normal (non-delta) WIM, so the WIM file
2111 * *must* be explicitly specified. */
2112 if (base_wimfiles.num_strings > 1) {
2113 imagex_error(T("For capture of delta WIM "
2114 "based on multiple existing "
2116 " '--update-of' must "
2117 "specify WIMFILE:IMAGE!"));
2119 imagex_error(T("For capture of non-delta WIM, "
2120 "'--update-of' must specify "
2129 name_defaulted = false;
2131 /* Set default name to SOURCE argument, omitting any directory
2132 * prefixes and trailing slashes. This requires making a copy
2133 * of @source. Leave some free characters at the end in case we
2134 * append a number to keep the name unique. */
2135 size_t source_name_len;
2137 source_name_len = tstrlen(source);
2138 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2139 name = tbasename(tstrcpy(source_copy, source));
2140 name_defaulted = true;
2143 /* Image description (if given). */
2145 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2146 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2147 ret = string_list_append(&image_properties, p);
2153 /* Set up capture sources in source list mode */
2154 if (source[0] == T('-') && source[1] == T('\0')) {
2155 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2157 source_list_contents = file_get_text_contents(source,
2158 &source_list_nchars);
2160 if (!source_list_contents)
2163 capture_sources = parse_source_list(&source_list_contents,
2166 if (!capture_sources) {
2168 goto out_free_source_list_contents;
2170 capture_sources_malloced = true;
2172 /* Set up capture source in non-source-list mode. */
2173 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2174 capture_sources[0].fs_source_path = source;
2175 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2176 capture_sources[0].reserved = 0;
2178 capture_sources_malloced = false;
2179 source_list_contents = NULL;
2182 /* Open the existing WIM, or create a new one. */
2183 if (cmd == CMD_APPEND) {
2184 ret = wimlib_open_wim_with_progress(wimfile,
2185 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2187 imagex_progress_func,
2190 goto out_free_capture_sources;
2192 ret = wimlib_create_new_wim(compression_type, &wim);
2194 goto out_free_capture_sources;
2195 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2198 /* Set chunk size if non-default. */
2199 if (chunk_size != UINT32_MAX) {
2200 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2203 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2205 int ctype = compression_type;
2207 if (cmd == CMD_APPEND) {
2208 struct wimlib_wim_info info;
2209 wimlib_get_wim_info(wim, &info);
2210 ctype = info.compression_type;
2213 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2214 ret = wimlib_set_output_chunk_size(wim, 4096);
2219 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2220 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2224 if (solid_chunk_size != UINT32_MAX) {
2225 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2231 /* Detect if source is regular file or block device and set NTFS volume
2236 if (tstat(source, &stbuf) == 0) {
2237 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2238 imagex_printf(T("Capturing WIM image from NTFS "
2239 "filesystem on \"%"TS"\"\n"), source);
2240 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2243 if (errno != ENOENT) {
2244 imagex_error_with_errno(T("Failed to stat "
2245 "\"%"TS"\""), source);
2253 /* If the user did not specify an image name, and the basename of the
2254 * source already exists as an image name in the WIM file, append a
2255 * suffix to make it unique. */
2256 if (cmd == CMD_APPEND && name_defaulted) {
2257 unsigned long conflict_idx;
2258 tchar *name_end = tstrchr(name, T('\0'));
2259 for (conflict_idx = 1;
2260 wimlib_image_name_in_use(wim, name);
2263 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2267 /* If capturing a delta WIM, reference resources from the base WIMs
2268 * before adding the new image. */
2269 if (base_wimfiles.num_strings) {
2270 base_wims = calloc(base_wimfiles.num_strings,
2271 sizeof(base_wims[0]));
2272 if (base_wims == NULL) {
2273 imagex_error(T("Out of memory!"));
2278 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2279 ret = wimlib_open_wim_with_progress(
2280 base_wimfiles.strings[i], open_flags,
2281 &base_wims[i], imagex_progress_func, NULL);
2283 goto out_free_base_wims;
2287 ret = wimlib_reference_resources(wim, base_wims,
2288 base_wimfiles.num_strings, 0);
2290 goto out_free_base_wims;
2292 if (base_wimfiles.num_strings == 1) {
2293 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2294 base_wimfiles.strings[0]);
2296 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2297 base_wimfiles.num_strings);
2304 /* If capturing or appending as an update of an existing (template) image,
2305 * open the WIM if needed and parse the image index. */
2306 if (template_image_name_or_num) {
2308 if (cmd == CMD_APPEND && !tstrcmp(template_wimfile, wimfile)) {
2311 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2312 if (!tstrcmp(template_wimfile,
2313 base_wimfiles.strings[i])) {
2314 template_wim = base_wims[i];
2320 if (!template_wim) {
2321 ret = wimlib_open_wim_with_progress(template_wimfile,
2324 imagex_progress_func,
2327 goto out_free_base_wims;
2330 template_image = wimlib_resolve_image(template_wim,
2331 template_image_name_or_num);
2333 if (template_image_name_or_num[0] == T('-')) {
2336 struct wimlib_wim_info info;
2338 wimlib_get_wim_info(template_wim, &info);
2339 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2340 if (n >= 1 && n <= info.image_count &&
2342 tmp != template_image_name_or_num + 1)
2344 template_image = info.image_count - (n - 1);
2347 ret = verify_image_exists_and_is_single(template_image,
2348 template_image_name_or_num,
2351 goto out_free_template_wim;
2354 ret = wimlib_add_image_multisource(wim,
2361 goto out_free_template_wim;
2363 if (image_properties.num_strings || template_image_name_or_num) {
2364 /* User asked to set additional image properties, or an image on
2365 * which the added one is to be based has been specified with
2367 struct wimlib_wim_info info;
2369 wimlib_get_wim_info(wim, &info);
2371 ret = apply_image_properties(&image_properties, wim,
2372 info.image_count, NULL);
2374 goto out_free_template_wim;
2376 /* Reference template image if the user provided one. */
2377 if (template_image_name_or_num) {
2378 imagex_printf(T("Using image %d "
2379 "from \"%"TS"\" as template\n"),
2380 template_image, template_wimfile);
2381 ret = wimlib_reference_template_image(wim,
2387 goto out_free_template_wim;
2391 /* Write the new WIM or overwrite the existing WIM with the new image
2393 if (cmd == CMD_APPEND) {
2394 ret = wimlib_overwrite(wim, write_flags, num_threads);
2395 } else if (wimfile) {
2396 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2397 write_flags, num_threads);
2399 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2400 write_flags, num_threads);
2402 out_free_template_wim:
2403 /* 'template_wim' may alias 'wim' or any of the 'base_wims' */
2404 if (template_wim == wim)
2405 goto out_free_base_wims;
2406 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2407 if (template_wim == base_wims[i])
2408 goto out_free_base_wims;
2409 wimlib_free(template_wim);
2411 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2412 wimlib_free(base_wims[i]);
2416 out_free_capture_sources:
2417 if (capture_sources_malloced)
2418 free(capture_sources);
2419 out_free_source_list_contents:
2420 free(source_list_contents);
2422 string_list_destroy(&image_properties);
2423 string_list_destroy(&base_wimfiles);
2433 /* Remove image(s) from a WIM. */
2435 imagex_delete(int argc, tchar **argv, int cmd)
2438 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2439 int write_flags = 0;
2440 const tchar *wimfile;
2441 const tchar *image_num_or_name;
2446 for_opt(c, delete_options) {
2448 case IMAGEX_CHECK_OPTION:
2449 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2451 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2452 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2454 case IMAGEX_SOFT_OPTION:
2455 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2457 case IMAGEX_UNSAFE_COMPACT_OPTION:
2458 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2469 imagex_error(T("Must specify a WIM file"));
2471 imagex_error(T("Must specify an image"));
2475 image_num_or_name = argv[1];
2477 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2478 imagex_progress_func, NULL);
2482 image = wimlib_resolve_image(wim, image_num_or_name);
2484 ret = verify_image_exists(image, image_num_or_name, wimfile);
2486 goto out_wimlib_free;
2488 ret = wimlib_delete_image(wim, image);
2490 imagex_error(T("Failed to delete image from \"%"TS"\""),
2492 goto out_wimlib_free;
2495 ret = wimlib_overwrite(wim, write_flags, 0);
2497 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2498 "deleted"), wimfile);
2506 usage(CMD_DELETE, stderr);
2511 struct print_dentry_options {
2516 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2518 tprintf(T("%"TS"\n"), dentry->full_path);
2521 static const struct {
2524 } file_attr_flags[] = {
2525 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2526 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2527 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2528 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2529 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2530 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2531 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2532 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2533 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2534 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2535 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2536 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2537 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2538 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2539 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2542 #define TIMESTR_MAX 100
2545 print_time(const tchar *type, const struct wimlib_timespec *wts,
2548 tchar timestr[TIMESTR_MAX];
2552 if (sizeof(wts->tv_sec) == 4 && sizeof(t) > sizeof(wts->tv_sec))
2553 t = (uint32_t)wts->tv_sec | ((uint64_t)high_part << 32);
2558 tstrftime(timestr, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2559 timestr[TIMESTR_MAX - 1] = '\0';
2561 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2564 static void print_byte_field(const uint8_t field[], size_t len)
2567 tprintf(T("%02hhx"), *field++);
2571 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2573 tchar attr_string[256];
2576 tputs(T("WIM Information:"));
2577 tputs(T("----------------"));
2578 tprintf(T("Path: %"TS"\n"), wimfile);
2579 tprintf(T("GUID: 0x"));
2580 print_byte_field(info->guid, sizeof(info->guid));
2582 tprintf(T("Version: %u\n"), info->wim_version);
2583 tprintf(T("Image Count: %d\n"), info->image_count);
2584 tprintf(T("Compression: %"TS"\n"),
2585 wimlib_get_compression_type_string(info->compression_type));
2586 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2588 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2589 tprintf(T("Boot Index: %d\n"), info->boot_index);
2590 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2592 attr_string[0] = T('\0');
2595 tstrcat(attr_string, T("Pipable, "));
2597 if (info->has_integrity_table)
2598 tstrcat(attr_string, T("Integrity info, "));
2600 if (info->has_rpfix)
2601 tstrcat(attr_string, T("Relative path junction, "));
2603 if (info->resource_only)
2604 tstrcat(attr_string, T("Resource only, "));
2606 if (info->metadata_only)
2607 tstrcat(attr_string, T("Metadata only, "));
2609 if (info->is_marked_readonly)
2610 tstrcat(attr_string, T("Readonly, "));
2612 p = tstrchr(attr_string, T('\0'));
2613 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2616 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2620 print_resource(const struct wimlib_resource_entry *resource,
2623 tprintf(T("Hash = 0x"));
2624 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2627 if (!resource->is_missing) {
2628 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2629 resource->uncompressed_size);
2630 if (resource->packed) {
2631 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2632 "bytes @ offset %"PRIu64"\n"),
2633 resource->raw_resource_uncompressed_size,
2634 resource->raw_resource_compressed_size,
2635 resource->raw_resource_offset_in_wim);
2637 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2640 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2641 resource->compressed_size);
2643 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2647 tprintf(T("Part Number = %u\n"), resource->part_number);
2648 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2650 tprintf(T("Flags = "));
2651 if (resource->is_compressed)
2652 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2653 if (resource->is_metadata)
2654 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2655 if (resource->is_free)
2656 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2657 if (resource->is_spanned)
2658 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2659 if (resource->packed)
2660 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2668 print_blobs(WIMStruct *wim)
2670 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2675 default_print_security_descriptor(const uint8_t *sd, size_t size)
2677 tprintf(T("Security Descriptor = "));
2678 print_byte_field(sd, size);
2684 is_null_guid(const uint8_t *guid)
2686 static const uint8_t null_guid[WIMLIB_GUID_LEN];
2688 return !memcmp(guid, null_guid, WIMLIB_GUID_LEN);
2692 print_guid(const tchar *label, const uint8_t *guid)
2694 if (is_null_guid(guid))
2696 tprintf(T("%-20"TS"= 0x"), label);
2697 print_byte_field(guid, WIMLIB_GUID_LEN);
2702 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2705 "----------------------------------------------------------------------------\n"));
2706 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2707 if (dentry->dos_name)
2708 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2709 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2710 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2711 if (file_attr_flags[i].flag & dentry->attributes)
2712 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2713 file_attr_flags[i].name);
2715 if (dentry->security_descriptor) {
2716 print_security_descriptor(dentry->security_descriptor,
2717 dentry->security_descriptor_size);
2720 print_time(T("Creation Time"),
2721 &dentry->creation_time, dentry->creation_time_high);
2722 print_time(T("Last Write Time"),
2723 &dentry->last_write_time, dentry->last_write_time_high);
2724 print_time(T("Last Access Time"),
2725 &dentry->last_access_time, dentry->last_access_time_high);
2728 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2729 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2731 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2732 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2734 if (dentry->unix_mode != 0) {
2735 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2736 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2737 dentry->unix_uid, dentry->unix_gid,
2738 dentry->unix_mode, dentry->unix_rdev);
2741 if (!is_null_guid(dentry->object_id.object_id)) {
2742 print_guid(T("Object ID"), dentry->object_id.object_id);
2743 print_guid(T("Birth Volume ID"), dentry->object_id.birth_volume_id);
2744 print_guid(T("Birth Object ID"), dentry->object_id.birth_object_id);
2745 print_guid(T("Domain ID"), dentry->object_id.domain_id);
2748 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2749 if (dentry->streams[i].stream_name) {
2750 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2751 dentry->streams[i].stream_name);
2752 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2753 tprintf(T("\tRaw encrypted data stream:\n"));
2754 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2755 tprintf(T("\tReparse point stream:\n"));
2757 tprintf(T("\tUnnamed data stream:\n"));
2759 print_resource(&dentry->streams[i].resource, NULL);
2764 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2766 const struct print_dentry_options *options = _options;
2767 if (!options->detailed)
2768 print_dentry_full_path(dentry);
2770 print_dentry_detailed(dentry);
2774 /* Print the files contained in an image(s) in a WIM file. */
2776 imagex_dir(int argc, tchar **argv, int cmd)
2778 const tchar *wimfile;
2779 WIMStruct *wim = NULL;
2782 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2784 struct print_dentry_options options = {
2787 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2789 STRING_LIST(refglobs);
2791 for_opt(c, dir_options) {
2793 case IMAGEX_PATH_OPTION:
2796 case IMAGEX_DETAILED_OPTION:
2797 options.detailed = true;
2799 case IMAGEX_ONE_FILE_ONLY_OPTION:
2800 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2802 case IMAGEX_REF_OPTION:
2803 ret = string_list_append(&refglobs, optarg);
2805 goto out_free_refglobs;
2815 imagex_error(T("Must specify a WIM file"));
2819 imagex_error(T("Too many arguments"));
2824 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2825 imagex_progress_func, NULL);
2827 goto out_free_refglobs;
2830 image = wimlib_resolve_image(wim, argv[1]);
2831 ret = verify_image_exists(image, argv[1], wimfile);
2833 goto out_wimlib_free;
2835 /* No image specified; default to image 1, but only if the WIM
2836 * contains exactly one image. */
2838 struct wimlib_wim_info info;
2840 wimlib_get_wim_info(wim, &info);
2841 if (info.image_count != 1) {
2842 imagex_error(T("\"%"TS"\" contains %d images; Please "
2843 "select one (or all)."),
2844 wimfile, info.image_count);
2851 if (refglobs.num_strings) {
2852 ret = wim_reference_globs(wim, &refglobs, 0);
2854 goto out_wimlib_free;
2857 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2858 print_dentry, &options);
2859 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2860 struct wimlib_wim_info info;
2862 wimlib_get_wim_info(wim, &info);
2863 do_metadata_not_found_warning(wimfile, &info);
2868 string_list_destroy(&refglobs);
2872 usage(CMD_DIR, stderr);
2874 goto out_free_refglobs;
2877 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2880 imagex_export(int argc, tchar **argv, int cmd)
2884 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2885 int write_flags = 0;
2886 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2887 const tchar *src_wimfile;
2888 const tchar *src_image_num_or_name;
2889 const tchar *dest_wimfile;
2891 const tchar *dest_name;
2892 const tchar *dest_desc;
2894 struct wimlib_wim_info src_info;
2895 WIMStruct *dest_wim;
2900 STRING_LIST(refglobs);
2901 unsigned num_threads = 0;
2902 uint32_t chunk_size = UINT32_MAX;
2903 uint32_t solid_chunk_size = UINT32_MAX;
2904 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2906 for_opt(c, export_options) {
2908 case IMAGEX_BOOT_OPTION:
2909 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2911 case IMAGEX_CHECK_OPTION:
2912 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2914 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
2915 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2917 case IMAGEX_NOCHECK_OPTION:
2918 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2920 case IMAGEX_COMPRESS_OPTION:
2921 compression_type = get_compression_type(optarg, false);
2922 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2925 case IMAGEX_RECOMPRESS_OPTION:
2926 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2928 case IMAGEX_SOLID_OPTION:
2929 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2931 case IMAGEX_NO_SOLID_SORT_OPTION:
2932 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2934 case IMAGEX_CHUNK_SIZE_OPTION:
2935 chunk_size = parse_chunk_size(optarg);
2936 if (chunk_size == UINT32_MAX)
2939 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2940 solid_chunk_size = parse_chunk_size(optarg);
2941 if (solid_chunk_size == UINT32_MAX)
2944 case IMAGEX_SOLID_COMPRESS_OPTION:
2945 solid_ctype = get_compression_type(optarg, true);
2946 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2949 case IMAGEX_REF_OPTION:
2950 ret = string_list_append(&refglobs, optarg);
2952 goto out_free_refglobs;
2954 case IMAGEX_THREADS_OPTION:
2955 num_threads = parse_num_threads(optarg);
2956 if (num_threads == UINT_MAX)
2959 case IMAGEX_REBUILD_OPTION:
2960 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2962 case IMAGEX_PIPABLE_OPTION:
2963 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2965 case IMAGEX_NOT_PIPABLE_OPTION:
2966 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2968 case IMAGEX_WIMBOOT_OPTION:
2969 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2971 case IMAGEX_UNSAFE_COMPACT_OPTION:
2972 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2980 if (argc < 3 || argc > 5)
2982 src_wimfile = argv[0];
2983 src_image_num_or_name = argv[1];
2984 dest_wimfile = argv[2];
2985 dest_name = (argc >= 4) ? argv[3] : NULL;
2986 dest_desc = (argc >= 5) ? argv[4] : NULL;
2987 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2988 imagex_progress_func, NULL);
2990 goto out_free_refglobs;
2992 wimlib_get_wim_info(src_wim, &src_info);
2994 /* Determine if the destination is an existing file or not. If so, we
2995 * try to append the exported image(s) to it; otherwise, we create a new
2996 * WIM containing the exported image(s). Furthermore, determine if we
2997 * need to write a pipable WIM directly to standard output. */
2999 if (tstrcmp(dest_wimfile, T("-")) == 0) {
3001 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
3002 imagex_error("Can't write a non-pipable WIM to "
3003 "standard output! Specify --pipable\n"
3004 " if you want to create a pipable WIM "
3005 "(but read the docs first).");
3007 goto out_free_src_wim;
3010 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3012 dest_wimfile = NULL;
3013 dest_wim_fd = STDOUT_FILENO;
3014 imagex_output_to_stderr();
3015 set_fd_to_binary_mode(dest_wim_fd);
3018 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
3020 /* Destination file exists. */
3022 if (!S_ISREG(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) {
3023 imagex_error(T("\"%"TS"\" is not a regular file "
3024 "or block device"), dest_wimfile);
3026 goto out_free_src_wim;
3028 ret = wimlib_open_wim_with_progress(dest_wimfile,
3030 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
3032 imagex_progress_func,
3035 goto out_free_src_wim;
3037 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3038 /* The user specified a compression type, but we're
3039 * exporting to an existing WIM. Make sure the
3040 * specified compression type is the same as the
3041 * compression type of the existing destination WIM. */
3042 struct wimlib_wim_info dest_info;
3044 wimlib_get_wim_info(dest_wim, &dest_info);
3045 if (compression_type != dest_info.compression_type) {
3046 imagex_error(T("Cannot specify a compression type that is "
3047 "not the same as that used in the "
3048 "destination WIM"));
3050 goto out_free_dest_wim;
3056 if (errno != ENOENT) {
3057 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3060 goto out_free_src_wim;
3063 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3064 imagex_error(T("'--unsafe-compact' is only valid when "
3065 "exporting to an existing WIM file!"));
3067 goto out_free_src_wim;
3070 /* dest_wimfile is not an existing file, so create a new WIM. */
3072 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3073 /* The user did not specify a compression type; default
3074 * to that of the source WIM, unless --solid or
3075 * --wimboot was specified. */
3077 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3078 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3079 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3080 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3082 compression_type = src_info.compression_type;
3084 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3086 goto out_free_src_wim;
3088 wimlib_register_progress_function(dest_wim,
3089 imagex_progress_func, NULL);
3091 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3092 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3094 /* For --wimboot export, use small XPRESS chunks. */
3095 wimlib_set_output_chunk_size(dest_wim, 4096);
3096 } else if (compression_type == src_info.compression_type &&
3097 chunk_size == UINT32_MAX)
3099 /* Use same chunk size if compression type is the same. */
3100 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3104 if (chunk_size != UINT32_MAX) {
3105 /* Set destination chunk size. */
3106 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3108 goto out_free_dest_wim;
3110 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3111 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3113 goto out_free_dest_wim;
3115 if (solid_chunk_size != UINT32_MAX) {
3116 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3118 goto out_free_dest_wim;
3121 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3122 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3124 goto out_free_dest_wim;
3126 if (refglobs.num_strings) {
3127 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3129 goto out_free_dest_wim;
3132 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3133 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3135 imagex_error(T("--boot specified for all-images export, but source WIM "
3136 "has no bootable image."));
3138 goto out_free_dest_wim;
3141 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3142 dest_desc, export_flags);
3144 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3145 do_resource_not_found_warning(src_wimfile,
3146 &src_info, &refglobs);
3147 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3148 do_metadata_not_found_warning(src_wimfile, &src_info);
3150 goto out_free_dest_wim;
3154 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3155 else if (dest_wimfile)
3156 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3157 write_flags, num_threads);
3159 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3160 WIMLIB_ALL_IMAGES, write_flags,
3163 wimlib_free(dest_wim);
3165 wimlib_free(src_wim);
3167 string_list_destroy(&refglobs);
3171 usage(CMD_EXPORT, stderr);
3174 goto out_free_refglobs;
3177 /* Extract files or directories from a WIM image */
3179 imagex_extract(int argc, tchar **argv, int cmd)
3186 const tchar *wimfile;
3187 const tchar *image_num_or_name;
3188 tchar *dest_dir = T(".");
3189 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3190 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3191 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3192 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3194 STRING_LIST(refglobs);
3196 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3198 for_opt(c, extract_options) {
3200 case IMAGEX_CHECK_OPTION:
3201 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3203 case IMAGEX_VERBOSE_OPTION:
3204 /* No longer does anything. */
3206 case IMAGEX_REF_OPTION:
3207 ret = string_list_append(&refglobs, optarg);
3209 goto out_free_refglobs;
3211 case IMAGEX_UNIX_DATA_OPTION:
3212 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3214 case IMAGEX_NO_ACLS_OPTION:
3215 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3217 case IMAGEX_STRICT_ACLS_OPTION:
3218 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3220 case IMAGEX_NO_ATTRIBUTES_OPTION:
3221 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3223 case IMAGEX_DEST_DIR_OPTION:
3226 case IMAGEX_TO_STDOUT_OPTION:
3227 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3228 imagex_suppress_output();
3229 set_fd_to_binary_mode(STDOUT_FILENO);
3231 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3232 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3233 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3235 case IMAGEX_NO_GLOBS_OPTION:
3236 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3238 case IMAGEX_NULLGLOB_OPTION:
3239 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3241 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3242 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3244 case IMAGEX_WIMBOOT_OPTION:
3245 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3247 case IMAGEX_COMPACT_OPTION:
3248 ret = set_compact_mode(optarg, &extract_flags);
3250 goto out_free_refglobs;
3262 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3263 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3265 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3270 image_num_or_name = argv[1];
3275 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3276 imagex_progress_func, NULL);
3278 goto out_free_refglobs;
3280 image = wimlib_resolve_image(wim, image_num_or_name);
3281 ret = verify_image_exists_and_is_single(image,
3285 goto out_wimlib_free;
3287 if (refglobs.num_strings) {
3288 ret = wim_reference_globs(wim, &refglobs, open_flags);
3290 goto out_wimlib_free;
3296 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3299 while (argc != 0 && ret == 0) {
3303 num_paths < argc && argv[num_paths][0] != T('@');
3308 ret = wimlib_extract_paths(wim, image, dest_dir,
3309 (const tchar **)argv,
3311 extract_flags | notlist_extract_flags);
3315 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3324 imagex_printf(T("Done extracting files.\n"));
3325 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3326 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3327 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3328 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3329 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3332 T("Note: You can use the '--nullglob' "
3333 "option to ignore missing files.\n"));
3335 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3336 "files and directories\n"
3337 " are in the WIM image.\n"),
3338 get_cmd_string(CMD_DIR, false));
3339 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3340 struct wimlib_wim_info info;
3342 wimlib_get_wim_info(wim, &info);
3343 do_resource_not_found_warning(wimfile, &info, &refglobs);
3344 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3345 struct wimlib_wim_info info;
3347 wimlib_get_wim_info(wim, &info);
3348 do_metadata_not_found_warning(wimfile, &info);
3353 string_list_destroy(&refglobs);
3357 usage(CMD_EXTRACT, stderr);
3360 goto out_free_refglobs;
3363 /* Prints information about a WIM file; also can mark an image as bootable,
3364 * change the name of an image, or change the description of an image. */
3366 imagex_info(int argc, tchar **argv, int cmd)
3370 bool header = false;
3373 bool short_header = true;
3374 const tchar *xml_out_file = NULL;
3375 const tchar *wimfile;
3376 const tchar *image_num_or_name;
3377 STRING_LIST(image_properties);
3382 int write_flags = 0;
3383 struct wimlib_wim_info info;
3385 for_opt(c, info_options) {
3387 case IMAGEX_BOOT_OPTION:
3390 case IMAGEX_CHECK_OPTION:
3391 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3393 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3394 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3396 case IMAGEX_NOCHECK_OPTION:
3397 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3399 case IMAGEX_HEADER_OPTION:
3401 short_header = false;
3403 case IMAGEX_BLOBS_OPTION:
3405 short_header = false;
3407 case IMAGEX_XML_OPTION:
3409 short_header = false;
3411 case IMAGEX_EXTRACT_XML_OPTION:
3412 xml_out_file = optarg;
3413 short_header = false;
3415 case IMAGEX_IMAGE_PROPERTY_OPTION:
3416 ret = append_image_property_argument(&image_properties);
3427 if (argc < 1 || argc > 4)
3431 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3435 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3436 tsprintf(p, T("NAME=%"TS), argv[2]);
3437 ret = string_list_append(&image_properties, p);
3444 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3445 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3446 ret = string_list_append(&image_properties, p);
3451 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3452 imagex_progress_func, NULL);
3456 wimlib_get_wim_info(wim, &info);
3458 image = wimlib_resolve_image(wim, image_num_or_name);
3459 ret = WIMLIB_ERR_INVALID_IMAGE;
3460 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3461 verify_image_exists(image, image_num_or_name, wimfile);
3463 imagex_error(T("If you would like to set the boot "
3464 "index to 0, specify image \"0\" with "
3465 "the --boot flag."));
3467 goto out_wimlib_free;
3470 if (boot && info.image_count == 0) {
3471 imagex_error(T("--boot is meaningless on a WIM with no images"));
3472 goto out_wimlib_free;
3475 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3477 imagex_error(T("Cannot specify the --boot flag "
3478 "without specifying a specific "
3479 "image in a multi-image WIM"));
3480 goto out_wimlib_free;
3482 if (image_properties.num_strings) {
3483 imagex_error(T("Can't change image properties without "
3484 "specifying a specific image in a "
3485 "multi-image WIM"));
3486 goto out_wimlib_free;
3490 /* Operations that print information are separated from operations that
3491 * recreate the WIM file. */
3492 if (!image_properties.num_strings && !boot) {
3494 /* Read-only operations */
3496 if (image == WIMLIB_NO_IMAGE) {
3497 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3498 image_num_or_name, wimfile);
3499 goto out_wimlib_free;
3502 if (image == WIMLIB_ALL_IMAGES && short_header)
3503 print_wim_information(wimfile, &info);
3506 wimlib_print_header(wim);
3509 if (info.total_parts != 1) {
3510 tfprintf(stderr, T("Warning: Only showing the blobs "
3511 "for part %d of a %d-part WIM.\n"),
3512 info.part_number, info.total_parts);
3518 ret = wimlib_extract_xml_data(wim, stdout);
3520 goto out_wimlib_free;
3526 fp = tfopen(xml_out_file, T("wb"));
3528 imagex_error_with_errno(T("Failed to open the "
3529 "file \"%"TS"\" for "
3533 goto out_wimlib_free;
3535 ret = wimlib_extract_xml_data(wim, fp);
3537 imagex_error(T("Failed to close the file "
3543 goto out_wimlib_free;
3547 wimlib_print_available_images(wim, image);
3551 /* Modification operations */
3552 bool any_property_changes;
3554 if (image == WIMLIB_ALL_IMAGES)
3557 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3558 imagex_error(T("Cannot change image properties "
3559 "when using image 0"));
3561 goto out_wimlib_free;
3565 if (image == info.boot_index) {
3566 imagex_printf(T("Image %d is already marked as "
3567 "bootable.\n"), image);
3570 imagex_printf(T("Marking image %d as bootable.\n"),
3572 info.boot_index = image;
3573 ret = wimlib_set_wim_info(wim, &info,
3574 WIMLIB_CHANGE_BOOT_INDEX);
3576 goto out_wimlib_free;
3580 ret = apply_image_properties(&image_properties, wim, image,
3581 &any_property_changes);
3583 goto out_wimlib_free;
3585 /* Only call wimlib_overwrite() if something actually needs to
3587 if (boot || any_property_changes ||
3588 ((write_flags & WIMLIB_WRITE_FLAG_CHECK_INTEGRITY) &&
3589 !info.has_integrity_table) ||
3590 ((write_flags & WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY) &&
3591 info.has_integrity_table))
3593 ret = wimlib_overwrite(wim, write_flags, 1);
3595 imagex_printf(T("The file \"%"TS"\" was not modified "
3596 "because nothing needed to be done.\n"),
3604 string_list_destroy(&image_properties);
3608 usage(CMD_INFO, stderr);
3613 /* Join split WIMs into one part WIM */
3615 imagex_join(int argc, tchar **argv, int cmd)
3618 int swm_open_flags = 0;
3619 int wim_write_flags = 0;
3620 const tchar *output_path;
3623 for_opt(c, join_options) {
3625 case IMAGEX_CHECK_OPTION:
3626 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3628 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3629 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3639 imagex_error(T("Must specify one or more split WIM (.swm) "
3643 output_path = argv[0];
3644 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3649 imagex_progress_func,
3655 usage(CMD_JOIN, stderr);
3660 #if WIM_MOUNTING_SUPPORTED
3662 /* Mounts a WIM image. */
3664 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3667 int mount_flags = 0;
3669 const tchar *staging_dir = NULL;
3670 const tchar *wimfile;
3673 struct wimlib_wim_info info;
3677 STRING_LIST(refglobs);
3679 if (cmd == CMD_MOUNTRW) {
3680 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3681 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3684 for_opt(c, mount_options) {
3686 case IMAGEX_ALLOW_OTHER_OPTION:
3687 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3689 case IMAGEX_CHECK_OPTION:
3690 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3692 case IMAGEX_DEBUG_OPTION:
3693 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3695 case IMAGEX_STREAMS_INTERFACE_OPTION:
3696 if (!tstrcasecmp(optarg, T("none")))
3697 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3698 else if (!tstrcasecmp(optarg, T("xattr")))
3699 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3700 else if (!tstrcasecmp(optarg, T("windows")))
3701 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3703 imagex_error(T("Unknown stream interface \"%"TS"\""),
3708 case IMAGEX_REF_OPTION:
3709 ret = string_list_append(&refglobs, optarg);
3711 goto out_free_refglobs;
3713 case IMAGEX_STAGING_DIR_OPTION:
3714 staging_dir = optarg;
3716 case IMAGEX_UNIX_DATA_OPTION:
3717 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3725 if (argc != 2 && argc != 3)
3730 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3731 imagex_progress_func, NULL);
3733 goto out_free_refglobs;
3735 wimlib_get_wim_info(wim, &info);
3738 /* Image explicitly specified. */
3739 image = wimlib_resolve_image(wim, argv[1]);
3741 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3745 /* No image specified; default to image 1, but only if the WIM
3746 * contains exactly one image. */
3748 if (info.image_count != 1) {
3749 imagex_error(T("\"%"TS"\" contains %d images; Please "
3750 "select one."), wimfile, info.image_count);
3758 if (refglobs.num_strings) {
3759 ret = wim_reference_globs(wim, &refglobs, open_flags);
3764 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3766 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3767 do_metadata_not_found_warning(wimfile, &info);
3769 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3771 image, wimfile, dir);
3777 string_list_destroy(&refglobs);
3783 goto out_free_refglobs;
3785 #endif /* WIM_MOUNTING_SUPPORTED */
3787 /* Rebuild a WIM file */
3789 imagex_optimize(int argc, tchar **argv, int cmd)
3792 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3793 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3794 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3795 uint32_t chunk_size = UINT32_MAX;
3796 uint32_t solid_chunk_size = UINT32_MAX;
3797 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3800 const tchar *wimfile;
3803 unsigned num_threads = 0;
3805 for_opt(c, optimize_options) {
3807 case IMAGEX_CHECK_OPTION:
3808 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3810 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3811 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3813 case IMAGEX_NOCHECK_OPTION:
3814 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3816 case IMAGEX_COMPRESS_OPTION:
3817 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3818 compression_type = get_compression_type(optarg, false);
3819 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3822 case IMAGEX_RECOMPRESS_OPTION:
3823 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3825 case IMAGEX_CHUNK_SIZE_OPTION:
3826 chunk_size = parse_chunk_size(optarg);
3827 if (chunk_size == UINT32_MAX)
3830 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3831 solid_chunk_size = parse_chunk_size(optarg);
3832 if (solid_chunk_size == UINT32_MAX)
3835 case IMAGEX_SOLID_COMPRESS_OPTION:
3836 solid_ctype = get_compression_type(optarg, true);
3837 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3840 case IMAGEX_SOLID_OPTION:
3841 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3842 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3844 case IMAGEX_NO_SOLID_SORT_OPTION:
3845 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3847 case IMAGEX_THREADS_OPTION:
3848 num_threads = parse_num_threads(optarg);
3849 if (num_threads == UINT_MAX)
3852 case IMAGEX_PIPABLE_OPTION:
3853 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3855 case IMAGEX_NOT_PIPABLE_OPTION:
3856 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3858 case IMAGEX_UNSAFE_COMPACT_OPTION:
3859 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3873 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3874 imagex_progress_func, NULL);
3878 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3879 /* Change compression type. */
3880 ret = wimlib_set_output_compression_type(wim, compression_type);
3882 goto out_wimlib_free;
3885 if (chunk_size != UINT32_MAX) {
3886 /* Change chunk size. */
3887 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3889 goto out_wimlib_free;
3891 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3892 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3894 goto out_wimlib_free;
3896 if (solid_chunk_size != UINT32_MAX) {
3897 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3899 goto out_wimlib_free;
3902 old_size = file_get_size(wimfile);
3903 tprintf(T("\"%"TS"\" original size: "), wimfile);
3905 tputs(T("Unknown"));
3907 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3909 ret = wimlib_overwrite(wim, write_flags, num_threads);
3911 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3912 goto out_wimlib_free;
3915 new_size = file_get_size(wimfile);
3916 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3918 tputs(T("Unknown"));
3920 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3922 tfputs(T("Space saved: "), stdout);
3923 if (new_size != -1 && old_size != -1) {
3924 tprintf(T("%lld KiB\n"),
3925 ((long long)old_size - (long long)new_size) >> 10);
3927 tputs(T("Unknown"));
3936 usage(CMD_OPTIMIZE, stderr);
3942 /* Split a WIM into a spanned set */
3944 imagex_split(int argc, tchar **argv, int cmd)
3948 int write_flags = 0;
3949 unsigned long part_size;
3954 for_opt(c, split_options) {
3956 case IMAGEX_CHECK_OPTION:
3957 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3959 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
3960 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3972 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3973 if (tmp == argv[2] || *tmp) {
3974 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3975 imagex_error(T("The part size must be an integer or "
3976 "floating-point number of megabytes."));
3979 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3980 imagex_progress_func, NULL);
3984 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3990 usage(CMD_SPLIT, stderr);
3996 #if WIM_MOUNTING_SUPPORTED
3997 /* Unmounts a mounted WIM image. */
3999 imagex_unmount(int argc, tchar **argv, int cmd)
4002 int unmount_flags = 0;
4005 for_opt(c, unmount_options) {
4007 case IMAGEX_COMMIT_OPTION:
4008 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
4010 case IMAGEX_CHECK_OPTION:
4011 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
4013 case IMAGEX_REBUILD_OPTION:
4014 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
4016 case IMAGEX_LAZY_OPTION:
4017 case IMAGEX_FORCE_OPTION:
4018 /* Now, unmount is lazy by default. However, committing
4019 * the image will fail with
4020 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4021 * file descriptors on the WIM image. The
4022 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4023 * descriptors to be closed. */
4024 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4026 case IMAGEX_NEW_IMAGE_OPTION:
4027 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4038 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4039 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4040 imagex_error(T("--new-image is meaningless "
4041 "without --commit also specified!"));
4046 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4047 imagex_progress_func, NULL);
4049 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4050 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4052 "\tNote: Use --commit --force to force changes "
4053 "to be committed, regardless\n"
4054 "\t of open files.\n"));
4061 usage(CMD_UNMOUNT, stderr);
4066 #endif /* WIM_MOUNTING_SUPPORTED */
4069 * Add, delete, or rename files in a WIM image.
4072 imagex_update(int argc, tchar **argv, int cmd)
4074 const tchar *wimfile;
4078 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4079 int write_flags = 0;
4080 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4081 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4082 WIMLIB_ADD_FLAG_VERBOSE |
4083 WIMLIB_ADD_FLAG_WINCONFIG;
4084 int default_delete_flags = 0;
4085 unsigned num_threads = 0;
4087 tchar *cmd_file_contents;
4088 size_t cmd_file_nchars;
4089 struct wimlib_update_command *cmds;
4091 tchar *command_str = NULL;
4092 tchar *config_file = NULL;
4093 tchar *wimboot_config = NULL;
4095 for_opt(c, update_options) {
4097 /* Generic or write options */
4098 case IMAGEX_THREADS_OPTION:
4099 num_threads = parse_num_threads(optarg);
4100 if (num_threads == UINT_MAX)
4103 case IMAGEX_CHECK_OPTION:
4104 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4106 case IMAGEX_INCLUDE_INTEGRITY_OPTION:
4107 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4109 case IMAGEX_REBUILD_OPTION:
4110 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4112 case IMAGEX_COMMAND_OPTION:
4114 imagex_error(T("--command may only be specified "
4115 "one time. Please provide\n"
4116 " the update commands "
4117 "on standard input instead."));
4120 command_str = tstrdup(optarg);
4122 imagex_error(T("Out of memory!"));
4126 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4127 wimboot_config = optarg;
4129 /* Default delete options */
4130 case IMAGEX_FORCE_OPTION:
4131 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4133 case IMAGEX_RECURSIVE_OPTION:
4134 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4137 /* Global add option */
4138 case IMAGEX_CONFIG_OPTION:
4139 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4140 config_file = optarg;
4143 /* Default add options */
4144 case IMAGEX_VERBOSE_OPTION:
4145 /* No longer does anything. */
4147 case IMAGEX_DEREFERENCE_OPTION:
4148 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4150 case IMAGEX_UNIX_DATA_OPTION:
4151 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4153 case IMAGEX_NO_ACLS_OPTION:
4154 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4156 case IMAGEX_STRICT_ACLS_OPTION:
4157 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4159 case IMAGEX_NO_REPLACE_OPTION:
4160 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4162 case IMAGEX_UNSAFE_COMPACT_OPTION:
4163 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4172 if (argc != 1 && argc != 2)
4176 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4177 imagex_progress_func, NULL);
4179 goto out_free_command_str;
4182 /* Image explicitly specified. */
4183 image = wimlib_resolve_image(wim, argv[1]);
4184 ret = verify_image_exists_and_is_single(image, argv[1],
4187 goto out_wimlib_free;
4189 /* No image specified; default to image 1, but only if the WIM
4190 * contains exactly one image. */
4191 struct wimlib_wim_info info;
4193 wimlib_get_wim_info(wim, &info);
4194 if (info.image_count != 1) {
4195 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4196 wimfile, info.image_count);
4203 /* Read update commands from standard input, or the command string if
4206 cmd_file_contents = NULL;
4207 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4211 goto out_free_cmd_file_contents;
4213 } else if (!wimboot_config) {
4214 if (isatty(STDIN_FILENO)) {
4215 tputs(T("Reading update commands from standard input..."));
4216 recommend_man_page(CMD_UPDATE, stdout);
4218 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4219 if (!cmd_file_contents) {
4221 goto out_wimlib_free;
4224 /* Parse the update commands */
4225 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4229 goto out_free_cmd_file_contents;
4232 cmd_file_contents = NULL;
4237 /* Set default flags and capture config on the update commands */
4238 for (size_t i = 0; i < num_cmds; i++) {
4239 switch (cmds[i].op) {
4240 case WIMLIB_UPDATE_OP_ADD:
4241 cmds[i].add.add_flags |= default_add_flags;
4242 cmds[i].add.config_file = config_file;
4244 case WIMLIB_UPDATE_OP_DELETE:
4245 cmds[i].delete_.delete_flags |= default_delete_flags;
4252 /* Execute the update commands */
4253 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4257 if (wimboot_config) {
4258 /* --wimboot-config=FILE is short for an
4259 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4261 struct wimlib_update_command cmd;
4263 cmd.op = WIMLIB_UPDATE_OP_ADD;
4264 cmd.add.fs_source_path = wimboot_config;
4265 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4266 cmd.add.config_file = NULL;
4267 cmd.add.add_flags = 0;
4269 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4274 /* Overwrite the updated WIM */
4275 ret = wimlib_overwrite(wim, write_flags, num_threads);
4278 out_free_cmd_file_contents:
4279 free(cmd_file_contents);
4282 out_free_command_str:
4287 usage(CMD_UPDATE, stderr);
4290 goto out_free_command_str;
4293 /* Verify a WIM file. */
4295 imagex_verify(int argc, tchar **argv, int cmd)
4298 const tchar *wimfile;
4300 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4301 int verify_flags = 0;
4302 STRING_LIST(refglobs);
4305 for_opt(c, verify_options) {
4307 case IMAGEX_REF_OPTION:
4308 ret = string_list_append(&refglobs, optarg);
4310 goto out_free_refglobs;
4312 case IMAGEX_NOCHECK_OPTION:
4313 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4325 imagex_error(T("Must specify a WIM file!"));
4327 imagex_error(T("At most one WIM file can be specified!"));
4333 ret = wimlib_open_wim_with_progress(wimfile,
4336 imagex_progress_func,
4339 goto out_free_refglobs;
4341 ret = wim_reference_globs(wim, &refglobs, open_flags);
4343 goto out_wimlib_free;
4345 ret = wimlib_verify_wim(wim, verify_flags);
4347 tputc(T('\n'), stderr);
4348 imagex_error(T("\"%"TS"\" failed verification!"),
4350 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4351 refglobs.num_strings == 0)
4353 imagex_printf(T("Note: if this WIM file is not standalone, "
4354 "use the --ref option to specify the other parts.\n"));
4357 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4364 string_list_destroy(&refglobs);
4368 usage(CMD_VERIFY, stderr);
4370 goto out_free_refglobs;
4373 struct imagex_command {
4375 int (*func)(int argc, tchar **argv, int cmd);
4378 static const struct imagex_command imagex_commands[] = {
4379 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4380 [CMD_APPLY] = {T("apply"), imagex_apply},
4381 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4382 [CMD_DELETE] = {T("delete"), imagex_delete},
4383 [CMD_DIR ] = {T("dir"), imagex_dir},
4384 [CMD_EXPORT] = {T("export"), imagex_export},
4385 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4386 [CMD_INFO] = {T("info"), imagex_info},
4387 [CMD_JOIN] = {T("join"), imagex_join},
4388 #if WIM_MOUNTING_SUPPORTED
4389 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4390 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4392 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4393 [CMD_SPLIT] = {T("split"), imagex_split},
4394 #if WIM_MOUNTING_SUPPORTED
4395 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4397 [CMD_UPDATE] = {T("update"), imagex_update},
4398 [CMD_VERIFY] = {T("verify"), imagex_verify},
4403 /* Can be a directory or source list file. But source list file is probably
4404 * a rare use case, so just say directory. */
4405 # define SOURCE_STR T("DIRECTORY")
4407 /* Can only be a directory */
4408 # define TARGET_STR T("DIRECTORY")
4411 /* Can be a directory, NTFS volume, or source list file. */
4412 # define SOURCE_STR T("SOURCE")
4414 /* Can be a directory or NTFS volume. */
4415 # define TARGET_STR T("TARGET")
4419 static const tchar * const usage_strings[] = {
4422 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4423 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4424 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4425 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4426 " [--delta-from=WIMFILE] [--wimboot] [--unix-data]\n"
4427 " [--dereference] [--snapshot]\n"
4431 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4432 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4433 " [--no-attributes] [--rpfix] [--norpfix]\n"
4434 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4435 " [--compact=FORMAT]\n"
4439 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4440 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4441 " [--config=FILE] [--threads=NUM_THREADS]\n"
4442 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4443 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4444 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4449 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4453 " %"TS" WIMFILE [IMAGE] [--path=PATH] [--detailed]\n"
4457 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4458 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4459 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4460 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4461 " [--wimboot] [--solid]\n"
4465 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4466 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4467 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4468 " [--no-attributes] [--include-invalid-names]\n"
4469 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4473 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4474 " [--boot] [--check] [--nocheck] [--xml]\n"
4475 " [--extract-xml FILE] [--header] [--blobs]\n"
4476 " [--image-property NAME=VALUE]\n"
4480 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4482 #if WIM_MOUNTING_SUPPORTED
4485 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4486 " [--check] [--streams-interface=INTERFACE]\n"
4487 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4491 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4492 " [--check] [--streams-interface=INTERFACE]\n"
4493 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4499 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4500 " [--check] [--nocheck] [--solid]\n"
4505 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4507 #if WIM_MOUNTING_SUPPORTED
4510 " %"TS" DIRECTORY\n"
4511 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4516 " %"TS" WIMFILE [IMAGE]\n"
4517 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4518 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4519 " [--command=STRING] [--wimboot-config=FILE]\n"
4524 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4528 static const tchar *invocation_name;
4529 static int invocation_cmd = CMD_NONE;
4531 static const tchar *get_cmd_string(int cmd, bool only_short_form)
4533 static tchar buf[50];
4535 if (cmd == CMD_NONE)
4536 return T("wimlib-imagex");
4538 if (only_short_form || invocation_cmd != CMD_NONE) {
4539 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4541 tsprintf(buf, T("%"TS" %"TS), invocation_name,
4542 imagex_commands[cmd].name);
4550 static const tchar * const fmt =
4552 "wimlib-imagex " PACKAGE_VERSION " (using wimlib %"TS")\n"
4553 "Copyright (C) 2012-2018 Eric Biggers\n"
4554 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4555 "This is free software: you are free to change and redistribute it.\n"
4556 "There is NO WARRANTY, to the extent permitted by law.\n"
4558 "Report bugs to "PACKAGE_BUGREPORT".\n"
4560 tfprintf(stdout, fmt, wimlib_get_version_string());
4564 do_common_options(int *argc_p, tchar **argv, int cmd)
4570 for (i = 1; i < argc; i++) {
4572 if (p[0] == T('-') && p[1] == T('-')) {
4574 if (!tstrcmp(p, T("help"))) {
4575 if (cmd == CMD_NONE)
4580 } else if (!tstrcmp(p, T("version"))) {
4583 } else if (!tstrcmp(p, T("quiet"))) {
4584 imagex_suppress_output();
4585 memmove(&argv[i], &argv[i + 1],
4586 (argc - i) * sizeof(argv[i]));
4589 } else if (!*p) /* reached "--", no more options */
4598 print_usage_string(int cmd, FILE *fp)
4600 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4604 recommend_man_page(int cmd, FILE *fp)
4606 const tchar *format_str;
4608 format_str = T("Some uncommon options are not listed;\n"
4609 "See %"TS".pdf in the doc directory for more details.\n");
4611 format_str = T("Some uncommon options are not listed; see `man %"TS"' for more details.\n");
4613 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4617 usage(int cmd, FILE *fp)
4619 tfprintf(fp, T("Usage:\n"));
4620 print_usage_string(cmd, fp);
4621 tfprintf(fp, T("\n"));
4622 recommend_man_page(cmd, fp);
4628 tfprintf(fp, T("Usage:\n"));
4629 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4630 print_usage_string(cmd, fp);
4631 tfprintf(fp, T("\n"));
4633 static const tchar * const extra =
4636 " %"TS" --version\n"
4639 tfprintf(fp, extra, invocation_name, invocation_name);
4641 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4642 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4643 "For some commands IMAGE may be \"all\".\n"
4645 recommend_man_page(CMD_NONE, fp);
4649 extern int wmain(int argc, wchar_t **argv);
4653 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4654 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4655 * something else), while on Windows the command arguments will be UTF-16LE
4656 * encoded 'wchar_t' strings. */
4658 main(int argc, tchar **argv)
4664 imagex_info_file = stdout;
4665 invocation_name = tbasename(argv[0]);
4668 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4669 if (igcase != NULL) {
4670 if (!tstrcmp(igcase, T("no")) ||
4671 !tstrcmp(igcase, T("0")))
4672 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4673 else if (!tstrcmp(igcase, T("yes")) ||
4674 !tstrcmp(igcase, T("1")))
4675 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4678 "WARNING: Ignoring unknown setting of "
4679 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4684 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4686 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4687 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4688 for (int i = 0; i < CMD_MAX; i++) {
4689 if (!tstrcmp(invocation_name + 3,
4690 imagex_commands[i].name))
4699 /* Unless already known from the invocation name, determine which
4700 * command was specified. */
4701 if (cmd == CMD_NONE) {
4703 imagex_error(T("No command specified!\n"));
4707 for (int i = 0; i < CMD_MAX; i++) {
4708 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4713 if (cmd != CMD_NONE) {
4719 /* Handle common options. May exit early (for --help or --version). */
4720 do_common_options(&argc, argv, cmd);
4722 /* Bail if a valid command was not specified. */
4723 if (cmd == CMD_NONE) {
4724 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4729 /* Enable warning and error messages in wimlib to be more user-friendly.
4731 wimlib_set_print_errors(true);
4733 /* Initialize wimlib. */
4734 ret = wimlib_global_init(init_flags);
4736 goto out_check_status;
4738 /* Call the command handler function. */
4739 ret = imagex_commands[cmd].func(argc, argv, cmd);
4741 /* Check for error writing to standard output, especially since for some
4742 * commands, writing to standard output is part of the program's actual
4743 * behavior and not just for informational purposes. */
4744 if (ferror(stdout) || fclose(stdout)) {
4745 imagex_error_with_errno(T("error writing to standard output"));
4750 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4751 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4752 * error code from which an error message can be printed. */
4754 imagex_error(T("Exiting with error code %d:\n"
4756 wimlib_get_error_string(ret));
4757 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4758 imagex_error_with_errno(T("errno"));
4760 /* Make wimlib free any resources it's holding (although this is not
4761 * strictly necessary because the process is ending anyway). */
4762 wimlib_global_cleanup();