4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012-2016 Eric Biggers
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # include "config.h" /* Need for PACKAGE_VERSION, etc. */
30 #include "wimlib_tchar.h"
49 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
52 # include "imagex-win32.h"
53 # define print_security_descriptor win32_print_security_descriptor
56 # include <langinfo.h>
57 # define print_security_descriptor default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
63 /* Don't confuse the user by presenting the mounting commands on Windows when
64 * they will never work. However on UNIX-like systems we always present them,
65 * even if WITH_FUSE is not defined at this point, as to not tie the build of
66 * wimlib-imagex to a specific build of wimlib. */
68 # define WIM_MOUNTING_SUPPORTED 0
70 # define WIM_MOUNTING_SUPPORTED 1
73 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
76 is_any_path_separator(tchar c)
78 return c == T('/') || c == T('\\');
81 /* Like basename(), but handles both forward and backwards slashes. */
83 tbasename(tchar *path)
85 tchar *p = tstrchr(path, T('\0'));
90 if (!is_any_path_separator(*--p))
98 if (is_any_path_separator(*--p))
103 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
117 #if WIM_MOUNTING_SUPPORTED
123 #if WIM_MOUNTING_SUPPORTED
131 static void usage(int cmd, FILE *fp);
132 static void usage_all(FILE *fp);
133 static void recommend_man_page(int cmd, FILE *fp);
134 static const tchar *get_cmd_string(int cmd, bool nospace);
136 static bool imagex_be_quiet = false;
137 static FILE *imagex_info_file;
139 #define imagex_printf(format, ...) \
140 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
143 IMAGEX_ALLOW_OTHER_OPTION,
147 IMAGEX_CHUNK_SIZE_OPTION,
148 IMAGEX_COMMAND_OPTION,
149 IMAGEX_COMMIT_OPTION,
150 IMAGEX_COMPACT_OPTION,
151 IMAGEX_COMPRESS_OPTION,
152 IMAGEX_COMPRESS_SLOW_OPTION,
153 IMAGEX_CONFIG_OPTION,
155 IMAGEX_DELTA_FROM_OPTION,
156 IMAGEX_DEREFERENCE_OPTION,
157 IMAGEX_DEST_DIR_OPTION,
158 IMAGEX_DETAILED_OPTION,
159 IMAGEX_EXTRACT_XML_OPTION,
162 IMAGEX_HEADER_OPTION,
163 IMAGEX_IMAGE_PROPERTY_OPTION,
164 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
166 IMAGEX_METADATA_OPTION,
167 IMAGEX_NEW_IMAGE_OPTION,
168 IMAGEX_NOCHECK_OPTION,
169 IMAGEX_NORPFIX_OPTION,
170 IMAGEX_NOT_PIPABLE_OPTION,
171 IMAGEX_NO_ACLS_OPTION,
172 IMAGEX_NO_ATTRIBUTES_OPTION,
173 IMAGEX_NO_GLOBS_OPTION,
174 IMAGEX_NO_REPLACE_OPTION,
175 IMAGEX_NO_SOLID_SORT_OPTION,
176 IMAGEX_NULLGLOB_OPTION,
177 IMAGEX_ONE_FILE_ONLY_OPTION,
179 IMAGEX_PIPABLE_OPTION,
180 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
181 IMAGEX_REBUILD_OPTION,
182 IMAGEX_RECOMPRESS_OPTION,
183 IMAGEX_RECURSIVE_OPTION,
186 IMAGEX_SNAPSHOT_OPTION,
188 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
189 IMAGEX_SOLID_COMPRESS_OPTION,
191 IMAGEX_SOURCE_LIST_OPTION,
192 IMAGEX_STAGING_DIR_OPTION,
193 IMAGEX_STREAMS_INTERFACE_OPTION,
194 IMAGEX_STRICT_ACLS_OPTION,
195 IMAGEX_THREADS_OPTION,
196 IMAGEX_TO_STDOUT_OPTION,
197 IMAGEX_UNIX_DATA_OPTION,
198 IMAGEX_UNSAFE_COMPACT_OPTION,
199 IMAGEX_UPDATE_OF_OPTION,
200 IMAGEX_VERBOSE_OPTION,
201 IMAGEX_WIMBOOT_CONFIG_OPTION,
202 IMAGEX_WIMBOOT_OPTION,
206 static const struct option apply_options[] = {
207 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
208 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
209 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
210 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
211 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
212 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
213 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
214 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
215 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
216 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
217 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
218 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
219 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
223 static const struct option capture_or_append_options[] = {
224 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
225 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
226 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
227 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
228 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
229 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
230 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
231 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
232 {T("pack-streams"), no_argument, NULL, IMAGEX_SOLID_OPTION},
233 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
234 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
235 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
236 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
237 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
238 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
239 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
240 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
241 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
242 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
243 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
244 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
245 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
246 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
247 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
248 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
249 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
250 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
251 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
252 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
253 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
254 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
255 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
256 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
257 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
258 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
262 static const struct option delete_options[] = {
263 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
264 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
265 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
269 static const struct option dir_options[] = {
270 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
271 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
272 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
273 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
277 static const struct option export_options[] = {
278 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
279 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
280 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
281 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
282 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
283 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
284 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
285 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
286 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
287 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
288 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
289 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
290 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
291 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
292 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
293 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
294 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
295 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
296 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
297 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
298 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
299 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
303 static const struct option extract_options[] = {
304 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
305 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
306 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
307 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
308 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
309 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
310 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
311 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
312 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
313 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
314 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
315 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
316 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
317 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
318 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
319 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
320 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
324 static const struct option info_options[] = {
325 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
326 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
327 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
328 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
329 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
330 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
331 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
332 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
333 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
334 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
335 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
339 static const struct option join_options[] = {
340 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
344 static const struct option mount_options[] = {
345 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
346 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
347 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
348 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
349 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
350 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
351 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
355 static const struct option optimize_options[] = {
356 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
357 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
358 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
359 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
360 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
361 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
362 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
363 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
364 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
365 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
366 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
367 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
368 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
369 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
370 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
371 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
372 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
373 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
374 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
378 static const struct option split_options[] = {
379 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
383 static const struct option unmount_options[] = {
384 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
385 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
386 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
387 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
388 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
389 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
393 static const struct option update_options[] = {
394 /* Careful: some of the options here set the defaults for update
395 * commands, but the flags given to an actual update command (and not to
396 * `imagex update' itself are also handled in
397 * update_command_add_option(). */
398 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
399 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
400 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
401 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
402 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
404 /* Default delete options */
405 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
406 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
408 /* Global add option */
409 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
411 /* Default add options */
412 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
413 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
414 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
415 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
416 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
417 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
418 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
419 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
424 static const struct option verify_options[] = {
425 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
426 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
432 # define _format_attribute(type, format_str, args_start) \
433 __attribute__((format(type, format_str, args_start)))
435 # define _format_attribute(type, format_str, args_start)
438 /* Print formatted error message to stderr. */
439 static void _format_attribute(printf, 1, 2)
440 imagex_error(const tchar *format, ...)
443 va_start(va, format);
444 tfputs(T("ERROR: "), stderr);
445 tvfprintf(stderr, format, va);
446 tputc(T('\n'), stderr);
450 /* Print formatted error message to stderr. */
451 static void _format_attribute(printf, 1, 2)
452 imagex_error_with_errno(const tchar *format, ...)
454 int errno_save = errno;
456 va_start(va, format);
457 tfputs(T("ERROR: "), stderr);
458 tvfprintf(stderr, format, va);
459 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
464 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
466 if (image == WIMLIB_NO_IMAGE) {
467 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
468 " Please specify a 1-based image index or "
469 "image name. To list the images\n"
470 " contained in the WIM archive, run\n"
472 " %"TS" \"%"TS"\"\n"),
473 image_name, wim_name,
474 get_cmd_string(CMD_INFO, false), wim_name);
475 return WIMLIB_ERR_INVALID_IMAGE;
481 verify_image_is_single(int image)
483 if (image == WIMLIB_ALL_IMAGES) {
484 imagex_error(T("Cannot specify all images for this action!"));
485 return WIMLIB_ERR_INVALID_IMAGE;
491 verify_image_exists_and_is_single(int image, const tchar *image_name,
492 const tchar *wim_name)
495 ret = verify_image_exists(image, image_name, wim_name);
497 ret = verify_image_is_single(image);
502 print_available_compression_types(FILE *fp)
504 static const tchar *s =
506 "Available compression types:\n"
509 " xpress (alias: \"fast\")\n"
510 " lzx (alias: \"maximum\") (default for capture)\n"
511 " lzms (alias: \"recovery\")\n"
517 /* Parse the argument to --compress or --solid-compress */
519 get_compression_type(tchar *optarg, bool solid)
522 unsigned int compression_level = 0;
525 plevel = tstrchr(optarg, T(':'));
531 ultmp = tstrtoul(plevel, &ptmp, 10);
532 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
533 imagex_error(T("Compression level must be a positive integer! "
534 "e.g. --compress=lzx:80"));
535 return WIMLIB_COMPRESSION_TYPE_INVALID;
537 compression_level = ultmp;
540 if (!tstrcasecmp(optarg, T("maximum")) ||
541 !tstrcasecmp(optarg, T("lzx")) ||
542 !tstrcasecmp(optarg, T("max"))) {
543 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
544 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
545 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
546 } else if (!tstrcasecmp(optarg, T("recovery"))) {
550 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
551 " differently from DISM. Instead, you typically want to use '--solid' to\n"
552 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
553 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
554 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
555 " of '--compress=recovery'.\n"));
557 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
558 } else if (!tstrcasecmp(optarg, T("lzms"))) {
559 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
560 } else if (!tstrcasecmp(optarg, T("none"))) {
561 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
563 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
564 print_available_compression_types(stderr);
565 return WIMLIB_COMPRESSION_TYPE_INVALID;
568 if (compression_level != 0)
569 wimlib_set_default_compression_level(ctype, compression_level);
573 /* Parse the argument to --compact */
575 set_compact_mode(const tchar *arg, int *extract_flags)
578 if (!tstrcasecmp(arg, T("xpress4k")))
579 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
580 else if (!tstrcasecmp(arg, T("xpress8k")))
581 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
582 else if (!tstrcasecmp(arg, T("xpress16k")))
583 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
584 else if (!tstrcasecmp(arg, T("lzx")))
585 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
588 *extract_flags |= flag;
593 "\"%"TS"\" is not a recognized System Compression format. The options are:"
595 " --compact=xpress4k\n"
596 " --compact=xpress8k\n"
597 " --compact=xpress16k\n"
605 set_compress_slow(void)
608 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
609 " Use the '--compress=TYPE:LEVEL' option instead.\n");
611 wimlib_set_default_compression_level(-1, 100);
616 unsigned num_strings;
617 unsigned num_alloc_strings;
620 #define STRING_SET_INITIALIZER \
621 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
623 #define STRING_SET(_strings) \
624 struct string_set _strings = STRING_SET_INITIALIZER
627 string_set_append(struct string_set *set, tchar *glob)
629 unsigned num_alloc_strings = set->num_alloc_strings;
631 if (set->num_strings == num_alloc_strings) {
634 num_alloc_strings += 4;
635 new_strings = realloc(set->strings,
636 sizeof(set->strings[0]) * num_alloc_strings);
638 imagex_error(T("Out of memory!"));
641 set->strings = new_strings;
642 set->num_alloc_strings = num_alloc_strings;
644 set->strings[set->num_strings++] = glob;
649 string_set_destroy(struct string_set *set)
655 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
657 return wimlib_reference_resource_files(wim, (const tchar **)set->strings,
659 WIMLIB_REF_FLAG_GLOB_ENABLE,
664 append_image_property_argument(struct string_set *image_properties)
666 if (!tstrchr(optarg, '=')) {
667 imagex_error(T("'--image-property' argument "
668 "must be in the form NAME=VALUE"));
671 return string_set_append(image_properties, optarg);
675 apply_image_properties(struct string_set *image_properties,
676 WIMStruct *wim, int image, bool *any_changes_ret)
678 bool any_changes = false;
679 for (unsigned i = 0; i < image_properties->num_strings; i++) {
681 const tchar *current_value;
684 name = image_properties->strings[i];
685 value = tstrchr(name, '=');
688 current_value = wimlib_get_image_property(wim, image, name);
689 if (current_value && !tstrcmp(current_value, value)) {
690 imagex_printf(T("The %"TS" property of image %d "
691 "already has value \"%"TS"\".\n"),
694 imagex_printf(T("Setting the %"TS" property of image "
695 "%d to \"%"TS"\".\n"),
697 ret = wimlib_set_image_property(wim, image, name, value);
704 *any_changes_ret = any_changes;
709 do_resource_not_found_warning(const tchar *wimfile,
710 const struct wimlib_wim_info *info,
711 const struct string_set *refglobs)
713 if (info->total_parts > 1) {
714 if (refglobs->num_strings == 0) {
715 imagex_error(T("\"%"TS"\" is part of a split WIM. "
716 "Use --ref to specify the other parts."),
719 imagex_error(T("Perhaps the '--ref' argument did not "
720 "specify all other parts of the split "
724 imagex_error(T("If this is a delta WIM, use the --ref argument "
725 "to specify the WIM(s) on which it is based."));
730 do_metadata_not_found_warning(const tchar *wimfile,
731 const struct wimlib_wim_info *info)
733 if (info->part_number != 1) {
734 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
735 " You must specify the first part."),
740 /* Returns the size of a file given its name, or -1 if the file does not exist
741 * or its size cannot be determined. */
743 file_get_size(const tchar *filename)
746 if (tstat(filename, &st) == 0)
753 PARSE_STRING_SUCCESS = 0,
754 PARSE_STRING_FAILURE = 1,
755 PARSE_STRING_NONE = 2,
759 * Parses a string token from an array of characters.
761 * Tokens are either whitespace-delimited, or double or single-quoted.
763 * @line_p: Pointer to the pointer to the line of data. Will be updated
764 * to point past the string token iff the return value is
765 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
768 * @len_p: @len_p initially stores the length of the line of data, which may
769 * be 0, and it will be updated to the number of bytes remaining in
770 * the line iff the return value is PARSE_STRING_SUCCESS.
772 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
773 * parsed string token will be returned here.
775 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
776 * PARSE_STRING_FAILURE if the data was invalid due to a missing
777 * closing quote; or PARSE_STRING_NONE if the line ended before the
778 * beginning of a string token was found.
781 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
784 tchar *line = *line_p;
788 /* Skip leading whitespace */
791 return PARSE_STRING_NONE;
792 if (!istspace(*line) && *line != T('\0'))
798 if (quote_char == T('"') || quote_char == T('\'')) {
803 line = tmemchr(line, quote_char, len);
805 imagex_error(T("Missing closing quote: %"TS), fn - 1);
806 return PARSE_STRING_FAILURE;
809 /* Unquoted string. Go until whitespace. Line is terminated
810 * by '\0', so no need to check 'len'. */
814 } while (!istspace(*line) && *line != T('\0'));
821 return PARSE_STRING_SUCCESS;
824 /* Parses a line of data (not an empty line or comment) in the source list file
825 * format. (See the man page for 'wimlib-imagex capture' for details on this
826 * format and the meaning.)
828 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
829 * len == 0. The data in @line will be modified by this function call.
831 * @len: Length of the line of data.
833 * @source: On success, the capture source and target described by the line is
834 * written into this destination. Note that it will contain pointers
835 * to data in the @line array.
837 * Returns true if the line was valid; false otherwise. */
839 parse_source_list_line(tchar *line, size_t len,
840 struct wimlib_capture_source *source)
844 ret = parse_string(&line, &len, &source->fs_source_path);
845 if (ret != PARSE_STRING_SUCCESS)
847 ret = parse_string(&line, &len, &source->wim_target_path);
848 if (ret == PARSE_STRING_NONE)
849 source->wim_target_path = source->fs_source_path;
850 return ret != PARSE_STRING_FAILURE;
853 /* Returns %true if the given line of length @len > 0 is a comment or empty line
854 * in the source list file format. */
856 is_comment_line(const tchar *line, size_t len)
859 if (*line == T('#') || *line == T(';'))
861 if (!istspace(*line) && *line != T('\0'))
871 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
874 tchar *contents = *contents_p;
875 size_t nchars = *nchars_p;
878 for (i = 0; i < nchars; i++)
879 if (contents[i] == T('\n'))
882 /* Handle last line not terminated by a newline */
883 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
884 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
886 imagex_error(T("Out of memory!"));
889 contents[nchars] = T('\n');
890 *contents_p = contents;
898 /* Parses a file in the source list format. (See the man page for
899 * 'wimlib-imagex capture' for details on this format and the meaning.)
901 * @source_list_contents: Contents of the source list file. Note that this
902 * buffer will be modified to save memory allocations,
903 * and cannot be freed until the returned array of
904 * wimlib_capture_source's has also been freed.
906 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
909 * @nsources_ret: On success, the length of the returned array is
912 * Returns: An array of `struct wimlib_capture_source's that can be passed to
913 * the wimlib_add_image_multisource() function to specify how a WIM image is to
915 static struct wimlib_capture_source *
916 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
917 size_t *nsources_ret)
921 struct wimlib_capture_source *sources;
924 nlines = text_file_count_lines(source_list_contents_p,
925 &source_list_nchars);
929 /* Always allocate at least 1 slot, just in case the implementation of
930 * calloc() returns NULL if 0 bytes are requested. */
931 sources = calloc(nlines ?: 1, sizeof(*sources));
933 imagex_error(T("out of memory"));
936 p = *source_list_contents_p;
938 for (i = 0; i < nlines; i++) {
939 /* XXX: Could use rawmemchr() here instead, but it may not be
940 * available on all platforms. */
941 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
942 size_t len = endp - p + 1;
944 if (!is_comment_line(p, len)) {
945 if (!parse_source_list_line(p, len, &sources[j++])) {
957 /* Reads the contents of a file into memory. */
959 file_get_contents(const tchar *filename, size_t *len_ret)
966 if (tstat(filename, &stbuf) != 0) {
967 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
972 fp = tfopen(filename, T("rb"));
974 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
978 buf = malloc(len ? len : 1);
980 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
981 "contents of file \"%"TS"\""), len, filename);
984 if (fread(buf, 1, len, fp) != len) {
985 imagex_error_with_errno(T("Failed to read %zu bytes from the "
986 "file \"%"TS"\""), len, filename);
1000 /* Read standard input until EOF and return the full contents in a malloc()ed
1001 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1004 stdin_get_contents(size_t *len_ret)
1006 /* stdin can, of course, be a pipe or other non-seekable file, so the
1007 * total length of the data cannot be pre-determined */
1009 size_t newlen = 1024;
1013 char *p = realloc(buf, newlen);
1014 size_t bytes_read, bytes_to_read;
1016 imagex_error(T("out of memory while reading stdin"));
1020 bytes_to_read = newlen - pos;
1021 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1023 if (bytes_read != bytes_to_read) {
1028 imagex_error_with_errno(T("error reading stdin"));
1042 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1045 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1047 *num_tchars_ret = num_bytes;
1049 #else /* !__WIN32__ */
1050 /* On Windows, translate the text to UTF-16LE */
1054 if (num_bytes >= 2 &&
1055 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1056 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1058 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1059 * with something that looks like an ASCII character encoded as
1060 * a UTF-16LE code unit. Assume the file is encoded as
1061 * UTF-16LE. This is not a 100% reliable check. */
1062 num_wchars = num_bytes / 2;
1063 text_wstr = (wchar_t*)text;
1065 /* File does not look like UTF-16LE. Assume it is encoded in
1066 * the current Windows code page. I think these are always
1067 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1068 * should work as expected. */
1069 text_wstr = win32_mbs_to_wcs(text,
1074 *num_tchars_ret = num_wchars;
1076 #endif /* __WIN32__ */
1080 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1085 contents = file_get_contents(filename, &num_bytes);
1088 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1092 stdin_get_text_contents(size_t *num_tchars_ret)
1097 contents = stdin_get_contents(&num_bytes);
1100 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1103 #define TO_PERCENT(numerator, denominator) \
1104 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1106 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1107 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1108 #define KIBIBYTE_MIN_NBYTES 10000ULL
1111 get_unit(uint64_t total_bytes, const tchar **name_ret)
1113 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1114 *name_ret = T("GiB");
1116 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1117 *name_ret = T("MiB");
1119 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1120 *name_ret = T("KiB");
1123 *name_ret = T("bytes");
1128 static struct wimlib_progress_info_scan last_scan_progress;
1131 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1133 uint64_t prev_count, cur_count;
1135 prev_count = last_scan_progress.num_nondirs_scanned +
1136 last_scan_progress.num_dirs_scanned;
1137 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1139 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1140 cur_count % 128 == 0)
1142 unsigned unit_shift;
1143 const tchar *unit_name;
1145 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1146 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1147 "%"PRIu64" directories) "),
1148 scan->num_bytes_scanned >> unit_shift,
1150 scan->num_nondirs_scanned,
1151 scan->num_dirs_scanned);
1152 last_scan_progress = *scan;
1155 /* Progress callback function passed to various wimlib functions. */
1156 static enum wimlib_progress_status
1157 imagex_progress_func(enum wimlib_progress_msg msg,
1158 union wimlib_progress_info *info,
1159 void *_ignored_context)
1161 unsigned percent_done;
1162 unsigned unit_shift;
1163 const tchar *unit_name;
1165 if (imagex_be_quiet)
1166 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1168 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1170 static bool started;
1172 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1173 imagex_printf(T("Using %"TS" compression "
1174 "with %u thread%"TS"\n"),
1175 wimlib_get_compression_type_string(
1176 info->write_streams.compression_type),
1177 info->write_streams.num_threads,
1178 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1183 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1184 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1185 info->write_streams.total_bytes);
1187 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1188 info->write_streams.completed_bytes >> unit_shift,
1190 info->write_streams.total_bytes >> unit_shift,
1193 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1194 imagex_printf(T("\n"));
1196 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1197 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1198 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1199 imagex_printf(T("\n"));
1201 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1202 info->scan.wim_target_path);
1204 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1206 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1207 switch (info->scan.status) {
1208 case WIMLIB_SCAN_DENTRY_OK:
1209 report_scan_progress(&info->scan, false);
1211 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1212 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1214 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1215 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1216 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1218 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1219 /* Symlink fixups are enabled by default. This is
1220 * mainly intended for Windows, which for some reason
1221 * uses absolute junctions (with drive letters!) in the
1222 * default installation. On UNIX-like systems, warn the
1223 * user when fixing the target of an absolute symbolic
1224 * link, so they know to disable this if they want. */
1226 imagex_printf(T("\nWARNING: Adjusted target of "
1227 "absolute symbolic link \"%"TS"\"\n"
1228 " (Use --norpfix to capture "
1229 "absolute symbolic links as-is)\n"),
1230 info->scan.cur_path);
1237 case WIMLIB_PROGRESS_MSG_SCAN_END:
1238 report_scan_progress(&info->scan, true);
1239 imagex_printf(T("\n"));
1241 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1242 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1243 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1244 info->integrity.total_bytes);
1245 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1246 "of %"PRIu64" %"TS" (%u%%) done"),
1247 info->integrity.filename,
1248 info->integrity.completed_bytes >> unit_shift,
1250 info->integrity.total_bytes >> unit_shift,
1253 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1254 imagex_printf(T("\n"));
1256 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1257 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1258 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1259 info->integrity.total_bytes);
1260 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1261 "of %"PRIu64" %"TS" (%u%%) done"),
1262 info->integrity.completed_bytes >> unit_shift,
1264 info->integrity.total_bytes >> unit_shift,
1267 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1268 imagex_printf(T("\n"));
1270 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1271 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1272 "to %"TS" \"%"TS"\"\n"),
1273 info->extract.image,
1274 info->extract.image_name,
1275 info->extract.wimfile_name,
1276 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1277 T("NTFS volume") : T("directory")),
1278 info->extract.target);
1280 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1281 if (info->extract.end_file_count >= 2000) {
1282 percent_done = TO_PERCENT(info->extract.current_file_count,
1283 info->extract.end_file_count);
1284 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1285 info->extract.current_file_count,
1286 info->extract.end_file_count, percent_done);
1287 if (info->extract.current_file_count == info->extract.end_file_count)
1288 imagex_printf(T("\n"));
1291 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1292 percent_done = TO_PERCENT(info->extract.completed_bytes,
1293 info->extract.total_bytes);
1294 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1295 imagex_printf(T("\rExtracting file data: "
1296 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1297 info->extract.completed_bytes >> unit_shift,
1299 info->extract.total_bytes >> unit_shift,
1302 if (info->extract.completed_bytes >= info->extract.total_bytes)
1303 imagex_printf(T("\n"));
1305 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1306 if (info->extract.end_file_count >= 2000) {
1307 percent_done = TO_PERCENT(info->extract.current_file_count,
1308 info->extract.end_file_count);
1309 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1310 info->extract.current_file_count,
1311 info->extract.end_file_count, percent_done);
1312 if (info->extract.current_file_count == info->extract.end_file_count)
1313 imagex_printf(T("\n"));
1316 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1317 if (info->extract.total_parts != 1) {
1318 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1319 info->extract.part_number,
1320 info->extract.total_parts);
1323 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1324 percent_done = TO_PERCENT(info->split.completed_bytes,
1325 info->split.total_bytes);
1326 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1327 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1328 "%"PRIu64" %"TS" (%u%%) written\n"),
1329 info->split.part_name,
1330 info->split.cur_part_number,
1331 info->split.total_parts,
1332 info->split.completed_bytes >> unit_shift,
1334 info->split.total_bytes >> unit_shift,
1338 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1339 if (info->split.completed_bytes == info->split.total_bytes) {
1340 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1341 info->split.cur_part_number,
1342 info->split.total_parts);
1345 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1346 switch (info->update.command->op) {
1347 case WIMLIB_UPDATE_OP_DELETE:
1348 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1349 info->update.command->delete_.wim_path);
1351 case WIMLIB_UPDATE_OP_RENAME:
1352 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1353 info->update.command->rename.wim_source_path,
1354 info->update.command->rename.wim_target_path);
1356 case WIMLIB_UPDATE_OP_ADD:
1361 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1362 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1363 info->replace.path_in_wim);
1365 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1366 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1367 info->wimboot_exclude.path_in_wim);
1369 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1370 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1371 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1372 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1373 info->unmount.mounted_wim,
1374 info->unmount.mounted_image);
1376 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1377 info->unmount.mounted_wim,
1378 info->unmount.mounted_image);
1379 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1383 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1384 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1385 info->verify_image.current_image,
1386 info->verify_image.total_images);
1388 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1389 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1390 info->verify_streams.total_bytes);
1391 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1392 imagex_printf(T("\rVerifying file data: "
1393 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1394 info->verify_streams.completed_bytes >> unit_shift,
1396 info->verify_streams.total_bytes >> unit_shift,
1399 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1400 imagex_printf(T("\n"));
1405 fflush(imagex_info_file);
1406 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1410 parse_num_threads(const tchar *optarg)
1413 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1414 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1415 imagex_error(T("Number of threads must be a non-negative integer!"));
1423 parse_chunk_size(const tchar *optarg)
1426 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1427 if (chunk_size == 0) {
1428 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1429 " with optional K, M, or G suffix"));
1433 if (*tmp == T('k') || *tmp == T('K')) {
1436 } else if (*tmp == T('m') || *tmp == T('M')) {
1439 } else if (*tmp == T('g') || *tmp == T('G')) {
1443 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1444 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1448 if (chunk_size >= UINT32_MAX) {
1449 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1457 * Parse an option passed to an update command.
1459 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1462 * @option: Text string for the option (beginning with --)
1464 * @cmd: `struct wimlib_update_command' that is being constructed for
1467 * Returns true if the option was recognized; false if not.
1470 update_command_add_option(int op, const tchar *option,
1471 struct wimlib_update_command *cmd)
1473 bool recognized = true;
1475 case WIMLIB_UPDATE_OP_ADD:
1476 if (!tstrcmp(option, T("--verbose")))
1477 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1478 else if (!tstrcmp(option, T("--unix-data")))
1479 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1480 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1481 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1482 else if (!tstrcmp(option, T("--strict-acls")))
1483 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1484 else if (!tstrcmp(option, T("--dereference")))
1485 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1486 else if (!tstrcmp(option, T("--no-replace")))
1487 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1491 case WIMLIB_UPDATE_OP_DELETE:
1492 if (!tstrcmp(option, T("--force")))
1493 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1494 else if (!tstrcmp(option, T("--recursive")))
1495 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1506 /* How many nonoption arguments each `imagex update' command expects */
1507 static const unsigned update_command_num_nonoptions[] = {
1508 [WIMLIB_UPDATE_OP_ADD] = 2,
1509 [WIMLIB_UPDATE_OP_DELETE] = 1,
1510 [WIMLIB_UPDATE_OP_RENAME] = 2,
1514 update_command_add_nonoption(int op, const tchar *nonoption,
1515 struct wimlib_update_command *cmd,
1516 unsigned num_nonoptions)
1519 case WIMLIB_UPDATE_OP_ADD:
1520 if (num_nonoptions == 0)
1521 cmd->add.fs_source_path = (tchar*)nonoption;
1523 cmd->add.wim_target_path = (tchar*)nonoption;
1525 case WIMLIB_UPDATE_OP_DELETE:
1526 cmd->delete_.wim_path = (tchar*)nonoption;
1528 case WIMLIB_UPDATE_OP_RENAME:
1529 if (num_nonoptions == 0)
1530 cmd->rename.wim_source_path = (tchar*)nonoption;
1532 cmd->rename.wim_target_path = (tchar*)nonoption;
1538 * Parse a command passed on stdin to `imagex update'.
1540 * @line: Text of the command.
1541 * @len: Length of the line, including a null terminator
1544 * @command: A `struct wimlib_update_command' to fill in from the parsed
1547 * @line_number: Line number of the command, for diagnostics.
1549 * Returns true on success; returns false on parse error.
1552 parse_update_command(tchar *line, size_t len,
1553 struct wimlib_update_command *command,
1557 tchar *command_name;
1559 size_t num_nonoptions;
1561 /* Get the command name ("add", "delete", "rename") */
1562 ret = parse_string(&line, &len, &command_name);
1563 if (ret != PARSE_STRING_SUCCESS)
1566 if (!tstrcasecmp(command_name, T("add"))) {
1567 op = WIMLIB_UPDATE_OP_ADD;
1568 } else if (!tstrcasecmp(command_name, T("delete"))) {
1569 op = WIMLIB_UPDATE_OP_DELETE;
1570 } else if (!tstrcasecmp(command_name, T("rename"))) {
1571 op = WIMLIB_UPDATE_OP_RENAME;
1573 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1574 command_name, line_number);
1579 /* Parse additional options and non-options as needed */
1584 ret = parse_string(&line, &len, &next_string);
1585 if (ret == PARSE_STRING_NONE) /* End of line */
1587 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1589 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1591 if (!update_command_add_option(op, next_string, command))
1593 imagex_error(T("Unrecognized option \"%"TS"\" to "
1594 "update command \"%"TS"\" on line %zu"),
1595 next_string, command_name, line_number);
1601 if (num_nonoptions == update_command_num_nonoptions[op])
1603 imagex_error(T("Unexpected argument \"%"TS"\" in "
1604 "update command on line %zu\n"
1605 " (The \"%"TS"\" command only "
1606 "takes %zu nonoption arguments!)\n"),
1607 next_string, line_number,
1608 command_name, num_nonoptions);
1611 update_command_add_nonoption(op, next_string,
1612 command, num_nonoptions);
1617 if (num_nonoptions != update_command_num_nonoptions[op]) {
1618 imagex_error(T("Not enough arguments to update command "
1619 "\"%"TS"\" on line %zu"), command_name, line_number);
1625 static struct wimlib_update_command *
1626 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1627 size_t *num_cmds_ret)
1631 struct wimlib_update_command *cmds;
1634 nlines = text_file_count_lines(cmd_file_contents_p,
1639 /* Always allocate at least 1 slot, just in case the implementation of
1640 * calloc() returns NULL if 0 bytes are requested. */
1641 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1643 imagex_error(T("out of memory"));
1646 p = *cmd_file_contents_p;
1648 for (i = 0; i < nlines; i++) {
1649 /* XXX: Could use rawmemchr() here instead, but it may not be
1650 * available on all platforms. */
1651 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1652 size_t len = endp - p + 1;
1654 if (!is_comment_line(p, len)) {
1655 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1666 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1667 * one image from a WIM file to an NTFS volume. */
1669 imagex_apply(int argc, tchar **argv, int cmd)
1673 int image = WIMLIB_NO_IMAGE;
1675 struct wimlib_wim_info info;
1677 const tchar *wimfile;
1678 const tchar *target;
1679 const tchar *image_num_or_name = NULL;
1680 int extract_flags = 0;
1682 STRING_SET(refglobs);
1684 for_opt(c, apply_options) {
1686 case IMAGEX_CHECK_OPTION:
1687 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1689 case IMAGEX_VERBOSE_OPTION:
1690 /* No longer does anything. */
1692 case IMAGEX_REF_OPTION:
1693 ret = string_set_append(&refglobs, optarg);
1695 goto out_free_refglobs;
1697 case IMAGEX_UNIX_DATA_OPTION:
1698 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1700 case IMAGEX_NO_ACLS_OPTION:
1701 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1703 case IMAGEX_STRICT_ACLS_OPTION:
1704 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1706 case IMAGEX_NO_ATTRIBUTES_OPTION:
1707 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1709 case IMAGEX_NORPFIX_OPTION:
1710 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1712 case IMAGEX_RPFIX_OPTION:
1713 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1715 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1716 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1717 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1719 case IMAGEX_WIMBOOT_OPTION:
1720 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1722 case IMAGEX_COMPACT_OPTION:
1723 ret = set_compact_mode(optarg, &extract_flags);
1725 goto out_free_refglobs;
1733 if (argc != 2 && argc != 3)
1738 if (!tstrcmp(wimfile, T("-"))) {
1739 /* Attempt to apply pipable WIM from standard input. */
1741 image_num_or_name = NULL;
1744 image_num_or_name = argv[1];
1749 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1750 imagex_progress_func, NULL);
1752 goto out_free_refglobs;
1754 wimlib_get_wim_info(wim, &info);
1757 /* Image explicitly specified. */
1758 image_num_or_name = argv[1];
1759 image = wimlib_resolve_image(wim, image_num_or_name);
1760 ret = verify_image_exists(image, image_num_or_name, wimfile);
1762 goto out_wimlib_free;
1765 /* No image specified; default to image 1, but only if the WIM
1766 * contains exactly one image. */
1768 if (info.image_count != 1) {
1769 imagex_error(T("\"%"TS"\" contains %d images; "
1770 "Please select one (or all)."),
1771 wimfile, info.image_count);
1780 if (refglobs.num_strings) {
1782 imagex_error(T("Can't specify --ref when applying from stdin!"));
1784 goto out_wimlib_free;
1786 ret = wim_reference_globs(wim, &refglobs, open_flags);
1788 goto out_wimlib_free;
1793 /* Interpret a regular file or block device target as an NTFS
1797 if (tstat(target, &stbuf)) {
1798 if (errno != ENOENT) {
1799 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1802 goto out_wimlib_free;
1805 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1806 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1812 ret = wimlib_extract_image(wim, image, target, extract_flags);
1814 set_fd_to_binary_mode(STDIN_FILENO);
1815 ret = wimlib_extract_image_from_pipe_with_progress(
1820 imagex_progress_func,
1824 imagex_printf(T("Done applying WIM image.\n"));
1825 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1827 do_resource_not_found_warning(wimfile, &info, &refglobs);
1829 imagex_error(T( "If you are applying an image "
1830 "from a split pipable WIM,\n"
1831 " make sure you have "
1832 "concatenated together all parts."));
1834 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1835 do_metadata_not_found_warning(wimfile, &info);
1840 string_set_destroy(&refglobs);
1844 usage(CMD_APPLY, stderr);
1846 goto out_free_refglobs;
1849 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1850 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1851 * the desired image. 'wimlib-imagex append': add a new image to an existing
1854 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1858 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1859 WIMLIB_ADD_FLAG_WINCONFIG |
1860 WIMLIB_ADD_FLAG_VERBOSE;
1861 int write_flags = 0;
1862 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1863 uint32_t chunk_size = UINT32_MAX;
1864 uint32_t solid_chunk_size = UINT32_MAX;
1865 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1866 const tchar *wimfile;
1869 STRING_SET(image_properties);
1872 STRING_SET(base_wimfiles);
1873 WIMStruct **base_wims;
1875 WIMStruct *template_wim;
1876 const tchar *template_wimfile = NULL;
1877 const tchar *template_image_name_or_num = NULL;
1878 int template_image = WIMLIB_NO_IMAGE;
1881 unsigned num_threads = 0;
1886 tchar *config_file = NULL;
1888 bool source_list = false;
1889 size_t source_list_nchars = 0;
1890 tchar *source_list_contents;
1891 bool capture_sources_malloced;
1892 struct wimlib_capture_source *capture_sources;
1894 bool name_defaulted;
1896 for_opt(c, capture_or_append_options) {
1898 case IMAGEX_BOOT_OPTION:
1899 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1901 case IMAGEX_CHECK_OPTION:
1902 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1903 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1905 case IMAGEX_NOCHECK_OPTION:
1906 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1908 case IMAGEX_CONFIG_OPTION:
1909 config_file = optarg;
1910 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1912 case IMAGEX_COMPRESS_OPTION:
1913 compression_type = get_compression_type(optarg, false);
1914 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1917 case IMAGEX_COMPRESS_SLOW_OPTION:
1918 set_compress_slow();
1920 case IMAGEX_CHUNK_SIZE_OPTION:
1921 chunk_size = parse_chunk_size(optarg);
1922 if (chunk_size == UINT32_MAX)
1925 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1926 solid_chunk_size = parse_chunk_size(optarg);
1927 if (solid_chunk_size == UINT32_MAX)
1930 case IMAGEX_SOLID_COMPRESS_OPTION:
1931 solid_ctype = get_compression_type(optarg, true);
1932 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1935 case IMAGEX_SOLID_OPTION:
1936 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1938 case IMAGEX_NO_SOLID_SORT_OPTION:
1939 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1941 case IMAGEX_FLAGS_OPTION: {
1942 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1943 tsprintf(p, T("FLAGS=%"TS), optarg);
1944 ret = string_set_append(&image_properties, p);
1949 case IMAGEX_IMAGE_PROPERTY_OPTION:
1950 ret = append_image_property_argument(&image_properties);
1954 case IMAGEX_DEREFERENCE_OPTION:
1955 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1957 case IMAGEX_VERBOSE_OPTION:
1958 /* No longer does anything. */
1960 case IMAGEX_THREADS_OPTION:
1961 num_threads = parse_num_threads(optarg);
1962 if (num_threads == UINT_MAX)
1965 case IMAGEX_REBUILD_OPTION:
1966 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1968 case IMAGEX_UNIX_DATA_OPTION:
1969 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1971 case IMAGEX_SOURCE_LIST_OPTION:
1974 case IMAGEX_NO_ACLS_OPTION:
1975 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1977 case IMAGEX_STRICT_ACLS_OPTION:
1978 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1980 case IMAGEX_RPFIX_OPTION:
1981 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1983 case IMAGEX_NORPFIX_OPTION:
1984 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1986 case IMAGEX_PIPABLE_OPTION:
1987 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1989 case IMAGEX_NOT_PIPABLE_OPTION:
1990 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1992 case IMAGEX_UPDATE_OF_OPTION:
1993 if (template_image_name_or_num) {
1994 imagex_error(T("'--update-of' can only be "
1995 "specified one time!"));
1999 colon = tstrrchr(optarg, T(':'));
2002 template_wimfile = optarg;
2004 template_image_name_or_num = colon + 1;
2006 template_wimfile = NULL;
2007 template_image_name_or_num = optarg;
2011 case IMAGEX_DELTA_FROM_OPTION:
2012 if (cmd != CMD_CAPTURE) {
2013 imagex_error(T("'--delta-from' is only "
2014 "valid for capture!"));
2017 ret = string_set_append(&base_wimfiles, optarg);
2020 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2022 case IMAGEX_WIMBOOT_OPTION:
2023 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2025 case IMAGEX_UNSAFE_COMPACT_OPTION:
2026 if (cmd != CMD_APPEND) {
2027 imagex_error(T("'--unsafe-compact' is only "
2028 "valid for append!"));
2031 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2033 case IMAGEX_SNAPSHOT_OPTION:
2034 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2043 if (argc < 2 || argc > 4)
2049 /* Set default compression type and parameters. */
2052 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2053 /* No compression type specified. Use the default. */
2055 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2056 /* With --wimboot, default to XPRESS compression. */
2057 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2058 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2059 /* With --solid, default to LZMS compression. (However,
2060 * this will not affect solid resources!) */
2061 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2063 /* Otherwise, default to LZX compression. */
2064 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2068 if (!tstrcmp(wimfile, T("-"))) {
2069 /* Writing captured WIM to standard output. */
2071 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2072 imagex_error("Can't write a non-pipable WIM to "
2073 "standard output! Specify --pipable\n"
2074 " if you want to create a pipable WIM "
2075 "(but read the docs first).");
2079 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2081 if (cmd == CMD_APPEND) {
2082 imagex_error(T("Using standard output for append does "
2083 "not make sense."));
2086 wim_fd = STDOUT_FILENO;
2088 imagex_info_file = stderr;
2089 set_fd_to_binary_mode(wim_fd);
2092 /* If template image was specified using --update-of=IMAGE rather
2093 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2094 if (template_image_name_or_num && !template_wimfile) {
2095 if (base_wimfiles.num_strings == 1) {
2096 /* Capturing delta WIM based on single WIM: default to
2098 template_wimfile = base_wimfiles.strings[0];
2099 } else if (cmd == CMD_APPEND) {
2100 /* Appending to WIM: default to WIM being appended to.
2102 template_wimfile = wimfile;
2104 /* Capturing a normal (non-delta) WIM, so the WIM file
2105 * *must* be explicitly specified. */
2106 if (base_wimfiles.num_strings > 1) {
2107 imagex_error(T("For capture of delta WIM "
2108 "based on multiple existing "
2110 " '--update-of' must "
2111 "specify WIMFILE:IMAGE!"));
2113 imagex_error(T("For capture of non-delta WIM, "
2114 "'--update-of' must specify "
2123 name_defaulted = false;
2125 /* Set default name to SOURCE argument, omitting any directory
2126 * prefixes and trailing slashes. This requires making a copy
2127 * of @source. Leave some free characters at the end in case we
2128 * append a number to keep the name unique. */
2129 size_t source_name_len;
2131 source_name_len = tstrlen(source);
2132 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2133 name = tbasename(tstrcpy(source_copy, source));
2134 name_defaulted = true;
2137 /* Image description (if given). */
2139 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2140 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2141 ret = string_set_append(&image_properties, p);
2147 /* Set up capture sources in source list mode */
2148 if (source[0] == T('-') && source[1] == T('\0')) {
2149 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2151 source_list_contents = file_get_text_contents(source,
2152 &source_list_nchars);
2154 if (!source_list_contents)
2157 capture_sources = parse_source_list(&source_list_contents,
2160 if (!capture_sources) {
2162 goto out_free_source_list_contents;
2164 capture_sources_malloced = true;
2166 /* Set up capture source in non-source-list mode. */
2167 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2168 capture_sources[0].fs_source_path = source;
2169 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2170 capture_sources[0].reserved = 0;
2172 capture_sources_malloced = false;
2173 source_list_contents = NULL;
2176 /* Open the existing WIM, or create a new one. */
2177 if (cmd == CMD_APPEND) {
2178 ret = wimlib_open_wim_with_progress(wimfile,
2179 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2181 imagex_progress_func,
2184 goto out_free_capture_sources;
2186 ret = wimlib_create_new_wim(compression_type, &wim);
2188 goto out_free_capture_sources;
2189 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2192 /* Set chunk size if non-default. */
2193 if (chunk_size != UINT32_MAX) {
2194 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2197 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2199 int ctype = compression_type;
2201 if (cmd == CMD_APPEND) {
2202 struct wimlib_wim_info info;
2203 wimlib_get_wim_info(wim, &info);
2204 ctype = info.compression_type;
2207 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2208 ret = wimlib_set_output_chunk_size(wim, 4096);
2213 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2214 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2218 if (solid_chunk_size != UINT32_MAX) {
2219 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2225 /* Detect if source is regular file or block device and set NTFS volume
2230 if (tstat(source, &stbuf) == 0) {
2231 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2232 imagex_printf(T("Capturing WIM image from NTFS "
2233 "filesystem on \"%"TS"\"\n"), source);
2234 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2237 if (errno != ENOENT) {
2238 imagex_error_with_errno(T("Failed to stat "
2239 "\"%"TS"\""), source);
2247 /* If the user did not specify an image name, and the basename of the
2248 * source already exists as an image name in the WIM file, append a
2249 * suffix to make it unique. */
2250 if (cmd == CMD_APPEND && name_defaulted) {
2251 unsigned long conflict_idx;
2252 tchar *name_end = tstrchr(name, T('\0'));
2253 for (conflict_idx = 1;
2254 wimlib_image_name_in_use(wim, name);
2257 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2261 /* If capturing a delta WIM, reference resources from the base WIMs
2262 * before adding the new image. */
2263 if (base_wimfiles.num_strings) {
2264 base_wims = calloc(base_wimfiles.num_strings,
2265 sizeof(base_wims[0]));
2266 if (base_wims == NULL) {
2267 imagex_error(T("Out of memory!"));
2272 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2273 ret = wimlib_open_wim_with_progress(
2274 base_wimfiles.strings[i], open_flags,
2275 &base_wims[i], imagex_progress_func, NULL);
2277 goto out_free_base_wims;
2281 ret = wimlib_reference_resources(wim, base_wims,
2282 base_wimfiles.num_strings, 0);
2284 goto out_free_base_wims;
2286 if (base_wimfiles.num_strings == 1) {
2287 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2288 base_wimfiles.strings[0]);
2290 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2291 base_wimfiles.num_strings);
2298 /* If capturing or appending as an update of an existing (template) image,
2299 * open the WIM if needed and parse the image index. */
2300 if (template_image_name_or_num) {
2303 if (base_wimfiles.num_strings == 1 &&
2304 template_wimfile == base_wimfiles.strings[0]) {
2305 template_wim = base_wims[0];
2306 } else if (template_wimfile == wimfile) {
2309 ret = wimlib_open_wim_with_progress(template_wimfile,
2312 imagex_progress_func,
2315 goto out_free_base_wims;
2318 template_image = wimlib_resolve_image(template_wim,
2319 template_image_name_or_num);
2321 if (template_image_name_or_num[0] == T('-')) {
2324 struct wimlib_wim_info info;
2326 wimlib_get_wim_info(template_wim, &info);
2327 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2328 if (n >= 1 && n <= info.image_count &&
2330 tmp != template_image_name_or_num + 1)
2332 template_image = info.image_count - (n - 1);
2335 ret = verify_image_exists_and_is_single(template_image,
2336 template_image_name_or_num,
2339 goto out_free_template_wim;
2341 template_wim = NULL;
2344 ret = wimlib_add_image_multisource(wim,
2351 goto out_free_template_wim;
2353 if (image_properties.num_strings || template_image_name_or_num) {
2354 /* User asked to set additional image properties, or an image on
2355 * which the added one is to be based has been specified with
2357 struct wimlib_wim_info info;
2359 wimlib_get_wim_info(wim, &info);
2361 ret = apply_image_properties(&image_properties, wim,
2362 info.image_count, NULL);
2364 goto out_free_template_wim;
2366 /* Reference template image if the user provided one. */
2367 if (template_image_name_or_num) {
2368 imagex_printf(T("Using image %d "
2369 "from \"%"TS"\" as template\n"),
2370 template_image, template_wimfile);
2371 ret = wimlib_reference_template_image(wim,
2377 goto out_free_template_wim;
2381 /* Write the new WIM or overwrite the existing WIM with the new image
2383 if (cmd == CMD_APPEND) {
2384 ret = wimlib_overwrite(wim, write_flags, num_threads);
2385 } else if (wimfile) {
2386 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2387 write_flags, num_threads);
2389 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2390 write_flags, num_threads);
2392 out_free_template_wim:
2393 /* template_wim may alias base_wims[0] or wim. */
2394 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2395 template_wim != wim)
2396 wimlib_free(template_wim);
2398 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2399 wimlib_free(base_wims[i]);
2403 out_free_capture_sources:
2404 if (capture_sources_malloced)
2405 free(capture_sources);
2406 out_free_source_list_contents:
2407 free(source_list_contents);
2409 string_set_destroy(&image_properties);
2410 string_set_destroy(&base_wimfiles);
2420 /* Remove image(s) from a WIM. */
2422 imagex_delete(int argc, tchar **argv, int cmd)
2425 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2426 int write_flags = 0;
2427 const tchar *wimfile;
2428 const tchar *image_num_or_name;
2433 for_opt(c, delete_options) {
2435 case IMAGEX_CHECK_OPTION:
2436 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2437 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2439 case IMAGEX_SOFT_OPTION:
2440 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2442 case IMAGEX_UNSAFE_COMPACT_OPTION:
2443 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2454 imagex_error(T("Must specify a WIM file"));
2456 imagex_error(T("Must specify an image"));
2460 image_num_or_name = argv[1];
2462 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2463 imagex_progress_func, NULL);
2467 image = wimlib_resolve_image(wim, image_num_or_name);
2469 ret = verify_image_exists(image, image_num_or_name, wimfile);
2471 goto out_wimlib_free;
2473 ret = wimlib_delete_image(wim, image);
2475 imagex_error(T("Failed to delete image from \"%"TS"\""),
2477 goto out_wimlib_free;
2480 ret = wimlib_overwrite(wim, write_flags, 0);
2482 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2483 "deleted"), wimfile);
2491 usage(CMD_DELETE, stderr);
2496 struct print_dentry_options {
2501 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2503 tprintf(T("%"TS"\n"), dentry->full_path);
2506 static const struct {
2509 } file_attr_flags[] = {
2510 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2511 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2512 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2513 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2514 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2515 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2516 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2517 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2518 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2519 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2520 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2521 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2522 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2523 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2524 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2527 #define TIMESTR_MAX 100
2530 timespec_to_string(const struct timespec *spec, tchar *buf)
2532 time_t t = spec->tv_sec;
2535 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2536 buf[TIMESTR_MAX - 1] = '\0';
2540 print_time(const tchar *type, const struct timespec *spec)
2542 tchar timestr[TIMESTR_MAX];
2544 timespec_to_string(spec, timestr);
2546 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2549 static void print_byte_field(const uint8_t field[], size_t len)
2552 tprintf(T("%02hhx"), *field++);
2556 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2558 tchar attr_string[256];
2561 tputs(T("WIM Information:"));
2562 tputs(T("----------------"));
2563 tprintf(T("Path: %"TS"\n"), wimfile);
2564 tprintf(T("GUID: 0x"));
2565 print_byte_field(info->guid, sizeof(info->guid));
2567 tprintf(T("Version: %u\n"), info->wim_version);
2568 tprintf(T("Image Count: %d\n"), info->image_count);
2569 tprintf(T("Compression: %"TS"\n"),
2570 wimlib_get_compression_type_string(info->compression_type));
2571 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2573 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2574 tprintf(T("Boot Index: %d\n"), info->boot_index);
2575 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2577 attr_string[0] = T('\0');
2580 tstrcat(attr_string, T("Pipable, "));
2582 if (info->has_integrity_table)
2583 tstrcat(attr_string, T("Integrity info, "));
2585 if (info->has_rpfix)
2586 tstrcat(attr_string, T("Relative path junction, "));
2588 if (info->resource_only)
2589 tstrcat(attr_string, T("Resource only, "));
2591 if (info->metadata_only)
2592 tstrcat(attr_string, T("Metadata only, "));
2594 if (info->is_marked_readonly)
2595 tstrcat(attr_string, T("Readonly, "));
2597 p = tstrchr(attr_string, T('\0'));
2598 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2601 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2605 print_resource(const struct wimlib_resource_entry *resource,
2608 tprintf(T("Hash = 0x"));
2609 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2612 if (!resource->is_missing) {
2613 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2614 resource->uncompressed_size);
2615 if (resource->packed) {
2616 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2617 "bytes @ offset %"PRIu64"\n"),
2618 resource->raw_resource_uncompressed_size,
2619 resource->raw_resource_compressed_size,
2620 resource->raw_resource_offset_in_wim);
2622 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2625 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2626 resource->compressed_size);
2628 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2632 tprintf(T("Part Number = %u\n"), resource->part_number);
2633 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2635 tprintf(T("Flags = "));
2636 if (resource->is_compressed)
2637 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2638 if (resource->is_metadata)
2639 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2640 if (resource->is_free)
2641 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2642 if (resource->is_spanned)
2643 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2644 if (resource->packed)
2645 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2653 print_blobs(WIMStruct *wim)
2655 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2659 default_print_security_descriptor(const uint8_t *sd, size_t size)
2661 tprintf(T("Security Descriptor = "));
2662 print_byte_field(sd, size);
2667 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2671 "----------------------------------------------------------------------------\n"));
2672 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2673 if (dentry->dos_name)
2674 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2675 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2676 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2677 if (file_attr_flags[i].flag & dentry->attributes)
2678 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2679 file_attr_flags[i].name);
2681 if (dentry->security_descriptor) {
2682 print_security_descriptor(dentry->security_descriptor,
2683 dentry->security_descriptor_size);
2686 print_time(T("Creation Time"), &dentry->creation_time);
2687 print_time(T("Last Write Time"), &dentry->last_write_time);
2688 print_time(T("Last Access Time"), &dentry->last_access_time);
2691 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2692 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2694 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2695 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2697 if (dentry->unix_mode != 0) {
2698 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2699 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2700 dentry->unix_uid, dentry->unix_gid,
2701 dentry->unix_mode, dentry->unix_rdev);
2704 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2705 if (dentry->streams[i].stream_name) {
2706 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2707 dentry->streams[i].stream_name);
2708 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2709 tprintf(T("\tRaw encrypted data stream:\n"));
2710 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2711 tprintf(T("\tReparse point stream:\n"));
2713 tprintf(T("\tUnnamed data stream:\n"));
2715 print_resource(&dentry->streams[i].resource, NULL);
2720 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2722 const struct print_dentry_options *options = _options;
2723 if (!options->detailed)
2724 print_dentry_full_path(dentry);
2726 print_dentry_detailed(dentry);
2730 /* Print the files contained in an image(s) in a WIM file. */
2732 imagex_dir(int argc, tchar **argv, int cmd)
2734 const tchar *wimfile;
2735 WIMStruct *wim = NULL;
2738 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2740 struct print_dentry_options options = {
2743 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2745 STRING_SET(refglobs);
2747 for_opt(c, dir_options) {
2749 case IMAGEX_PATH_OPTION:
2752 case IMAGEX_DETAILED_OPTION:
2753 options.detailed = true;
2755 case IMAGEX_ONE_FILE_ONLY_OPTION:
2756 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2758 case IMAGEX_REF_OPTION:
2759 ret = string_set_append(&refglobs, optarg);
2761 goto out_free_refglobs;
2771 imagex_error(T("Must specify a WIM file"));
2775 imagex_error(T("Too many arguments"));
2780 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2781 imagex_progress_func, NULL);
2783 goto out_free_refglobs;
2786 image = wimlib_resolve_image(wim, argv[1]);
2787 ret = verify_image_exists(image, argv[1], wimfile);
2789 goto out_wimlib_free;
2791 /* No image specified; default to image 1, but only if the WIM
2792 * contains exactly one image. */
2794 struct wimlib_wim_info info;
2796 wimlib_get_wim_info(wim, &info);
2797 if (info.image_count != 1) {
2798 imagex_error(T("\"%"TS"\" contains %d images; Please "
2799 "select one (or all)."),
2800 wimfile, info.image_count);
2807 if (refglobs.num_strings) {
2808 ret = wim_reference_globs(wim, &refglobs, 0);
2810 goto out_wimlib_free;
2813 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2814 print_dentry, &options);
2815 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2816 struct wimlib_wim_info info;
2818 wimlib_get_wim_info(wim, &info);
2819 do_metadata_not_found_warning(wimfile, &info);
2824 string_set_destroy(&refglobs);
2828 usage(CMD_DIR, stderr);
2830 goto out_free_refglobs;
2833 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2836 imagex_export(int argc, tchar **argv, int cmd)
2840 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2841 int write_flags = 0;
2842 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2843 const tchar *src_wimfile;
2844 const tchar *src_image_num_or_name;
2845 const tchar *dest_wimfile;
2847 const tchar *dest_name;
2848 const tchar *dest_desc;
2850 struct wimlib_wim_info src_info;
2851 WIMStruct *dest_wim;
2856 STRING_SET(refglobs);
2857 unsigned num_threads = 0;
2858 uint32_t chunk_size = UINT32_MAX;
2859 uint32_t solid_chunk_size = UINT32_MAX;
2860 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2862 for_opt(c, export_options) {
2864 case IMAGEX_BOOT_OPTION:
2865 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2867 case IMAGEX_CHECK_OPTION:
2868 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2869 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2871 case IMAGEX_NOCHECK_OPTION:
2872 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2874 case IMAGEX_COMPRESS_OPTION:
2875 compression_type = get_compression_type(optarg, false);
2876 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2879 case IMAGEX_COMPRESS_SLOW_OPTION:
2880 set_compress_slow();
2881 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2883 case IMAGEX_RECOMPRESS_OPTION:
2884 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2886 case IMAGEX_SOLID_OPTION:
2887 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2889 case IMAGEX_NO_SOLID_SORT_OPTION:
2890 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2892 case IMAGEX_CHUNK_SIZE_OPTION:
2893 chunk_size = parse_chunk_size(optarg);
2894 if (chunk_size == UINT32_MAX)
2897 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2898 solid_chunk_size = parse_chunk_size(optarg);
2899 if (solid_chunk_size == UINT32_MAX)
2902 case IMAGEX_SOLID_COMPRESS_OPTION:
2903 solid_ctype = get_compression_type(optarg, true);
2904 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2907 case IMAGEX_REF_OPTION:
2908 ret = string_set_append(&refglobs, optarg);
2910 goto out_free_refglobs;
2912 case IMAGEX_THREADS_OPTION:
2913 num_threads = parse_num_threads(optarg);
2914 if (num_threads == UINT_MAX)
2917 case IMAGEX_REBUILD_OPTION:
2918 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2920 case IMAGEX_PIPABLE_OPTION:
2921 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2923 case IMAGEX_NOT_PIPABLE_OPTION:
2924 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2926 case IMAGEX_WIMBOOT_OPTION:
2927 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2929 case IMAGEX_UNSAFE_COMPACT_OPTION:
2930 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2938 if (argc < 3 || argc > 5)
2940 src_wimfile = argv[0];
2941 src_image_num_or_name = argv[1];
2942 dest_wimfile = argv[2];
2943 dest_name = (argc >= 4) ? argv[3] : NULL;
2944 dest_desc = (argc >= 5) ? argv[4] : NULL;
2945 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2946 imagex_progress_func, NULL);
2948 goto out_free_refglobs;
2950 wimlib_get_wim_info(src_wim, &src_info);
2952 /* Determine if the destination is an existing file or not. If so, we
2953 * try to append the exported image(s) to it; otherwise, we create a new
2954 * WIM containing the exported image(s). Furthermore, determine if we
2955 * need to write a pipable WIM directly to standard output. */
2957 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2959 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2960 imagex_error("Can't write a non-pipable WIM to "
2961 "standard output! Specify --pipable\n"
2962 " if you want to create a pipable WIM "
2963 "(but read the docs first).");
2965 goto out_free_src_wim;
2968 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2970 dest_wimfile = NULL;
2971 dest_wim_fd = STDOUT_FILENO;
2972 imagex_info_file = stderr;
2973 set_fd_to_binary_mode(dest_wim_fd);
2976 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2978 /* Destination file exists. */
2980 if (!S_ISREG(stbuf.st_mode)) {
2981 imagex_error(T("\"%"TS"\" is not a regular file"),
2984 goto out_free_src_wim;
2986 ret = wimlib_open_wim_with_progress(dest_wimfile,
2988 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2990 imagex_progress_func,
2993 goto out_free_src_wim;
2995 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2996 /* The user specified a compression type, but we're
2997 * exporting to an existing WIM. Make sure the
2998 * specified compression type is the same as the
2999 * compression type of the existing destination WIM. */
3000 struct wimlib_wim_info dest_info;
3002 wimlib_get_wim_info(dest_wim, &dest_info);
3003 if (compression_type != dest_info.compression_type) {
3004 imagex_error(T("Cannot specify a compression type that is "
3005 "not the same as that used in the "
3006 "destination WIM"));
3008 goto out_free_dest_wim;
3014 if (errno != ENOENT) {
3015 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3018 goto out_free_src_wim;
3021 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3022 imagex_error(T("'--unsafe-compact' is only valid when "
3023 "exporting to an existing WIM file!"));
3025 goto out_free_src_wim;
3028 /* dest_wimfile is not an existing file, so create a new WIM. */
3030 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3031 /* The user did not specify a compression type; default
3032 * to that of the source WIM, unless --solid or
3033 * --wimboot was specified. */
3035 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3036 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3037 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3038 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3040 compression_type = src_info.compression_type;
3042 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3044 goto out_free_src_wim;
3046 wimlib_register_progress_function(dest_wim,
3047 imagex_progress_func, NULL);
3049 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3050 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3052 /* For --wimboot export, use small XPRESS chunks. */
3053 wimlib_set_output_chunk_size(dest_wim, 4096);
3054 } else if (compression_type == src_info.compression_type &&
3055 chunk_size == UINT32_MAX)
3057 /* Use same chunk size if compression type is the same. */
3058 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3062 if (chunk_size != UINT32_MAX) {
3063 /* Set destination chunk size. */
3064 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3066 goto out_free_dest_wim;
3068 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3069 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3071 goto out_free_dest_wim;
3073 if (solid_chunk_size != UINT32_MAX) {
3074 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3076 goto out_free_dest_wim;
3079 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3080 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3082 goto out_free_dest_wim;
3084 if (refglobs.num_strings) {
3085 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3087 goto out_free_dest_wim;
3090 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3091 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3093 imagex_error(T("--boot specified for all-images export, but source WIM "
3094 "has no bootable image."));
3096 goto out_free_dest_wim;
3099 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3100 dest_desc, export_flags);
3102 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3103 do_resource_not_found_warning(src_wimfile,
3104 &src_info, &refglobs);
3105 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3106 do_metadata_not_found_warning(src_wimfile, &src_info);
3108 goto out_free_dest_wim;
3112 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3113 else if (dest_wimfile)
3114 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3115 write_flags, num_threads);
3117 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3118 WIMLIB_ALL_IMAGES, write_flags,
3121 wimlib_free(dest_wim);
3123 wimlib_free(src_wim);
3125 string_set_destroy(&refglobs);
3129 usage(CMD_EXPORT, stderr);
3132 goto out_free_refglobs;
3135 /* Extract files or directories from a WIM image */
3137 imagex_extract(int argc, tchar **argv, int cmd)
3144 const tchar *wimfile;
3145 const tchar *image_num_or_name;
3146 tchar *dest_dir = T(".");
3147 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3148 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3149 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3150 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3152 STRING_SET(refglobs);
3154 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3156 for_opt(c, extract_options) {
3158 case IMAGEX_CHECK_OPTION:
3159 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3161 case IMAGEX_VERBOSE_OPTION:
3162 /* No longer does anything. */
3164 case IMAGEX_REF_OPTION:
3165 ret = string_set_append(&refglobs, optarg);
3167 goto out_free_refglobs;
3169 case IMAGEX_UNIX_DATA_OPTION:
3170 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3172 case IMAGEX_NO_ACLS_OPTION:
3173 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3175 case IMAGEX_STRICT_ACLS_OPTION:
3176 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3178 case IMAGEX_NO_ATTRIBUTES_OPTION:
3179 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3181 case IMAGEX_DEST_DIR_OPTION:
3184 case IMAGEX_TO_STDOUT_OPTION:
3185 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3186 imagex_info_file = stderr;
3187 imagex_be_quiet = true;
3188 set_fd_to_binary_mode(STDOUT_FILENO);
3190 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3191 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3192 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3194 case IMAGEX_NO_GLOBS_OPTION:
3195 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3197 case IMAGEX_NULLGLOB_OPTION:
3198 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3200 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3201 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3203 case IMAGEX_WIMBOOT_OPTION:
3204 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3206 case IMAGEX_COMPACT_OPTION:
3207 ret = set_compact_mode(optarg, &extract_flags);
3209 goto out_free_refglobs;
3221 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3222 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3224 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3229 image_num_or_name = argv[1];
3234 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3235 imagex_progress_func, NULL);
3237 goto out_free_refglobs;
3239 image = wimlib_resolve_image(wim, image_num_or_name);
3240 ret = verify_image_exists_and_is_single(image,
3244 goto out_wimlib_free;
3246 if (refglobs.num_strings) {
3247 ret = wim_reference_globs(wim, &refglobs, open_flags);
3249 goto out_wimlib_free;
3255 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3258 while (argc != 0 && ret == 0) {
3262 num_paths < argc && argv[num_paths][0] != T('@');
3267 ret = wimlib_extract_paths(wim, image, dest_dir,
3268 (const tchar **)argv,
3270 extract_flags | notlist_extract_flags);
3274 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3283 if (!imagex_be_quiet)
3284 imagex_printf(T("Done extracting files.\n"));
3285 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3286 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3287 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3288 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3289 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3292 T("Note: You can use the '--nullglob' "
3293 "option to ignore missing files.\n"));
3295 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3296 "files and directories\n"
3297 " are in the WIM image.\n"),
3298 get_cmd_string(CMD_DIR, false));
3299 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3300 struct wimlib_wim_info info;
3302 wimlib_get_wim_info(wim, &info);
3303 do_resource_not_found_warning(wimfile, &info, &refglobs);
3304 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3305 struct wimlib_wim_info info;
3307 wimlib_get_wim_info(wim, &info);
3308 do_metadata_not_found_warning(wimfile, &info);
3313 string_set_destroy(&refglobs);
3317 usage(CMD_EXTRACT, stderr);
3320 goto out_free_refglobs;
3323 /* Prints information about a WIM file; also can mark an image as bootable,
3324 * change the name of an image, or change the description of an image. */
3326 imagex_info(int argc, tchar **argv, int cmd)
3331 bool nocheck = false;
3332 bool header = false;
3335 bool short_header = true;
3336 const tchar *xml_out_file = NULL;
3337 const tchar *wimfile;
3338 const tchar *image_num_or_name;
3339 STRING_SET(image_properties);
3344 struct wimlib_wim_info info;
3346 for_opt(c, info_options) {
3348 case IMAGEX_BOOT_OPTION:
3351 case IMAGEX_CHECK_OPTION:
3354 case IMAGEX_NOCHECK_OPTION:
3357 case IMAGEX_HEADER_OPTION:
3359 short_header = false;
3361 case IMAGEX_BLOBS_OPTION:
3363 short_header = false;
3365 case IMAGEX_XML_OPTION:
3367 short_header = false;
3369 case IMAGEX_EXTRACT_XML_OPTION:
3370 xml_out_file = optarg;
3371 short_header = false;
3373 case IMAGEX_METADATA_OPTION:
3374 imagex_error(T("The --metadata option has been removed. "
3375 "Use 'wimdir --detail' instead."));
3377 case IMAGEX_IMAGE_PROPERTY_OPTION:
3378 ret = append_image_property_argument(&image_properties);
3389 if (argc < 1 || argc > 4)
3393 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3397 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3398 tsprintf(p, T("NAME=%"TS), argv[2]);
3399 ret = string_set_append(&image_properties, p);
3406 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3407 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3408 ret = string_set_append(&image_properties, p);
3413 if (check && nocheck) {
3414 imagex_error(T("Can't specify both --check and --nocheck"));
3419 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3421 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3422 imagex_progress_func, NULL);
3426 wimlib_get_wim_info(wim, &info);
3428 image = wimlib_resolve_image(wim, image_num_or_name);
3429 ret = WIMLIB_ERR_INVALID_IMAGE;
3430 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3431 verify_image_exists(image, image_num_or_name, wimfile);
3433 imagex_error(T("If you would like to set the boot "
3434 "index to 0, specify image \"0\" with "
3435 "the --boot flag."));
3437 goto out_wimlib_free;
3440 if (boot && info.image_count == 0) {
3441 imagex_error(T("--boot is meaningless on a WIM with no images"));
3442 goto out_wimlib_free;
3445 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3447 imagex_error(T("Cannot specify the --boot flag "
3448 "without specifying a specific "
3449 "image in a multi-image WIM"));
3450 goto out_wimlib_free;
3452 if (image_properties.num_strings) {
3453 imagex_error(T("Can't change image properties without "
3454 "specifying a specific image in a "
3455 "multi-image WIM"));
3456 goto out_wimlib_free;
3460 /* Operations that print information are separated from operations that
3461 * recreate the WIM file. */
3462 if (!image_properties.num_strings && !boot) {
3464 /* Read-only operations */
3466 if (image == WIMLIB_NO_IMAGE) {
3467 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3468 image_num_or_name, wimfile);
3469 goto out_wimlib_free;
3472 if (image == WIMLIB_ALL_IMAGES && short_header)
3473 print_wim_information(wimfile, &info);
3476 wimlib_print_header(wim);
3479 if (info.total_parts != 1) {
3480 tfprintf(stderr, T("Warning: Only showing the blobs "
3481 "for part %d of a %d-part WIM.\n"),
3482 info.part_number, info.total_parts);
3488 ret = wimlib_extract_xml_data(wim, stdout);
3490 goto out_wimlib_free;
3496 fp = tfopen(xml_out_file, T("wb"));
3498 imagex_error_with_errno(T("Failed to open the "
3499 "file \"%"TS"\" for "
3503 goto out_wimlib_free;
3505 ret = wimlib_extract_xml_data(wim, fp);
3507 imagex_error(T("Failed to close the file "
3513 goto out_wimlib_free;
3517 wimlib_print_available_images(wim, image);
3521 /* Modification operations */
3522 bool any_property_changes;
3524 if (image == WIMLIB_ALL_IMAGES)
3527 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3528 imagex_error(T("Cannot change image properties "
3529 "when using image 0"));
3531 goto out_wimlib_free;
3535 if (image == info.boot_index) {
3536 imagex_printf(T("Image %d is already marked as "
3537 "bootable.\n"), image);
3540 imagex_printf(T("Marking image %d as bootable.\n"),
3542 info.boot_index = image;
3543 ret = wimlib_set_wim_info(wim, &info,
3544 WIMLIB_CHANGE_BOOT_INDEX);
3546 goto out_wimlib_free;
3550 ret = apply_image_properties(&image_properties, wim, image,
3551 &any_property_changes);
3553 goto out_wimlib_free;
3555 /* Only call wimlib_overwrite() if something actually needs to
3557 if (boot || any_property_changes ||
3558 (check && !info.has_integrity_table) ||
3559 (nocheck && info.has_integrity_table))
3561 int write_flags = 0;
3564 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3566 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3567 ret = wimlib_overwrite(wim, write_flags, 1);
3569 imagex_printf(T("The file \"%"TS"\" was not modified "
3570 "because nothing needed to be done.\n"),
3578 string_set_destroy(&image_properties);
3582 usage(CMD_INFO, stderr);
3588 /* Join split WIMs into one part WIM */
3590 imagex_join(int argc, tchar **argv, int cmd)
3593 int swm_open_flags = 0;
3594 int wim_write_flags = 0;
3595 const tchar *output_path;
3598 for_opt(c, join_options) {
3600 case IMAGEX_CHECK_OPTION:
3601 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3602 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3612 imagex_error(T("Must specify one or more split WIM (.swm) "
3616 output_path = argv[0];
3617 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3622 imagex_progress_func,
3628 usage(CMD_JOIN, stderr);
3633 #if WIM_MOUNTING_SUPPORTED
3635 /* Mounts a WIM image. */
3637 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3640 int mount_flags = 0;
3642 const tchar *staging_dir = NULL;
3643 const tchar *wimfile;
3646 struct wimlib_wim_info info;
3650 STRING_SET(refglobs);
3652 if (cmd == CMD_MOUNTRW) {
3653 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3654 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3657 for_opt(c, mount_options) {
3659 case IMAGEX_ALLOW_OTHER_OPTION:
3660 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3662 case IMAGEX_CHECK_OPTION:
3663 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3665 case IMAGEX_DEBUG_OPTION:
3666 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3668 case IMAGEX_STREAMS_INTERFACE_OPTION:
3669 if (!tstrcasecmp(optarg, T("none")))
3670 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3671 else if (!tstrcasecmp(optarg, T("xattr")))
3672 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3673 else if (!tstrcasecmp(optarg, T("windows")))
3674 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3676 imagex_error(T("Unknown stream interface \"%"TS"\""),
3681 case IMAGEX_REF_OPTION:
3682 ret = string_set_append(&refglobs, optarg);
3684 goto out_free_refglobs;
3686 case IMAGEX_STAGING_DIR_OPTION:
3687 staging_dir = optarg;
3689 case IMAGEX_UNIX_DATA_OPTION:
3690 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3698 if (argc != 2 && argc != 3)
3703 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3704 imagex_progress_func, NULL);
3706 goto out_free_refglobs;
3708 wimlib_get_wim_info(wim, &info);
3711 /* Image explicitly specified. */
3712 image = wimlib_resolve_image(wim, argv[1]);
3714 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3718 /* No image specified; default to image 1, but only if the WIM
3719 * contains exactly one image. */
3721 if (info.image_count != 1) {
3722 imagex_error(T("\"%"TS"\" contains %d images; Please "
3723 "select one."), wimfile, info.image_count);
3731 if (refglobs.num_strings) {
3732 ret = wim_reference_globs(wim, &refglobs, open_flags);
3737 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3739 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3740 do_metadata_not_found_warning(wimfile, &info);
3742 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3744 image, wimfile, dir);
3750 string_set_destroy(&refglobs);
3756 goto out_free_refglobs;
3758 #endif /* WIM_MOUNTING_SUPPORTED */
3760 /* Rebuild a WIM file */
3762 imagex_optimize(int argc, tchar **argv, int cmd)
3765 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3766 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3767 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3768 uint32_t chunk_size = UINT32_MAX;
3769 uint32_t solid_chunk_size = UINT32_MAX;
3770 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3773 const tchar *wimfile;
3776 unsigned num_threads = 0;
3778 for_opt(c, optimize_options) {
3780 case IMAGEX_CHECK_OPTION:
3781 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3782 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3784 case IMAGEX_NOCHECK_OPTION:
3785 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3787 case IMAGEX_COMPRESS_OPTION:
3788 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3789 compression_type = get_compression_type(optarg, false);
3790 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3793 case IMAGEX_COMPRESS_SLOW_OPTION:
3794 set_compress_slow();
3795 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3797 case IMAGEX_RECOMPRESS_OPTION:
3798 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3800 case IMAGEX_CHUNK_SIZE_OPTION:
3801 chunk_size = parse_chunk_size(optarg);
3802 if (chunk_size == UINT32_MAX)
3805 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3806 solid_chunk_size = parse_chunk_size(optarg);
3807 if (solid_chunk_size == UINT32_MAX)
3810 case IMAGEX_SOLID_COMPRESS_OPTION:
3811 solid_ctype = get_compression_type(optarg, true);
3812 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3815 case IMAGEX_SOLID_OPTION:
3816 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3817 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3819 case IMAGEX_NO_SOLID_SORT_OPTION:
3820 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3822 case IMAGEX_THREADS_OPTION:
3823 num_threads = parse_num_threads(optarg);
3824 if (num_threads == UINT_MAX)
3827 case IMAGEX_PIPABLE_OPTION:
3828 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3830 case IMAGEX_NOT_PIPABLE_OPTION:
3831 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3833 case IMAGEX_UNSAFE_COMPACT_OPTION:
3834 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3848 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3849 imagex_progress_func, NULL);
3853 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3854 /* Change compression type. */
3855 ret = wimlib_set_output_compression_type(wim, compression_type);
3857 goto out_wimlib_free;
3860 if (chunk_size != UINT32_MAX) {
3861 /* Change chunk size. */
3862 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3864 goto out_wimlib_free;
3866 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3867 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3869 goto out_wimlib_free;
3871 if (solid_chunk_size != UINT32_MAX) {
3872 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3874 goto out_wimlib_free;
3877 old_size = file_get_size(wimfile);
3878 tprintf(T("\"%"TS"\" original size: "), wimfile);
3880 tputs(T("Unknown"));
3882 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3884 ret = wimlib_overwrite(wim, write_flags, num_threads);
3886 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3887 goto out_wimlib_free;
3890 new_size = file_get_size(wimfile);
3891 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3893 tputs(T("Unknown"));
3895 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3897 tfputs(T("Space saved: "), stdout);
3898 if (new_size != -1 && old_size != -1) {
3899 tprintf(T("%lld KiB\n"),
3900 ((long long)old_size - (long long)new_size) >> 10);
3902 tputs(T("Unknown"));
3911 usage(CMD_OPTIMIZE, stderr);
3917 /* Split a WIM into a spanned set */
3919 imagex_split(int argc, tchar **argv, int cmd)
3923 int write_flags = 0;
3924 unsigned long part_size;
3929 for_opt(c, split_options) {
3931 case IMAGEX_CHECK_OPTION:
3932 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3933 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3945 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3946 if (tmp == argv[2] || *tmp) {
3947 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3948 imagex_error(T("The part size must be an integer or "
3949 "floating-point number of megabytes."));
3952 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3953 imagex_progress_func, NULL);
3957 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3963 usage(CMD_SPLIT, stderr);
3969 #if WIM_MOUNTING_SUPPORTED
3970 /* Unmounts a mounted WIM image. */
3972 imagex_unmount(int argc, tchar **argv, int cmd)
3975 int unmount_flags = 0;
3978 for_opt(c, unmount_options) {
3980 case IMAGEX_COMMIT_OPTION:
3981 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3983 case IMAGEX_CHECK_OPTION:
3984 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3986 case IMAGEX_REBUILD_OPTION:
3987 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3989 case IMAGEX_LAZY_OPTION:
3990 case IMAGEX_FORCE_OPTION:
3991 /* Now, unmount is lazy by default. However, committing
3992 * the image will fail with
3993 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3994 * file descriptors on the WIM image. The
3995 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3996 * descriptors to be closed. */
3997 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3999 case IMAGEX_NEW_IMAGE_OPTION:
4000 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4011 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4012 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4013 imagex_error(T("--new-image is meaningless "
4014 "without --commit also specified!"));
4019 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4020 imagex_progress_func, NULL);
4022 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4023 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4025 "\tNote: Use --commit --force to force changes "
4026 "to be committed, regardless\n"
4027 "\t of open files.\n"));
4034 usage(CMD_UNMOUNT, stderr);
4039 #endif /* WIM_MOUNTING_SUPPORTED */
4042 * Add, delete, or rename files in a WIM image.
4045 imagex_update(int argc, tchar **argv, int cmd)
4047 const tchar *wimfile;
4051 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4052 int write_flags = 0;
4053 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4054 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4055 WIMLIB_ADD_FLAG_VERBOSE |
4056 WIMLIB_ADD_FLAG_WINCONFIG;
4057 int default_delete_flags = 0;
4058 unsigned num_threads = 0;
4060 tchar *cmd_file_contents;
4061 size_t cmd_file_nchars;
4062 struct wimlib_update_command *cmds;
4064 tchar *command_str = NULL;
4065 tchar *config_file = NULL;
4066 tchar *wimboot_config = NULL;
4068 for_opt(c, update_options) {
4070 /* Generic or write options */
4071 case IMAGEX_THREADS_OPTION:
4072 num_threads = parse_num_threads(optarg);
4073 if (num_threads == UINT_MAX)
4076 case IMAGEX_CHECK_OPTION:
4077 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4078 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4080 case IMAGEX_REBUILD_OPTION:
4081 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4083 case IMAGEX_COMMAND_OPTION:
4085 imagex_error(T("--command may only be specified "
4086 "one time. Please provide\n"
4087 " the update commands "
4088 "on standard input instead."));
4091 command_str = tstrdup(optarg);
4093 imagex_error(T("Out of memory!"));
4097 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4098 wimboot_config = optarg;
4100 /* Default delete options */
4101 case IMAGEX_FORCE_OPTION:
4102 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4104 case IMAGEX_RECURSIVE_OPTION:
4105 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4108 /* Global add option */
4109 case IMAGEX_CONFIG_OPTION:
4110 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4111 config_file = optarg;
4114 /* Default add options */
4115 case IMAGEX_VERBOSE_OPTION:
4116 /* No longer does anything. */
4118 case IMAGEX_DEREFERENCE_OPTION:
4119 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4121 case IMAGEX_UNIX_DATA_OPTION:
4122 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4124 case IMAGEX_NO_ACLS_OPTION:
4125 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4127 case IMAGEX_STRICT_ACLS_OPTION:
4128 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4130 case IMAGEX_NO_REPLACE_OPTION:
4131 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4133 case IMAGEX_UNSAFE_COMPACT_OPTION:
4134 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4143 if (argc != 1 && argc != 2)
4147 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4148 imagex_progress_func, NULL);
4150 goto out_free_command_str;
4153 /* Image explicitly specified. */
4154 image = wimlib_resolve_image(wim, argv[1]);
4155 ret = verify_image_exists_and_is_single(image, argv[1],
4158 goto out_wimlib_free;
4160 /* No image specified; default to image 1, but only if the WIM
4161 * contains exactly one image. */
4162 struct wimlib_wim_info info;
4164 wimlib_get_wim_info(wim, &info);
4165 if (info.image_count != 1) {
4166 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4167 wimfile, info.image_count);
4174 /* Read update commands from standard input, or the command string if
4177 cmd_file_contents = NULL;
4178 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4182 goto out_free_cmd_file_contents;
4184 } else if (!wimboot_config) {
4185 if (isatty(STDIN_FILENO)) {
4186 tputs(T("Reading update commands from standard input..."));
4187 recommend_man_page(CMD_UPDATE, stdout);
4189 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4190 if (!cmd_file_contents) {
4192 goto out_wimlib_free;
4195 /* Parse the update commands */
4196 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4200 goto out_free_cmd_file_contents;
4203 cmd_file_contents = NULL;
4208 /* Set default flags and capture config on the update commands */
4209 for (size_t i = 0; i < num_cmds; i++) {
4210 switch (cmds[i].op) {
4211 case WIMLIB_UPDATE_OP_ADD:
4212 cmds[i].add.add_flags |= default_add_flags;
4213 cmds[i].add.config_file = config_file;
4215 case WIMLIB_UPDATE_OP_DELETE:
4216 cmds[i].delete_.delete_flags |= default_delete_flags;
4223 /* Execute the update commands */
4224 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4228 if (wimboot_config) {
4229 /* --wimboot-config=FILE is short for an
4230 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4232 struct wimlib_update_command cmd;
4234 cmd.op = WIMLIB_UPDATE_OP_ADD;
4235 cmd.add.fs_source_path = wimboot_config;
4236 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4237 cmd.add.config_file = NULL;
4238 cmd.add.add_flags = 0;
4240 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4245 /* Overwrite the updated WIM */
4246 ret = wimlib_overwrite(wim, write_flags, num_threads);
4249 out_free_cmd_file_contents:
4250 free(cmd_file_contents);
4253 out_free_command_str:
4258 usage(CMD_UPDATE, stderr);
4261 goto out_free_command_str;
4264 /* Verify a WIM file. */
4266 imagex_verify(int argc, tchar **argv, int cmd)
4269 const tchar *wimfile;
4271 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4272 int verify_flags = 0;
4273 STRING_SET(refglobs);
4276 for_opt(c, verify_options) {
4278 case IMAGEX_REF_OPTION:
4279 ret = string_set_append(&refglobs, optarg);
4281 goto out_free_refglobs;
4283 case IMAGEX_NOCHECK_OPTION:
4284 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4296 imagex_error(T("Must specify a WIM file!"));
4298 imagex_error(T("At most one WIM file can be specified!"));
4304 ret = wimlib_open_wim_with_progress(wimfile,
4307 imagex_progress_func,
4310 goto out_free_refglobs;
4312 ret = wim_reference_globs(wim, &refglobs, open_flags);
4314 goto out_wimlib_free;
4316 ret = wimlib_verify_wim(wim, verify_flags);
4318 tputc(T('\n'), stderr);
4319 imagex_error(T("\"%"TS"\" failed verification!"),
4321 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4322 refglobs.num_strings == 0)
4324 imagex_printf(T("Note: if this WIM file is not standalone, "
4325 "use the --ref option to specify the other parts.\n"));
4328 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4335 string_set_destroy(&refglobs);
4339 usage(CMD_VERIFY, stderr);
4341 goto out_free_refglobs;
4344 struct imagex_command {
4346 int (*func)(int argc, tchar **argv, int cmd);
4349 static const struct imagex_command imagex_commands[] = {
4350 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4351 [CMD_APPLY] = {T("apply"), imagex_apply},
4352 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4353 [CMD_DELETE] = {T("delete"), imagex_delete},
4354 [CMD_DIR ] = {T("dir"), imagex_dir},
4355 [CMD_EXPORT] = {T("export"), imagex_export},
4356 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4357 [CMD_INFO] = {T("info"), imagex_info},
4358 [CMD_JOIN] = {T("join"), imagex_join},
4359 #if WIM_MOUNTING_SUPPORTED
4360 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4361 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4363 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4364 [CMD_SPLIT] = {T("split"), imagex_split},
4365 #if WIM_MOUNTING_SUPPORTED
4366 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4368 [CMD_UPDATE] = {T("update"), imagex_update},
4369 [CMD_VERIFY] = {T("verify"), imagex_verify},
4374 /* Can be a directory or source list file. But source list file is probably
4375 * a rare use case, so just say directory. */
4376 # define SOURCE_STR T("DIRECTORY")
4378 /* Can only be a directory */
4379 # define TARGET_STR T("DIRECTORY")
4382 /* Can be a directory, NTFS volume, or source list file. */
4383 # define SOURCE_STR T("SOURCE")
4385 /* Can be a directory or NTFS volume. */
4386 # define TARGET_STR T("TARGET")
4390 static const tchar *usage_strings[] = {
4393 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4394 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4395 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4396 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4397 " [--wimboot] [--unix-data] [--dereference] [--snapshot]\n"
4401 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4402 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4403 " [--no-attributes] [--rpfix] [--norpfix]\n"
4404 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4405 " [--compact=FORMAT]\n"
4409 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4410 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4411 " [--config=FILE] [--threads=NUM_THREADS]\n"
4412 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4413 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4414 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4419 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4423 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4427 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4428 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4429 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4430 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4431 " [--wimboot] [--solid]\n"
4435 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4436 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4437 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4438 " [--no-attributes] [--include-invalid-names]\n"
4439 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4443 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4444 " [--boot] [--check] [--nocheck] [--xml]\n"
4445 " [--extract-xml FILE] [--header] [--blobs]\n"
4446 " [--image-property NAME=VALUE]\n"
4450 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4452 #if WIM_MOUNTING_SUPPORTED
4455 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4456 " [--check] [--streams-interface=INTERFACE]\n"
4457 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4461 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4462 " [--check] [--streams-interface=INTERFACE]\n"
4463 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4469 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4470 " [--check] [--nocheck] [--solid]\n"
4475 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4477 #if WIM_MOUNTING_SUPPORTED
4480 " %"TS" DIRECTORY\n"
4481 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4486 " %"TS" WIMFILE [IMAGE]\n"
4487 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4488 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4489 " [--command=STRING] [--wimboot-config=FILE]\n"
4494 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4498 static const tchar *invocation_name;
4499 static int invocation_cmd = CMD_NONE;
4501 static const tchar *get_cmd_string(int cmd, bool nospace)
4503 static tchar buf[50];
4504 if (cmd == CMD_NONE) {
4505 return T("wimlib-imagex");
4506 } else if (invocation_cmd != CMD_NONE) {
4507 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4509 const tchar *format;
4512 format = T("%"TS"-%"TS"");
4514 format = T("%"TS" %"TS"");
4515 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4523 static const tchar *s =
4525 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4526 "Copyright (C) 2012-2016 Eric Biggers\n"
4527 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4528 "This is free software: you are free to change and redistribute it.\n"
4529 "There is NO WARRANTY, to the extent permitted by law.\n"
4531 "Report bugs to "PACKAGE_BUGREPORT".\n"
4538 help_or_version(int argc, tchar **argv, int cmd)
4543 for (i = 1; i < argc; i++) {
4545 if (p[0] == T('-') && p[1] == T('-')) {
4547 if (!tstrcmp(p, T("help"))) {
4548 if (cmd == CMD_NONE)
4553 } else if (!tstrcmp(p, T("version"))) {
4562 print_usage_string(int cmd, FILE *fp)
4564 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4568 recommend_man_page(int cmd, FILE *fp)
4570 const tchar *format_str;
4572 format_str = T("Some uncommon options are not listed;\n"
4573 "See %"TS".pdf in the doc directory for more details.\n");
4575 format_str = T("Some uncommon options are not listed;\n"
4576 "Try `man %"TS"' for more details.\n");
4578 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4582 usage(int cmd, FILE *fp)
4584 tfprintf(fp, T("Usage:\n"));
4585 print_usage_string(cmd, fp);
4586 tfprintf(fp, T("\n"));
4587 recommend_man_page(cmd, fp);
4593 tfprintf(fp, T("Usage:\n"));
4594 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4595 print_usage_string(cmd, fp);
4596 tfprintf(fp, T("\n"));
4598 static const tchar *extra =
4601 " %"TS" --version\n"
4604 tfprintf(fp, extra, invocation_name, invocation_name);
4606 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4607 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4608 "For some commands IMAGE may be \"all\".\n"
4610 recommend_man_page(CMD_NONE, fp);
4613 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4614 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4615 * something else), while on Windows the command arguments will be UTF-16LE
4616 * encoded 'wchar_t' strings. */
4619 wmain(int argc, wchar_t **argv, wchar_t **envp)
4621 main(int argc, char **argv)
4628 imagex_info_file = stdout;
4629 invocation_name = tbasename(argv[0]);
4632 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4633 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4637 setlocale(LC_ALL, "");
4638 codeset = nl_langinfo(CODESET);
4639 if (!strstr(codeset, "UTF-8") &&
4640 !strstr(codeset, "UTF8") &&
4641 !strstr(codeset, "utf-8") &&
4642 !strstr(codeset, "utf8"))
4645 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4646 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4647 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4648 " to any value to force wimlib to use UTF-8.\n",
4654 #endif /* !__WIN32__ */
4657 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4658 if (igcase != NULL) {
4659 if (!tstrcmp(igcase, T("no")) ||
4660 !tstrcmp(igcase, T("0")))
4661 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4662 else if (!tstrcmp(igcase, T("yes")) ||
4663 !tstrcmp(igcase, T("1")))
4664 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4667 "WARNING: Ignoring unknown setting of "
4668 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4673 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4675 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4676 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4677 for (int i = 0; i < CMD_MAX; i++) {
4678 if (!tstrcmp(invocation_name + 3,
4679 imagex_commands[i].name))
4688 /* Unless already known from the invocation name, determine which
4689 * command was specified. */
4690 if (cmd == CMD_NONE) {
4692 imagex_error(T("No command specified!\n"));
4696 for (int i = 0; i < CMD_MAX; i++) {
4697 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4702 if (cmd != CMD_NONE) {
4708 /* Handle --help and --version. --help can be either for the program as
4709 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4710 * CMD_NONE). Note: help_or_version() will not return if a --help or
4711 * --version argument was found. */
4712 help_or_version(argc, argv, cmd);
4714 /* Bail if a valid command was not specified. */
4715 if (cmd == CMD_NONE) {
4716 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4721 /* Enable warning and error messages in wimlib to be more user-friendly.
4723 wimlib_set_print_errors(true);
4725 /* Initialize wimlib. */
4726 ret = wimlib_global_init(init_flags);
4728 goto out_check_status;
4730 /* Call the command handler function. */
4731 ret = imagex_commands[cmd].func(argc, argv, cmd);
4733 /* Check for error writing to standard output, especially since for some
4734 * commands, writing to standard output is part of the program's actual
4735 * behavior and not just for informational purposes. */
4736 if (ferror(stdout) || fclose(stdout)) {
4737 imagex_error_with_errno(T("error writing to standard output"));
4742 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4743 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4744 * error code from which an error message can be printed. */
4746 imagex_error(T("Exiting with error code %d:\n"
4748 wimlib_get_error_string(ret));
4749 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4750 imagex_error_with_errno(T("errno"));
4752 /* Make wimlib free any resources it's holding (although this is not
4753 * strictly necessary because the process is ending anyway). */
4754 wimlib_global_cleanup();