4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # include "config.h" /* Need for PACKAGE_VERSION, etc. */
30 #include "wimlib_tchar.h"
49 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
52 # include "imagex-win32.h"
53 # define print_security_descriptor win32_print_security_descriptor
56 # include <langinfo.h>
57 # define print_security_descriptor default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
63 /* Don't confuse the user by presenting the mounting commands on Windows when
64 * they will never work. However on UNIX-like systems we always present them,
65 * even if WITH_FUSE is not defined at this point, as to not tie the build of
66 * wimlib-imagex to a specific build of wimlib. */
68 # define WIM_MOUNTING_SUPPORTED 0
70 # define WIM_MOUNTING_SUPPORTED 1
73 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
76 is_any_path_separator(tchar c)
78 return c == T('/') || c == T('\\');
81 /* Like basename(), but handles both forward and backwards slashes. */
83 tbasename(tchar *path)
85 tchar *p = tstrchr(path, T('\0'));
90 if (!is_any_path_separator(*--p))
98 if (is_any_path_separator(*--p))
103 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
117 #if WIM_MOUNTING_SUPPORTED
123 #if WIM_MOUNTING_SUPPORTED
131 static void usage(int cmd, FILE *fp);
132 static void usage_all(FILE *fp);
133 static void recommend_man_page(int cmd, FILE *fp);
134 static const tchar *get_cmd_string(int cmd, bool nospace);
136 static bool imagex_be_quiet = false;
137 static FILE *imagex_info_file;
139 #define imagex_printf(format, ...) \
140 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
143 IMAGEX_ALLOW_OTHER_OPTION,
147 IMAGEX_CHUNK_SIZE_OPTION,
148 IMAGEX_COMMAND_OPTION,
149 IMAGEX_COMMIT_OPTION,
150 IMAGEX_COMPACT_OPTION,
151 IMAGEX_COMPRESS_OPTION,
152 IMAGEX_COMPRESS_SLOW_OPTION,
153 IMAGEX_CONFIG_OPTION,
155 IMAGEX_DELTA_FROM_OPTION,
156 IMAGEX_DEREFERENCE_OPTION,
157 IMAGEX_DEST_DIR_OPTION,
158 IMAGEX_DETAILED_OPTION,
159 IMAGEX_EXTRACT_XML_OPTION,
162 IMAGEX_HEADER_OPTION,
163 IMAGEX_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,
185 IMAGEX_RESUME_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},
219 /* --resume is undocumented for now as it needs improvement. */
220 {T("resume"), no_argument, NULL, IMAGEX_RESUME_OPTION},
221 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
222 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
226 static const struct option capture_or_append_options[] = {
227 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
228 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
229 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
230 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
231 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
232 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
233 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
234 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
235 {T("pack-streams"), no_argument, NULL, IMAGEX_SOLID_OPTION},
236 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
237 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
238 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
239 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
240 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
241 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
242 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
243 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
244 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
245 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
246 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
247 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
248 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
249 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
250 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
251 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
252 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
253 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
254 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
255 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
256 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
257 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
258 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
259 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
260 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
264 static const struct option delete_options[] = {
265 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
266 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
267 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
271 static const struct option dir_options[] = {
272 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
273 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
274 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
275 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
279 static const struct option export_options[] = {
280 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
281 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
282 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
283 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
284 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
285 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
286 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
287 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
288 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
289 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
290 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
291 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
292 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
293 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
294 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
295 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
296 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
297 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
298 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
299 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
300 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
301 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
305 static const struct option extract_options[] = {
306 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
307 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
308 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
309 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
310 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
311 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
312 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
313 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
314 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
315 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
316 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
317 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
318 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
319 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
320 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
321 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
322 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
326 static const struct option info_options[] = {
327 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
328 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
329 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
330 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
331 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
332 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
333 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
334 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
335 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
336 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
337 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
341 static const struct option join_options[] = {
342 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
346 static const struct option mount_options[] = {
347 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
348 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
349 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
350 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
351 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
352 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
353 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
357 static const struct option optimize_options[] = {
358 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
359 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
360 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
361 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
362 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
363 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
364 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
365 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
366 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
367 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
368 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
369 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
370 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
371 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
372 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
373 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
374 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
375 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
376 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
380 static const struct option split_options[] = {
381 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
385 static const struct option unmount_options[] = {
386 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
387 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
388 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
389 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
390 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
391 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
395 static const struct option update_options[] = {
396 /* Careful: some of the options here set the defaults for update
397 * commands, but the flags given to an actual update command (and not to
398 * `imagex update' itself are also handled in
399 * update_command_add_option(). */
400 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
401 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
402 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
403 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
404 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
406 /* Default delete options */
407 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
408 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
410 /* Global add option */
411 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
413 /* Default add options */
414 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
415 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
416 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
417 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
418 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
419 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
420 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
421 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
426 static const struct option verify_options[] = {
427 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
428 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
434 # define _format_attribute(type, format_str, args_start) \
435 __attribute__((format(type, format_str, args_start)))
437 # define _format_attribute(type, format_str, args_start)
440 /* Print formatted error message to stderr. */
441 static void _format_attribute(printf, 1, 2)
442 imagex_error(const tchar *format, ...)
445 va_start(va, format);
446 tfputs(T("ERROR: "), stderr);
447 tvfprintf(stderr, format, va);
448 tputc(T('\n'), stderr);
452 /* Print formatted error message to stderr. */
453 static void _format_attribute(printf, 1, 2)
454 imagex_error_with_errno(const tchar *format, ...)
456 int errno_save = errno;
458 va_start(va, format);
459 tfputs(T("ERROR: "), stderr);
460 tvfprintf(stderr, format, va);
461 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
466 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
468 if (image == WIMLIB_NO_IMAGE) {
469 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
470 " Please specify a 1-based image index or "
471 "image name. To list the images\n"
472 " contained in the WIM archive, run\n"
474 " %"TS" \"%"TS"\"\n"),
475 image_name, wim_name,
476 get_cmd_string(CMD_INFO, false), wim_name);
477 return WIMLIB_ERR_INVALID_IMAGE;
483 verify_image_is_single(int image)
485 if (image == WIMLIB_ALL_IMAGES) {
486 imagex_error(T("Cannot specify all images for this action!"));
487 return WIMLIB_ERR_INVALID_IMAGE;
493 verify_image_exists_and_is_single(int image, const tchar *image_name,
494 const tchar *wim_name)
497 ret = verify_image_exists(image, image_name, wim_name);
499 ret = verify_image_is_single(image);
504 print_available_compression_types(FILE *fp)
506 static const tchar *s =
508 "Available compression types:\n"
511 " xpress (alias: \"fast\")\n"
512 " lzx (alias: \"maximum\") (default for capture)\n"
513 " lzms (alias: \"recovery\")\n"
519 /* Parse the argument to --compress */
521 get_compression_type(tchar *optarg)
524 unsigned int compression_level = 0;
527 plevel = tstrchr(optarg, T(':'));
533 ultmp = tstrtoul(plevel, &ptmp, 10);
534 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
535 imagex_error(T("Compression level must be a positive integer! "
536 "e.g. --compress=lzx:80"));
537 return WIMLIB_COMPRESSION_TYPE_INVALID;
539 compression_level = ultmp;
542 if (!tstrcasecmp(optarg, T("maximum")) ||
543 !tstrcasecmp(optarg, T("lzx")) ||
544 !tstrcasecmp(optarg, T("max")))
545 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
546 else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress")))
547 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
548 else if (!tstrcasecmp(optarg, T("recovery")) || !tstrcasecmp(optarg, T("lzms")))
549 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
550 else if (!tstrcasecmp(optarg, T("none")))
551 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
553 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
554 print_available_compression_types(stderr);
555 return WIMLIB_COMPRESSION_TYPE_INVALID;
558 if (compression_level != 0)
559 wimlib_set_default_compression_level(ctype, compression_level);
563 /* Parse the argument to --compact */
565 set_compact_mode(const tchar *arg, int *extract_flags)
568 if (!tstrcasecmp(arg, T("xpress4k")))
569 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
570 else if (!tstrcasecmp(arg, T("xpress8k")))
571 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
572 else if (!tstrcasecmp(arg, T("xpress16k")))
573 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
574 else if (!tstrcasecmp(arg, T("lzx")))
575 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
578 *extract_flags |= flag;
583 "\"%"TS"\" is not a recognized System Compression format. The options are:"
585 " --compact=xpress4k\n"
586 " --compact=xpress8k\n"
587 " --compact=xpress16k\n"
595 set_compress_slow(void)
598 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
599 " Use the '--compress=TYPE:LEVEL' option instead.\n");
601 wimlib_set_default_compression_level(-1, 100);
606 unsigned num_strings;
607 unsigned num_alloc_strings;
610 #define STRING_SET_INITIALIZER \
611 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
613 #define STRING_SET(_strings) \
614 struct string_set _strings = STRING_SET_INITIALIZER
617 string_set_append(struct string_set *set, tchar *glob)
619 unsigned num_alloc_strings = set->num_alloc_strings;
621 if (set->num_strings == num_alloc_strings) {
624 num_alloc_strings += 4;
625 new_strings = realloc(set->strings,
626 sizeof(set->strings[0]) * num_alloc_strings);
628 imagex_error(T("Out of memory!"));
631 set->strings = new_strings;
632 set->num_alloc_strings = num_alloc_strings;
634 set->strings[set->num_strings++] = glob;
639 string_set_destroy(struct string_set *set)
645 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
647 return wimlib_reference_resource_files(wim, (const tchar **)set->strings,
649 WIMLIB_REF_FLAG_GLOB_ENABLE,
654 append_image_property_argument(struct string_set *image_properties)
656 if (!tstrchr(optarg, '=')) {
657 imagex_error(T("'--image-property' argument "
658 "must be in the form NAME=VALUE"));
661 return string_set_append(image_properties, optarg);
665 apply_image_properties(struct string_set *image_properties,
666 WIMStruct *wim, int image, bool *any_changes_ret)
668 bool any_changes = false;
669 for (unsigned i = 0; i < image_properties->num_strings; i++) {
671 const tchar *current_value;
674 name = image_properties->strings[i];
675 value = tstrchr(name, '=');
678 current_value = wimlib_get_image_property(wim, image, name);
679 if (current_value && !tstrcmp(current_value, value)) {
680 imagex_printf(T("The %"TS" property of image %d "
681 "already has value \"%"TS"\".\n"),
684 imagex_printf(T("Setting the %"TS" property of image "
685 "%d to \"%"TS"\".\n"),
687 ret = wimlib_set_image_property(wim, image, name, value);
694 *any_changes_ret = any_changes;
699 do_resource_not_found_warning(const tchar *wimfile,
700 const struct wimlib_wim_info *info,
701 const struct string_set *refglobs)
703 if (info->total_parts > 1) {
704 if (refglobs->num_strings == 0) {
705 imagex_error(T("\"%"TS"\" is part of a split WIM. "
706 "Use --ref to specify the other parts."),
709 imagex_error(T("Perhaps the '--ref' argument did not "
710 "specify all other parts of the split "
714 imagex_error(T("If this is a delta WIM, use the --ref argument "
715 "to specify the WIM(s) on which it is based."));
720 do_metadata_not_found_warning(const tchar *wimfile,
721 const struct wimlib_wim_info *info)
723 if (info->part_number != 1) {
724 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
725 " You must specify the first part."),
730 /* Returns the size of a file given its name, or -1 if the file does not exist
731 * or its size cannot be determined. */
733 file_get_size(const tchar *filename)
736 if (tstat(filename, &st) == 0)
743 PARSE_STRING_SUCCESS = 0,
744 PARSE_STRING_FAILURE = 1,
745 PARSE_STRING_NONE = 2,
749 * Parses a string token from an array of characters.
751 * Tokens are either whitespace-delimited, or double or single-quoted.
753 * @line_p: Pointer to the pointer to the line of data. Will be updated
754 * to point past the string token iff the return value is
755 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
758 * @len_p: @len_p initially stores the length of the line of data, which may
759 * be 0, and it will be updated to the number of bytes remaining in
760 * the line iff the return value is PARSE_STRING_SUCCESS.
762 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
763 * parsed string token will be returned here.
765 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
766 * PARSE_STRING_FAILURE if the data was invalid due to a missing
767 * closing quote; or PARSE_STRING_NONE if the line ended before the
768 * beginning of a string token was found.
771 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
774 tchar *line = *line_p;
778 /* Skip leading whitespace */
781 return PARSE_STRING_NONE;
782 if (!istspace(*line) && *line != T('\0'))
788 if (quote_char == T('"') || quote_char == T('\'')) {
793 line = tmemchr(line, quote_char, len);
795 imagex_error(T("Missing closing quote: %"TS), fn - 1);
796 return PARSE_STRING_FAILURE;
799 /* Unquoted string. Go until whitespace. Line is terminated
800 * by '\0', so no need to check 'len'. */
804 } while (!istspace(*line) && *line != T('\0'));
811 return PARSE_STRING_SUCCESS;
814 /* Parses a line of data (not an empty line or comment) in the source list file
815 * format. (See the man page for 'wimlib-imagex capture' for details on this
816 * format and the meaning.)
818 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
819 * len == 0. The data in @line will be modified by this function call.
821 * @len: Length of the line of data.
823 * @source: On success, the capture source and target described by the line is
824 * written into this destination. Note that it will contain pointers
825 * to data in the @line array.
827 * Returns true if the line was valid; false otherwise. */
829 parse_source_list_line(tchar *line, size_t len,
830 struct wimlib_capture_source *source)
834 ret = parse_string(&line, &len, &source->fs_source_path);
835 if (ret != PARSE_STRING_SUCCESS)
837 ret = parse_string(&line, &len, &source->wim_target_path);
838 if (ret == PARSE_STRING_NONE)
839 source->wim_target_path = source->fs_source_path;
840 return ret != PARSE_STRING_FAILURE;
843 /* Returns %true if the given line of length @len > 0 is a comment or empty line
844 * in the source list file format. */
846 is_comment_line(const tchar *line, size_t len)
849 if (*line == T('#') || *line == T(';'))
851 if (!istspace(*line) && *line != T('\0'))
861 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
864 tchar *contents = *contents_p;
865 size_t nchars = *nchars_p;
868 for (i = 0; i < nchars; i++)
869 if (contents[i] == T('\n'))
872 /* Handle last line not terminated by a newline */
873 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
874 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
876 imagex_error(T("Out of memory!"));
879 contents[nchars] = T('\n');
880 *contents_p = contents;
888 /* Parses a file in the source list format. (See the man page for
889 * 'wimlib-imagex capture' for details on this format and the meaning.)
891 * @source_list_contents: Contents of the source list file. Note that this
892 * buffer will be modified to save memory allocations,
893 * and cannot be freed until the returned array of
894 * wimlib_capture_source's has also been freed.
896 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
899 * @nsources_ret: On success, the length of the returned array is
902 * Returns: An array of `struct wimlib_capture_source's that can be passed to
903 * the wimlib_add_image_multisource() function to specify how a WIM image is to
905 static struct wimlib_capture_source *
906 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
907 size_t *nsources_ret)
911 struct wimlib_capture_source *sources;
914 nlines = text_file_count_lines(source_list_contents_p,
915 &source_list_nchars);
919 /* Always allocate at least 1 slot, just in case the implementation of
920 * calloc() returns NULL if 0 bytes are requested. */
921 sources = calloc(nlines ?: 1, sizeof(*sources));
923 imagex_error(T("out of memory"));
926 p = *source_list_contents_p;
928 for (i = 0; i < nlines; i++) {
929 /* XXX: Could use rawmemchr() here instead, but it may not be
930 * available on all platforms. */
931 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
932 size_t len = endp - p + 1;
934 if (!is_comment_line(p, len)) {
935 if (!parse_source_list_line(p, len, &sources[j++])) {
947 /* Reads the contents of a file into memory. */
949 file_get_contents(const tchar *filename, size_t *len_ret)
956 if (tstat(filename, &stbuf) != 0) {
957 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
962 fp = tfopen(filename, T("rb"));
964 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
968 buf = malloc(len ? len : 1);
970 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
971 "contents of file \"%"TS"\""), len, filename);
974 if (fread(buf, 1, len, fp) != len) {
975 imagex_error_with_errno(T("Failed to read %zu bytes from the "
976 "file \"%"TS"\""), len, filename);
990 /* Read standard input until EOF and return the full contents in a malloc()ed
991 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
994 stdin_get_contents(size_t *len_ret)
996 /* stdin can, of course, be a pipe or other non-seekable file, so the
997 * total length of the data cannot be pre-determined */
999 size_t newlen = 1024;
1003 char *p = realloc(buf, newlen);
1004 size_t bytes_read, bytes_to_read;
1006 imagex_error(T("out of memory while reading stdin"));
1010 bytes_to_read = newlen - pos;
1011 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1013 if (bytes_read != bytes_to_read) {
1018 imagex_error_with_errno(T("error reading stdin"));
1032 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1035 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1037 *num_tchars_ret = num_bytes;
1039 #else /* !__WIN32__ */
1040 /* On Windows, translate the text to UTF-16LE */
1044 if (num_bytes >= 2 &&
1045 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1046 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1048 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1049 * with something that looks like an ASCII character encoded as
1050 * a UTF-16LE code unit. Assume the file is encoded as
1051 * UTF-16LE. This is not a 100% reliable check. */
1052 num_wchars = num_bytes / 2;
1053 text_wstr = (wchar_t*)text;
1055 /* File does not look like UTF-16LE. Assume it is encoded in
1056 * the current Windows code page. I think these are always
1057 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1058 * should work as expected. */
1059 text_wstr = win32_mbs_to_wcs(text,
1064 *num_tchars_ret = num_wchars;
1066 #endif /* __WIN32__ */
1070 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1075 contents = file_get_contents(filename, &num_bytes);
1078 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1082 stdin_get_text_contents(size_t *num_tchars_ret)
1087 contents = stdin_get_contents(&num_bytes);
1090 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1093 #define TO_PERCENT(numerator, denominator) \
1094 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1096 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1097 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1098 #define KIBIBYTE_MIN_NBYTES 10000ULL
1101 get_unit(uint64_t total_bytes, const tchar **name_ret)
1103 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1104 *name_ret = T("GiB");
1106 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1107 *name_ret = T("MiB");
1109 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1110 *name_ret = T("KiB");
1113 *name_ret = T("bytes");
1118 static struct wimlib_progress_info_scan last_scan_progress;
1121 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1123 uint64_t prev_count, cur_count;
1125 prev_count = last_scan_progress.num_nondirs_scanned +
1126 last_scan_progress.num_dirs_scanned;
1127 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1129 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1130 cur_count % 128 == 0)
1132 unsigned unit_shift;
1133 const tchar *unit_name;
1135 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1136 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1137 "%"PRIu64" directories) "),
1138 scan->num_bytes_scanned >> unit_shift,
1140 scan->num_nondirs_scanned,
1141 scan->num_dirs_scanned);
1142 last_scan_progress = *scan;
1145 /* Progress callback function passed to various wimlib functions. */
1146 static enum wimlib_progress_status
1147 imagex_progress_func(enum wimlib_progress_msg msg,
1148 union wimlib_progress_info *info,
1149 void *_ignored_context)
1151 unsigned percent_done;
1152 unsigned unit_shift;
1153 const tchar *unit_name;
1155 if (imagex_be_quiet)
1156 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1158 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1160 static bool first = true;
1162 imagex_printf(T("Writing %"TS"-compressed data "
1163 "using %u thread%"TS"\n"),
1164 wimlib_get_compression_type_string(
1165 info->write_streams.compression_type),
1166 info->write_streams.num_threads,
1167 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1171 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1172 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1173 info->write_streams.total_bytes);
1175 imagex_printf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
1176 "written (%u%% done)"),
1177 info->write_streams.completed_bytes >> unit_shift,
1179 info->write_streams.total_bytes >> unit_shift,
1182 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1183 imagex_printf(T("\n"));
1185 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1186 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1187 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1188 imagex_printf(T("\n"));
1190 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1191 info->scan.wim_target_path);
1193 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1195 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1196 switch (info->scan.status) {
1197 case WIMLIB_SCAN_DENTRY_OK:
1198 report_scan_progress(&info->scan, false);
1200 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1201 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1203 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1204 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1205 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1207 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1208 /* Symlink fixups are enabled by default. This is
1209 * mainly intended for Windows, which for some reason
1210 * uses absolute junctions (with drive letters!) in the
1211 * default installation. On UNIX-like systems, warn the
1212 * user when fixing the target of an absolute symbolic
1213 * link, so they know to disable this if they want. */
1215 imagex_printf(T("\nWARNING: Adjusted target of "
1216 "absolute symbolic link \"%"TS"\"\n"
1217 " (Use --norpfix to capture "
1218 "absolute symbolic links as-is)\n"),
1219 info->scan.cur_path);
1226 case WIMLIB_PROGRESS_MSG_SCAN_END:
1227 report_scan_progress(&info->scan, true);
1228 imagex_printf(T("\n"));
1230 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1231 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1232 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1233 info->integrity.total_bytes);
1234 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1235 "of %"PRIu64" %"TS" (%u%%) done"),
1236 info->integrity.filename,
1237 info->integrity.completed_bytes >> unit_shift,
1239 info->integrity.total_bytes >> unit_shift,
1242 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1243 imagex_printf(T("\n"));
1245 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1246 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1247 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1248 info->integrity.total_bytes);
1249 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1250 "of %"PRIu64" %"TS" (%u%%) done"),
1251 info->integrity.completed_bytes >> unit_shift,
1253 info->integrity.total_bytes >> unit_shift,
1256 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1257 imagex_printf(T("\n"));
1259 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1260 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1261 "to %"TS" \"%"TS"\"\n"),
1262 info->extract.image,
1263 info->extract.image_name,
1264 info->extract.wimfile_name,
1265 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1266 T("NTFS volume") : T("directory")),
1267 info->extract.target);
1269 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1270 if (info->extract.end_file_count >= 2000) {
1271 percent_done = TO_PERCENT(info->extract.current_file_count,
1272 info->extract.end_file_count);
1273 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1274 info->extract.current_file_count,
1275 info->extract.end_file_count, percent_done);
1276 if (info->extract.current_file_count == info->extract.end_file_count)
1277 imagex_printf(T("\n"));
1280 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1281 percent_done = TO_PERCENT(info->extract.completed_bytes,
1282 info->extract.total_bytes);
1283 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1284 imagex_printf(T("\rExtracting file data: "
1285 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1286 info->extract.completed_bytes >> unit_shift,
1288 info->extract.total_bytes >> unit_shift,
1291 if (info->extract.completed_bytes >= info->extract.total_bytes)
1292 imagex_printf(T("\n"));
1294 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1295 if (info->extract.end_file_count >= 2000) {
1296 percent_done = TO_PERCENT(info->extract.current_file_count,
1297 info->extract.end_file_count);
1298 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1299 info->extract.current_file_count,
1300 info->extract.end_file_count, percent_done);
1301 if (info->extract.current_file_count == info->extract.end_file_count)
1302 imagex_printf(T("\n"));
1305 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1306 if (info->extract.total_parts != 1) {
1307 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1308 info->extract.part_number,
1309 info->extract.total_parts);
1312 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1313 percent_done = TO_PERCENT(info->split.completed_bytes,
1314 info->split.total_bytes);
1315 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1316 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1317 "%"PRIu64" %"TS" (%u%%) written\n"),
1318 info->split.part_name,
1319 info->split.cur_part_number,
1320 info->split.total_parts,
1321 info->split.completed_bytes >> unit_shift,
1323 info->split.total_bytes >> unit_shift,
1327 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1328 if (info->split.completed_bytes == info->split.total_bytes) {
1329 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1330 info->split.cur_part_number,
1331 info->split.total_parts);
1334 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1335 switch (info->update.command->op) {
1336 case WIMLIB_UPDATE_OP_DELETE:
1337 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1338 info->update.command->delete_.wim_path);
1340 case WIMLIB_UPDATE_OP_RENAME:
1341 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1342 info->update.command->rename.wim_source_path,
1343 info->update.command->rename.wim_target_path);
1345 case WIMLIB_UPDATE_OP_ADD:
1350 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1351 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1352 info->replace.path_in_wim);
1354 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1355 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1356 info->wimboot_exclude.path_in_wim);
1358 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1359 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1360 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1361 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1362 info->unmount.mounted_wim,
1363 info->unmount.mounted_image);
1365 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1366 info->unmount.mounted_wim,
1367 info->unmount.mounted_image);
1368 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1372 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1373 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1374 info->verify_image.current_image,
1375 info->verify_image.total_images);
1377 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1378 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1379 info->verify_streams.total_bytes);
1380 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1381 imagex_printf(T("\rVerifying file data: "
1382 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1383 info->verify_streams.completed_bytes >> unit_shift,
1385 info->verify_streams.total_bytes >> unit_shift,
1388 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1389 imagex_printf(T("\n"));
1394 fflush(imagex_info_file);
1395 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1399 parse_num_threads(const tchar *optarg)
1402 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1403 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1404 imagex_error(T("Number of threads must be a non-negative integer!"));
1412 parse_chunk_size(const tchar *optarg)
1415 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1416 if (chunk_size == 0) {
1417 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1418 " with optional K, M, or G suffix"));
1422 if (*tmp == T('k') || *tmp == T('K')) {
1425 } else if (*tmp == T('m') || *tmp == T('M')) {
1428 } else if (*tmp == T('g') || *tmp == T('G')) {
1432 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1433 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1437 if (chunk_size >= UINT32_MAX) {
1438 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1446 * Parse an option passed to an update command.
1448 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1451 * @option: Text string for the option (beginning with --)
1453 * @cmd: `struct wimlib_update_command' that is being constructed for
1456 * Returns true if the option was recognized; false if not.
1459 update_command_add_option(int op, const tchar *option,
1460 struct wimlib_update_command *cmd)
1462 bool recognized = true;
1464 case WIMLIB_UPDATE_OP_ADD:
1465 if (!tstrcmp(option, T("--verbose")))
1466 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1467 else if (!tstrcmp(option, T("--unix-data")))
1468 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1469 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1470 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1471 else if (!tstrcmp(option, T("--strict-acls")))
1472 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1473 else if (!tstrcmp(option, T("--dereference")))
1474 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1475 else if (!tstrcmp(option, T("--no-replace")))
1476 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1480 case WIMLIB_UPDATE_OP_DELETE:
1481 if (!tstrcmp(option, T("--force")))
1482 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1483 else if (!tstrcmp(option, T("--recursive")))
1484 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1495 /* How many nonoption arguments each `imagex update' command expects */
1496 static const unsigned update_command_num_nonoptions[] = {
1497 [WIMLIB_UPDATE_OP_ADD] = 2,
1498 [WIMLIB_UPDATE_OP_DELETE] = 1,
1499 [WIMLIB_UPDATE_OP_RENAME] = 2,
1503 update_command_add_nonoption(int op, const tchar *nonoption,
1504 struct wimlib_update_command *cmd,
1505 unsigned num_nonoptions)
1508 case WIMLIB_UPDATE_OP_ADD:
1509 if (num_nonoptions == 0)
1510 cmd->add.fs_source_path = (tchar*)nonoption;
1512 cmd->add.wim_target_path = (tchar*)nonoption;
1514 case WIMLIB_UPDATE_OP_DELETE:
1515 cmd->delete_.wim_path = (tchar*)nonoption;
1517 case WIMLIB_UPDATE_OP_RENAME:
1518 if (num_nonoptions == 0)
1519 cmd->rename.wim_source_path = (tchar*)nonoption;
1521 cmd->rename.wim_target_path = (tchar*)nonoption;
1527 * Parse a command passed on stdin to `imagex update'.
1529 * @line: Text of the command.
1530 * @len: Length of the line, including a null terminator
1533 * @command: A `struct wimlib_update_command' to fill in from the parsed
1536 * @line_number: Line number of the command, for diagnostics.
1538 * Returns true on success; returns false on parse error.
1541 parse_update_command(tchar *line, size_t len,
1542 struct wimlib_update_command *command,
1546 tchar *command_name;
1548 size_t num_nonoptions;
1550 /* Get the command name ("add", "delete", "rename") */
1551 ret = parse_string(&line, &len, &command_name);
1552 if (ret != PARSE_STRING_SUCCESS)
1555 if (!tstrcasecmp(command_name, T("add"))) {
1556 op = WIMLIB_UPDATE_OP_ADD;
1557 } else if (!tstrcasecmp(command_name, T("delete"))) {
1558 op = WIMLIB_UPDATE_OP_DELETE;
1559 } else if (!tstrcasecmp(command_name, T("rename"))) {
1560 op = WIMLIB_UPDATE_OP_RENAME;
1562 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1563 command_name, line_number);
1568 /* Parse additional options and non-options as needed */
1573 ret = parse_string(&line, &len, &next_string);
1574 if (ret == PARSE_STRING_NONE) /* End of line */
1576 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1578 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1580 if (!update_command_add_option(op, next_string, command))
1582 imagex_error(T("Unrecognized option \"%"TS"\" to "
1583 "update command \"%"TS"\" on line %zu"),
1584 next_string, command_name, line_number);
1590 if (num_nonoptions == update_command_num_nonoptions[op])
1592 imagex_error(T("Unexpected argument \"%"TS"\" in "
1593 "update command on line %zu\n"
1594 " (The \"%"TS"\" command only "
1595 "takes %zu nonoption arguments!)\n"),
1596 next_string, line_number,
1597 command_name, num_nonoptions);
1600 update_command_add_nonoption(op, next_string,
1601 command, num_nonoptions);
1606 if (num_nonoptions != update_command_num_nonoptions[op]) {
1607 imagex_error(T("Not enough arguments to update command "
1608 "\"%"TS"\" on line %zu"), command_name, line_number);
1614 static struct wimlib_update_command *
1615 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1616 size_t *num_cmds_ret)
1620 struct wimlib_update_command *cmds;
1623 nlines = text_file_count_lines(cmd_file_contents_p,
1628 /* Always allocate at least 1 slot, just in case the implementation of
1629 * calloc() returns NULL if 0 bytes are requested. */
1630 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1632 imagex_error(T("out of memory"));
1635 p = *cmd_file_contents_p;
1637 for (i = 0; i < nlines; i++) {
1638 /* XXX: Could use rawmemchr() here instead, but it may not be
1639 * available on all platforms. */
1640 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1641 size_t len = endp - p + 1;
1643 if (!is_comment_line(p, len)) {
1644 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1655 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1656 * one image from a WIM file to an NTFS volume. */
1658 imagex_apply(int argc, tchar **argv, int cmd)
1662 int image = WIMLIB_NO_IMAGE;
1664 struct wimlib_wim_info info;
1666 const tchar *wimfile;
1667 const tchar *target;
1668 const tchar *image_num_or_name = NULL;
1669 int extract_flags = 0;
1671 STRING_SET(refglobs);
1673 for_opt(c, apply_options) {
1675 case IMAGEX_CHECK_OPTION:
1676 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1678 case IMAGEX_VERBOSE_OPTION:
1679 /* No longer does anything. */
1681 case IMAGEX_REF_OPTION:
1682 ret = string_set_append(&refglobs, optarg);
1684 goto out_free_refglobs;
1686 case IMAGEX_UNIX_DATA_OPTION:
1687 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1689 case IMAGEX_NO_ACLS_OPTION:
1690 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1692 case IMAGEX_STRICT_ACLS_OPTION:
1693 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1695 case IMAGEX_NO_ATTRIBUTES_OPTION:
1696 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1698 case IMAGEX_NORPFIX_OPTION:
1699 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1701 case IMAGEX_RPFIX_OPTION:
1702 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1704 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1705 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1706 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1708 case IMAGEX_RESUME_OPTION:
1709 extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME;
1711 case IMAGEX_WIMBOOT_OPTION:
1712 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1714 case IMAGEX_COMPACT_OPTION:
1715 ret = set_compact_mode(optarg, &extract_flags);
1717 goto out_free_refglobs;
1725 if (argc != 2 && argc != 3)
1730 if (!tstrcmp(wimfile, T("-"))) {
1731 /* Attempt to apply pipable WIM from standard input. */
1733 image_num_or_name = NULL;
1736 image_num_or_name = argv[1];
1741 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1742 imagex_progress_func, NULL);
1744 goto out_free_refglobs;
1746 wimlib_get_wim_info(wim, &info);
1749 /* Image explicitly specified. */
1750 image_num_or_name = argv[1];
1751 image = wimlib_resolve_image(wim, image_num_or_name);
1752 ret = verify_image_exists(image, image_num_or_name, wimfile);
1754 goto out_wimlib_free;
1757 /* No image specified; default to image 1, but only if the WIM
1758 * contains exactly one image. */
1760 if (info.image_count != 1) {
1761 imagex_error(T("\"%"TS"\" contains %d images; "
1762 "Please select one (or all)."),
1763 wimfile, info.image_count);
1772 if (refglobs.num_strings) {
1774 imagex_error(T("Can't specify --ref when applying from stdin!"));
1776 goto out_wimlib_free;
1778 ret = wim_reference_globs(wim, &refglobs, open_flags);
1780 goto out_wimlib_free;
1785 /* Interpret a regular file or block device target as an NTFS
1789 if (tstat(target, &stbuf)) {
1790 if (errno != ENOENT) {
1791 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1794 goto out_wimlib_free;
1797 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1798 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1804 ret = wimlib_extract_image(wim, image, target, extract_flags);
1806 set_fd_to_binary_mode(STDIN_FILENO);
1807 ret = wimlib_extract_image_from_pipe_with_progress(
1812 imagex_progress_func,
1816 imagex_printf(T("Done applying WIM image.\n"));
1817 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1819 do_resource_not_found_warning(wimfile, &info, &refglobs);
1821 imagex_error(T( "If you are applying an image "
1822 "from a split pipable WIM,\n"
1823 " make sure you have "
1824 "concatenated together all parts."));
1826 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1827 do_metadata_not_found_warning(wimfile, &info);
1832 string_set_destroy(&refglobs);
1836 usage(CMD_APPLY, stderr);
1838 goto out_free_refglobs;
1841 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1842 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1843 * the desired image. 'wimlib-imagex append': add a new image to an existing
1846 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1850 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1851 WIMLIB_ADD_FLAG_WINCONFIG |
1852 WIMLIB_ADD_FLAG_VERBOSE;
1853 int write_flags = 0;
1854 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1855 uint32_t chunk_size = UINT32_MAX;
1856 uint32_t solid_chunk_size = UINT32_MAX;
1857 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1858 const tchar *wimfile;
1861 STRING_SET(image_properties);
1864 STRING_SET(base_wimfiles);
1865 WIMStruct **base_wims;
1867 WIMStruct *template_wim;
1868 const tchar *template_wimfile = NULL;
1869 const tchar *template_image_name_or_num = NULL;
1870 int template_image = WIMLIB_NO_IMAGE;
1873 unsigned num_threads = 0;
1878 tchar *config_file = NULL;
1880 bool source_list = false;
1881 size_t source_list_nchars = 0;
1882 tchar *source_list_contents;
1883 bool capture_sources_malloced;
1884 struct wimlib_capture_source *capture_sources;
1886 bool name_defaulted;
1888 for_opt(c, capture_or_append_options) {
1890 case IMAGEX_BOOT_OPTION:
1891 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1893 case IMAGEX_CHECK_OPTION:
1894 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1895 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1897 case IMAGEX_NOCHECK_OPTION:
1898 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1900 case IMAGEX_CONFIG_OPTION:
1901 config_file = optarg;
1902 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1904 case IMAGEX_COMPRESS_OPTION:
1905 compression_type = get_compression_type(optarg);
1906 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1909 case IMAGEX_COMPRESS_SLOW_OPTION:
1910 set_compress_slow();
1912 case IMAGEX_CHUNK_SIZE_OPTION:
1913 chunk_size = parse_chunk_size(optarg);
1914 if (chunk_size == UINT32_MAX)
1917 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1918 solid_chunk_size = parse_chunk_size(optarg);
1919 if (solid_chunk_size == UINT32_MAX)
1922 case IMAGEX_SOLID_COMPRESS_OPTION:
1923 solid_ctype = get_compression_type(optarg);
1924 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1927 case IMAGEX_SOLID_OPTION:
1928 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1930 case IMAGEX_NO_SOLID_SORT_OPTION:
1931 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1933 case IMAGEX_FLAGS_OPTION: {
1934 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1935 tsprintf(p, T("FLAGS=%"TS), optarg);
1936 ret = string_set_append(&image_properties, p);
1941 case IMAGEX_IMAGE_PROPERTY_OPTION:
1942 ret = append_image_property_argument(&image_properties);
1946 case IMAGEX_DEREFERENCE_OPTION:
1947 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1949 case IMAGEX_VERBOSE_OPTION:
1950 /* No longer does anything. */
1952 case IMAGEX_THREADS_OPTION:
1953 num_threads = parse_num_threads(optarg);
1954 if (num_threads == UINT_MAX)
1957 case IMAGEX_REBUILD_OPTION:
1958 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1960 case IMAGEX_UNIX_DATA_OPTION:
1961 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1963 case IMAGEX_SOURCE_LIST_OPTION:
1966 case IMAGEX_NO_ACLS_OPTION:
1967 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1969 case IMAGEX_STRICT_ACLS_OPTION:
1970 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1972 case IMAGEX_RPFIX_OPTION:
1973 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1975 case IMAGEX_NORPFIX_OPTION:
1976 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1978 case IMAGEX_PIPABLE_OPTION:
1979 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1981 case IMAGEX_NOT_PIPABLE_OPTION:
1982 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1984 case IMAGEX_UPDATE_OF_OPTION:
1985 if (template_image_name_or_num) {
1986 imagex_error(T("'--update-of' can only be "
1987 "specified one time!"));
1991 colon = tstrrchr(optarg, T(':'));
1994 template_wimfile = optarg;
1996 template_image_name_or_num = colon + 1;
1998 template_wimfile = NULL;
1999 template_image_name_or_num = optarg;
2003 case IMAGEX_DELTA_FROM_OPTION:
2004 if (cmd != CMD_CAPTURE) {
2005 imagex_error(T("'--delta-from' is only "
2006 "valid for capture!"));
2009 ret = string_set_append(&base_wimfiles, optarg);
2012 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2014 case IMAGEX_WIMBOOT_OPTION:
2015 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2017 case IMAGEX_UNSAFE_COMPACT_OPTION:
2018 if (cmd != CMD_APPEND) {
2019 imagex_error(T("'--unsafe-compact' is only "
2020 "valid for append!"));
2023 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2032 if (argc < 2 || argc > 4)
2038 /* Set default compression type and parameters. */
2041 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2042 /* No compression type specified. Use the default. */
2044 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2045 /* With --wimboot, default to XPRESS compression. */
2046 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2047 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2048 /* With --solid, default to LZMS compression. (However,
2049 * this will not affect solid resources!) */
2050 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2052 /* Otherwise, default to LZX compression. */
2053 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2057 if (!tstrcmp(wimfile, T("-"))) {
2058 /* Writing captured WIM to standard output. */
2060 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2061 imagex_error("Can't write a non-pipable WIM to "
2062 "standard output! Specify --pipable\n"
2063 " if you want to create a pipable WIM "
2064 "(but read the docs first).");
2068 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2070 if (cmd == CMD_APPEND) {
2071 imagex_error(T("Using standard output for append does "
2072 "not make sense."));
2075 wim_fd = STDOUT_FILENO;
2077 imagex_info_file = stderr;
2078 set_fd_to_binary_mode(wim_fd);
2081 /* If template image was specified using --update-of=IMAGE rather
2082 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2083 if (template_image_name_or_num && !template_wimfile) {
2084 if (base_wimfiles.num_strings == 1) {
2085 /* Capturing delta WIM based on single WIM: default to
2087 template_wimfile = base_wimfiles.strings[0];
2088 } else if (cmd == CMD_APPEND) {
2089 /* Appending to WIM: default to WIM being appended to.
2091 template_wimfile = wimfile;
2093 /* Capturing a normal (non-delta) WIM, so the WIM file
2094 * *must* be explicitly specified. */
2095 if (base_wimfiles.num_strings > 1) {
2096 imagex_error(T("For capture of delta WIM "
2097 "based on multiple existing "
2099 " '--update-of' must "
2100 "specify WIMFILE:IMAGE!"));
2102 imagex_error(T("For capture of non-delta WIM, "
2103 "'--update-of' must specify "
2112 name_defaulted = false;
2114 /* Set default name to SOURCE argument, omitting any directory
2115 * prefixes and trailing slashes. This requires making a copy
2116 * of @source. Leave some free characters at the end in case we
2117 * append a number to keep the name unique. */
2118 size_t source_name_len;
2120 source_name_len = tstrlen(source);
2121 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2122 name = tbasename(tstrcpy(source_copy, source));
2123 name_defaulted = true;
2126 /* Image description (if given). */
2128 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2129 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2130 ret = string_set_append(&image_properties, p);
2136 /* Set up capture sources in source list mode */
2137 if (source[0] == T('-') && source[1] == T('\0')) {
2138 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2140 source_list_contents = file_get_text_contents(source,
2141 &source_list_nchars);
2143 if (!source_list_contents)
2146 capture_sources = parse_source_list(&source_list_contents,
2149 if (!capture_sources) {
2151 goto out_free_source_list_contents;
2153 capture_sources_malloced = true;
2155 /* Set up capture source in non-source-list mode. */
2156 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2157 capture_sources[0].fs_source_path = source;
2158 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2159 capture_sources[0].reserved = 0;
2161 capture_sources_malloced = false;
2162 source_list_contents = NULL;
2165 /* Open the existing WIM, or create a new one. */
2166 if (cmd == CMD_APPEND) {
2167 ret = wimlib_open_wim_with_progress(wimfile,
2168 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2170 imagex_progress_func,
2173 goto out_free_capture_sources;
2175 ret = wimlib_create_new_wim(compression_type, &wim);
2177 goto out_free_capture_sources;
2178 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2181 /* Set chunk size if non-default. */
2182 if (chunk_size != UINT32_MAX) {
2183 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2186 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2188 int ctype = compression_type;
2190 if (cmd == CMD_APPEND) {
2191 struct wimlib_wim_info info;
2192 wimlib_get_wim_info(wim, &info);
2193 ctype = info.compression_type;
2196 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2197 ret = wimlib_set_output_chunk_size(wim, 4096);
2202 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2203 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2207 if (solid_chunk_size != UINT32_MAX) {
2208 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2214 /* Detect if source is regular file or block device and set NTFS volume
2219 if (tstat(source, &stbuf) == 0) {
2220 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2221 imagex_printf(T("Capturing WIM image from NTFS "
2222 "filesystem on \"%"TS"\"\n"), source);
2223 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2226 if (errno != ENOENT) {
2227 imagex_error_with_errno(T("Failed to stat "
2228 "\"%"TS"\""), source);
2236 /* If the user did not specify an image name, and the basename of the
2237 * source already exists as an image name in the WIM file, append a
2238 * suffix to make it unique. */
2239 if (cmd == CMD_APPEND && name_defaulted) {
2240 unsigned long conflict_idx;
2241 tchar *name_end = tstrchr(name, T('\0'));
2242 for (conflict_idx = 1;
2243 wimlib_image_name_in_use(wim, name);
2246 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2250 /* If capturing a delta WIM, reference resources from the base WIMs
2251 * before adding the new image. */
2252 if (base_wimfiles.num_strings) {
2253 base_wims = calloc(base_wimfiles.num_strings,
2254 sizeof(base_wims[0]));
2255 if (base_wims == NULL) {
2256 imagex_error(T("Out of memory!"));
2261 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2262 ret = wimlib_open_wim_with_progress(
2263 base_wimfiles.strings[i], open_flags,
2264 &base_wims[i], imagex_progress_func, NULL);
2266 goto out_free_base_wims;
2270 ret = wimlib_reference_resources(wim, base_wims,
2271 base_wimfiles.num_strings, 0);
2273 goto out_free_base_wims;
2275 if (base_wimfiles.num_strings == 1) {
2276 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2277 base_wimfiles.strings[0]);
2279 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2280 base_wimfiles.num_strings);
2287 /* If capturing or appending as an update of an existing (template) image,
2288 * open the WIM if needed and parse the image index. */
2289 if (template_image_name_or_num) {
2292 if (base_wimfiles.num_strings == 1 &&
2293 template_wimfile == base_wimfiles.strings[0]) {
2294 template_wim = base_wims[0];
2295 } else if (template_wimfile == wimfile) {
2298 ret = wimlib_open_wim_with_progress(template_wimfile,
2301 imagex_progress_func,
2304 goto out_free_base_wims;
2307 template_image = wimlib_resolve_image(template_wim,
2308 template_image_name_or_num);
2310 if (template_image_name_or_num[0] == T('-')) {
2313 struct wimlib_wim_info info;
2315 wimlib_get_wim_info(template_wim, &info);
2316 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2317 if (n >= 1 && n <= info.image_count &&
2319 tmp != template_image_name_or_num + 1)
2321 template_image = info.image_count - (n - 1);
2324 ret = verify_image_exists_and_is_single(template_image,
2325 template_image_name_or_num,
2328 goto out_free_template_wim;
2330 template_wim = NULL;
2333 ret = wimlib_add_image_multisource(wim,
2340 goto out_free_template_wim;
2342 if (image_properties.num_strings || template_image_name_or_num) {
2343 /* User asked to set additional image properties, or an image on
2344 * which the added one is to be based has been specified with
2346 struct wimlib_wim_info info;
2348 wimlib_get_wim_info(wim, &info);
2350 ret = apply_image_properties(&image_properties, wim,
2351 info.image_count, NULL);
2353 goto out_free_template_wim;
2355 /* Reference template image if the user provided one. */
2356 if (template_image_name_or_num) {
2357 imagex_printf(T("Using image %d "
2358 "from \"%"TS"\" as template\n"),
2359 template_image, template_wimfile);
2360 ret = wimlib_reference_template_image(wim,
2366 goto out_free_template_wim;
2370 /* Write the new WIM or overwrite the existing WIM with the new image
2372 if (cmd == CMD_APPEND) {
2373 ret = wimlib_overwrite(wim, write_flags, num_threads);
2374 } else if (wimfile) {
2375 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2376 write_flags, num_threads);
2378 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2379 write_flags, num_threads);
2381 out_free_template_wim:
2382 /* template_wim may alias base_wims[0] or wim. */
2383 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2384 template_wim != wim)
2385 wimlib_free(template_wim);
2387 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2388 wimlib_free(base_wims[i]);
2392 out_free_capture_sources:
2393 if (capture_sources_malloced)
2394 free(capture_sources);
2395 out_free_source_list_contents:
2396 free(source_list_contents);
2398 string_set_destroy(&image_properties);
2399 string_set_destroy(&base_wimfiles);
2409 /* Remove image(s) from a WIM. */
2411 imagex_delete(int argc, tchar **argv, int cmd)
2414 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2415 int write_flags = 0;
2416 const tchar *wimfile;
2417 const tchar *image_num_or_name;
2422 for_opt(c, delete_options) {
2424 case IMAGEX_CHECK_OPTION:
2425 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2426 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2428 case IMAGEX_SOFT_OPTION:
2429 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2431 case IMAGEX_UNSAFE_COMPACT_OPTION:
2432 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2443 imagex_error(T("Must specify a WIM file"));
2445 imagex_error(T("Must specify an image"));
2449 image_num_or_name = argv[1];
2451 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2452 imagex_progress_func, NULL);
2456 image = wimlib_resolve_image(wim, image_num_or_name);
2458 ret = verify_image_exists(image, image_num_or_name, wimfile);
2460 goto out_wimlib_free;
2462 ret = wimlib_delete_image(wim, image);
2464 imagex_error(T("Failed to delete image from \"%"TS"\""),
2466 goto out_wimlib_free;
2469 ret = wimlib_overwrite(wim, write_flags, 0);
2471 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2472 "deleted"), wimfile);
2480 usage(CMD_DELETE, stderr);
2485 struct print_dentry_options {
2490 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2492 tprintf(T("%"TS"\n"), dentry->full_path);
2495 static const struct {
2498 } file_attr_flags[] = {
2499 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2500 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2501 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2502 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2503 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2504 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2505 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2506 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2507 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2508 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2509 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2510 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2511 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2512 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2513 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2516 #define TIMESTR_MAX 100
2519 timespec_to_string(const struct timespec *spec, tchar *buf)
2521 time_t t = spec->tv_sec;
2524 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2525 buf[TIMESTR_MAX - 1] = '\0';
2529 print_time(const tchar *type, const struct timespec *spec)
2531 tchar timestr[TIMESTR_MAX];
2533 timespec_to_string(spec, timestr);
2535 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2538 static void print_byte_field(const uint8_t field[], size_t len)
2541 tprintf(T("%02hhx"), *field++);
2545 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2547 tchar attr_string[256];
2550 tputs(T("WIM Information:"));
2551 tputs(T("----------------"));
2552 tprintf(T("Path: %"TS"\n"), wimfile);
2553 tprintf(T("GUID: 0x"));
2554 print_byte_field(info->guid, sizeof(info->guid));
2556 tprintf(T("Version: %u\n"), info->wim_version);
2557 tprintf(T("Image Count: %d\n"), info->image_count);
2558 tprintf(T("Compression: %"TS"\n"),
2559 wimlib_get_compression_type_string(info->compression_type));
2560 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2562 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2563 tprintf(T("Boot Index: %d\n"), info->boot_index);
2564 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2566 attr_string[0] = T('\0');
2569 tstrcat(attr_string, T("Pipable, "));
2571 if (info->has_integrity_table)
2572 tstrcat(attr_string, T("Integrity info, "));
2574 if (info->has_rpfix)
2575 tstrcat(attr_string, T("Relative path junction, "));
2577 if (info->resource_only)
2578 tstrcat(attr_string, T("Resource only, "));
2580 if (info->metadata_only)
2581 tstrcat(attr_string, T("Metadata only, "));
2583 if (info->is_marked_readonly)
2584 tstrcat(attr_string, T("Readonly, "));
2586 p = tstrchr(attr_string, T('\0'));
2587 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2590 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2594 print_resource(const struct wimlib_resource_entry *resource,
2597 tprintf(T("Hash = 0x"));
2598 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2601 if (!resource->is_missing) {
2602 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2603 resource->uncompressed_size);
2604 if (resource->packed) {
2605 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2606 "bytes @ offset %"PRIu64"\n"),
2607 resource->raw_resource_uncompressed_size,
2608 resource->raw_resource_compressed_size,
2609 resource->raw_resource_offset_in_wim);
2611 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2614 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2615 resource->compressed_size);
2617 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2621 tprintf(T("Part Number = %u\n"), resource->part_number);
2622 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2624 tprintf(T("Flags = "));
2625 if (resource->is_compressed)
2626 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2627 if (resource->is_metadata)
2628 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2629 if (resource->is_free)
2630 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2631 if (resource->is_spanned)
2632 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2633 if (resource->packed)
2634 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2642 print_blobs(WIMStruct *wim)
2644 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2648 default_print_security_descriptor(const uint8_t *sd, size_t size)
2650 tprintf(T("Security Descriptor = "));
2651 print_byte_field(sd, size);
2656 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2660 "----------------------------------------------------------------------------\n"));
2661 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2662 if (dentry->dos_name)
2663 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2664 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2665 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2666 if (file_attr_flags[i].flag & dentry->attributes)
2667 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2668 file_attr_flags[i].name);
2670 if (dentry->security_descriptor) {
2671 print_security_descriptor(dentry->security_descriptor,
2672 dentry->security_descriptor_size);
2675 print_time(T("Creation Time"), &dentry->creation_time);
2676 print_time(T("Last Write Time"), &dentry->last_write_time);
2677 print_time(T("Last Access Time"), &dentry->last_access_time);
2680 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2681 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2683 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2684 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2686 if (dentry->unix_mode != 0) {
2687 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2688 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2689 dentry->unix_uid, dentry->unix_gid,
2690 dentry->unix_mode, dentry->unix_rdev);
2693 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2694 if (dentry->streams[i].stream_name) {
2695 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2696 dentry->streams[i].stream_name);
2697 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2698 tprintf(T("\tRaw encrypted data stream:\n"));
2699 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2700 tprintf(T("\tReparse point stream:\n"));
2702 tprintf(T("\tUnnamed data stream:\n"));
2704 print_resource(&dentry->streams[i].resource, NULL);
2709 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2711 const struct print_dentry_options *options = _options;
2712 if (!options->detailed)
2713 print_dentry_full_path(dentry);
2715 print_dentry_detailed(dentry);
2719 /* Print the files contained in an image(s) in a WIM file. */
2721 imagex_dir(int argc, tchar **argv, int cmd)
2723 const tchar *wimfile;
2724 WIMStruct *wim = NULL;
2727 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2729 struct print_dentry_options options = {
2732 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2734 STRING_SET(refglobs);
2736 for_opt(c, dir_options) {
2738 case IMAGEX_PATH_OPTION:
2741 case IMAGEX_DETAILED_OPTION:
2742 options.detailed = true;
2744 case IMAGEX_ONE_FILE_ONLY_OPTION:
2745 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2747 case IMAGEX_REF_OPTION:
2748 ret = string_set_append(&refglobs, optarg);
2750 goto out_free_refglobs;
2760 imagex_error(T("Must specify a WIM file"));
2764 imagex_error(T("Too many arguments"));
2769 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2770 imagex_progress_func, NULL);
2772 goto out_free_refglobs;
2775 image = wimlib_resolve_image(wim, argv[1]);
2776 ret = verify_image_exists(image, argv[1], wimfile);
2778 goto out_wimlib_free;
2780 /* No image specified; default to image 1, but only if the WIM
2781 * contains exactly one image. */
2783 struct wimlib_wim_info info;
2785 wimlib_get_wim_info(wim, &info);
2786 if (info.image_count != 1) {
2787 imagex_error(T("\"%"TS"\" contains %d images; Please "
2788 "select one (or all)."),
2789 wimfile, info.image_count);
2796 if (refglobs.num_strings) {
2797 ret = wim_reference_globs(wim, &refglobs, 0);
2799 goto out_wimlib_free;
2802 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2803 print_dentry, &options);
2804 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2805 struct wimlib_wim_info info;
2807 wimlib_get_wim_info(wim, &info);
2808 do_metadata_not_found_warning(wimfile, &info);
2813 string_set_destroy(&refglobs);
2817 usage(CMD_DIR, stderr);
2819 goto out_free_refglobs;
2822 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2825 imagex_export(int argc, tchar **argv, int cmd)
2829 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2830 int write_flags = 0;
2831 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2832 const tchar *src_wimfile;
2833 const tchar *src_image_num_or_name;
2834 const tchar *dest_wimfile;
2836 const tchar *dest_name;
2837 const tchar *dest_desc;
2839 struct wimlib_wim_info src_info;
2840 WIMStruct *dest_wim;
2845 STRING_SET(refglobs);
2846 unsigned num_threads = 0;
2847 uint32_t chunk_size = UINT32_MAX;
2848 uint32_t solid_chunk_size = UINT32_MAX;
2849 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2851 for_opt(c, export_options) {
2853 case IMAGEX_BOOT_OPTION:
2854 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2856 case IMAGEX_CHECK_OPTION:
2857 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2858 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2860 case IMAGEX_NOCHECK_OPTION:
2861 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2863 case IMAGEX_COMPRESS_OPTION:
2864 compression_type = get_compression_type(optarg);
2865 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2868 case IMAGEX_COMPRESS_SLOW_OPTION:
2869 set_compress_slow();
2870 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2872 case IMAGEX_RECOMPRESS_OPTION:
2873 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2875 case IMAGEX_SOLID_OPTION:
2876 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2878 case IMAGEX_NO_SOLID_SORT_OPTION:
2879 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2881 case IMAGEX_CHUNK_SIZE_OPTION:
2882 chunk_size = parse_chunk_size(optarg);
2883 if (chunk_size == UINT32_MAX)
2886 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2887 solid_chunk_size = parse_chunk_size(optarg);
2888 if (solid_chunk_size == UINT32_MAX)
2891 case IMAGEX_SOLID_COMPRESS_OPTION:
2892 solid_ctype = get_compression_type(optarg);
2893 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2896 case IMAGEX_REF_OPTION:
2897 ret = string_set_append(&refglobs, optarg);
2899 goto out_free_refglobs;
2901 case IMAGEX_THREADS_OPTION:
2902 num_threads = parse_num_threads(optarg);
2903 if (num_threads == UINT_MAX)
2906 case IMAGEX_REBUILD_OPTION:
2907 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2909 case IMAGEX_PIPABLE_OPTION:
2910 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2912 case IMAGEX_NOT_PIPABLE_OPTION:
2913 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2915 case IMAGEX_WIMBOOT_OPTION:
2916 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2918 case IMAGEX_UNSAFE_COMPACT_OPTION:
2919 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2927 if (argc < 3 || argc > 5)
2929 src_wimfile = argv[0];
2930 src_image_num_or_name = argv[1];
2931 dest_wimfile = argv[2];
2932 dest_name = (argc >= 4) ? argv[3] : NULL;
2933 dest_desc = (argc >= 5) ? argv[4] : NULL;
2934 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2935 imagex_progress_func, NULL);
2937 goto out_free_refglobs;
2939 wimlib_get_wim_info(src_wim, &src_info);
2941 /* Determine if the destination is an existing file or not. If so, we
2942 * try to append the exported image(s) to it; otherwise, we create a new
2943 * WIM containing the exported image(s). Furthermore, determine if we
2944 * need to write a pipable WIM directly to standard output. */
2946 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2948 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2949 imagex_error("Can't write a non-pipable WIM to "
2950 "standard output! Specify --pipable\n"
2951 " if you want to create a pipable WIM "
2952 "(but read the docs first).");
2954 goto out_free_src_wim;
2957 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2959 dest_wimfile = NULL;
2960 dest_wim_fd = STDOUT_FILENO;
2961 imagex_info_file = stderr;
2962 set_fd_to_binary_mode(dest_wim_fd);
2965 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2967 /* Destination file exists. */
2969 if (!S_ISREG(stbuf.st_mode)) {
2970 imagex_error(T("\"%"TS"\" is not a regular file"),
2973 goto out_free_src_wim;
2975 ret = wimlib_open_wim_with_progress(dest_wimfile,
2977 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2979 imagex_progress_func,
2982 goto out_free_src_wim;
2984 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2985 /* The user specified a compression type, but we're
2986 * exporting to an existing WIM. Make sure the
2987 * specified compression type is the same as the
2988 * compression type of the existing destination WIM. */
2989 struct wimlib_wim_info dest_info;
2991 wimlib_get_wim_info(dest_wim, &dest_info);
2992 if (compression_type != dest_info.compression_type) {
2993 imagex_error(T("Cannot specify a compression type that is "
2994 "not the same as that used in the "
2995 "destination WIM"));
2997 goto out_free_dest_wim;
3003 if (errno != ENOENT) {
3004 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3007 goto out_free_src_wim;
3010 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3011 imagex_error(T("'--unsafe-compact' is only valid when "
3012 "exporting to an existing WIM file!"));
3014 goto out_free_src_wim;
3017 /* dest_wimfile is not an existing file, so create a new WIM. */
3019 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3020 /* The user did not specify a compression type; default
3021 * to that of the source WIM, unless --solid or
3022 * --wimboot was specified. */
3024 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3025 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3026 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3027 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3029 compression_type = src_info.compression_type;
3031 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3033 goto out_free_src_wim;
3035 wimlib_register_progress_function(dest_wim,
3036 imagex_progress_func, NULL);
3038 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3039 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3041 /* For --wimboot export, use small XPRESS chunks. */
3042 wimlib_set_output_chunk_size(dest_wim, 4096);
3043 } else if (compression_type == src_info.compression_type &&
3044 chunk_size == UINT32_MAX)
3046 /* Use same chunk size if compression type is the same. */
3047 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3051 if (chunk_size != UINT32_MAX) {
3052 /* Set destination chunk size. */
3053 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3055 goto out_free_dest_wim;
3057 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3058 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3060 goto out_free_dest_wim;
3062 if (solid_chunk_size != UINT32_MAX) {
3063 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3065 goto out_free_dest_wim;
3068 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3069 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3071 goto out_free_dest_wim;
3073 if (refglobs.num_strings) {
3074 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3076 goto out_free_dest_wim;
3079 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3080 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3082 imagex_error(T("--boot specified for all-images export, but source WIM "
3083 "has no bootable image."));
3085 goto out_free_dest_wim;
3088 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3089 dest_desc, export_flags);
3091 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3092 do_resource_not_found_warning(src_wimfile,
3093 &src_info, &refglobs);
3094 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3095 do_metadata_not_found_warning(src_wimfile, &src_info);
3097 goto out_free_dest_wim;
3101 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3102 else if (dest_wimfile)
3103 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3104 write_flags, num_threads);
3106 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3107 WIMLIB_ALL_IMAGES, write_flags,
3110 wimlib_free(dest_wim);
3112 wimlib_free(src_wim);
3114 string_set_destroy(&refglobs);
3118 usage(CMD_EXPORT, stderr);
3121 goto out_free_refglobs;
3124 /* Extract files or directories from a WIM image */
3126 imagex_extract(int argc, tchar **argv, int cmd)
3133 const tchar *wimfile;
3134 const tchar *image_num_or_name;
3135 tchar *dest_dir = T(".");
3136 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3137 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3138 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3139 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3141 STRING_SET(refglobs);
3143 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3145 for_opt(c, extract_options) {
3147 case IMAGEX_CHECK_OPTION:
3148 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3150 case IMAGEX_VERBOSE_OPTION:
3151 /* No longer does anything. */
3153 case IMAGEX_REF_OPTION:
3154 ret = string_set_append(&refglobs, optarg);
3156 goto out_free_refglobs;
3158 case IMAGEX_UNIX_DATA_OPTION:
3159 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3161 case IMAGEX_NO_ACLS_OPTION:
3162 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3164 case IMAGEX_STRICT_ACLS_OPTION:
3165 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3167 case IMAGEX_NO_ATTRIBUTES_OPTION:
3168 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3170 case IMAGEX_DEST_DIR_OPTION:
3173 case IMAGEX_TO_STDOUT_OPTION:
3174 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3175 imagex_info_file = stderr;
3176 imagex_be_quiet = true;
3177 set_fd_to_binary_mode(STDOUT_FILENO);
3179 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3180 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3181 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3183 case IMAGEX_NO_GLOBS_OPTION:
3184 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3186 case IMAGEX_NULLGLOB_OPTION:
3187 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3189 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3190 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3192 case IMAGEX_WIMBOOT_OPTION:
3193 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3195 case IMAGEX_COMPACT_OPTION:
3196 ret = set_compact_mode(optarg, &extract_flags);
3198 goto out_free_refglobs;
3210 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3211 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3213 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3218 image_num_or_name = argv[1];
3223 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3224 imagex_progress_func, NULL);
3226 goto out_free_refglobs;
3228 image = wimlib_resolve_image(wim, image_num_or_name);
3229 ret = verify_image_exists_and_is_single(image,
3233 goto out_wimlib_free;
3235 if (refglobs.num_strings) {
3236 ret = wim_reference_globs(wim, &refglobs, open_flags);
3238 goto out_wimlib_free;
3244 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3247 while (argc != 0 && ret == 0) {
3251 num_paths < argc && argv[num_paths][0] != T('@');
3256 ret = wimlib_extract_paths(wim, image, dest_dir,
3257 (const tchar **)argv,
3259 extract_flags | notlist_extract_flags);
3263 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3272 if (!imagex_be_quiet)
3273 imagex_printf(T("Done extracting files.\n"));
3274 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3275 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3276 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3277 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3278 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3281 T("Note: You can use the '--nullglob' "
3282 "option to ignore missing files.\n"));
3284 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3285 "files and directories\n"
3286 " are in the WIM image.\n"),
3287 get_cmd_string(CMD_DIR, false));
3288 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3289 struct wimlib_wim_info info;
3291 wimlib_get_wim_info(wim, &info);
3292 do_resource_not_found_warning(wimfile, &info, &refglobs);
3293 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3294 struct wimlib_wim_info info;
3296 wimlib_get_wim_info(wim, &info);
3297 do_metadata_not_found_warning(wimfile, &info);
3302 string_set_destroy(&refglobs);
3306 usage(CMD_EXTRACT, stderr);
3309 goto out_free_refglobs;
3312 /* Prints information about a WIM file; also can mark an image as bootable,
3313 * change the name of an image, or change the description of an image. */
3315 imagex_info(int argc, tchar **argv, int cmd)
3320 bool nocheck = false;
3321 bool header = false;
3324 bool short_header = true;
3325 const tchar *xml_out_file = NULL;
3326 const tchar *wimfile;
3327 const tchar *image_num_or_name;
3328 STRING_SET(image_properties);
3333 struct wimlib_wim_info info;
3335 for_opt(c, info_options) {
3337 case IMAGEX_BOOT_OPTION:
3340 case IMAGEX_CHECK_OPTION:
3343 case IMAGEX_NOCHECK_OPTION:
3346 case IMAGEX_HEADER_OPTION:
3348 short_header = false;
3350 case IMAGEX_BLOBS_OPTION:
3352 short_header = false;
3354 case IMAGEX_XML_OPTION:
3356 short_header = false;
3358 case IMAGEX_EXTRACT_XML_OPTION:
3359 xml_out_file = optarg;
3360 short_header = false;
3362 case IMAGEX_METADATA_OPTION:
3363 imagex_error(T("The --metadata option has been removed. "
3364 "Use 'wimdir --detail' instead."));
3366 case IMAGEX_IMAGE_PROPERTY_OPTION:
3367 ret = append_image_property_argument(&image_properties);
3378 if (argc < 1 || argc > 4)
3382 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3386 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3387 tsprintf(p, T("NAME=%"TS), argv[2]);
3388 ret = string_set_append(&image_properties, p);
3395 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3396 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3397 ret = string_set_append(&image_properties, p);
3402 if (check && nocheck) {
3403 imagex_error(T("Can't specify both --check and --nocheck"));
3408 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3410 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3411 imagex_progress_func, NULL);
3415 wimlib_get_wim_info(wim, &info);
3417 image = wimlib_resolve_image(wim, image_num_or_name);
3418 ret = WIMLIB_ERR_INVALID_IMAGE;
3419 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3420 verify_image_exists(image, image_num_or_name, wimfile);
3422 imagex_error(T("If you would like to set the boot "
3423 "index to 0, specify image \"0\" with "
3424 "the --boot flag."));
3426 goto out_wimlib_free;
3429 if (boot && info.image_count == 0) {
3430 imagex_error(T("--boot is meaningless on a WIM with no images"));
3431 goto out_wimlib_free;
3434 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3436 imagex_error(T("Cannot specify the --boot flag "
3437 "without specifying a specific "
3438 "image in a multi-image WIM"));
3439 goto out_wimlib_free;
3441 if (image_properties.num_strings) {
3442 imagex_error(T("Can't change image properties without "
3443 "specifying a specific image in a "
3444 "multi-image WIM"));
3445 goto out_wimlib_free;
3449 /* Operations that print information are separated from operations that
3450 * recreate the WIM file. */
3451 if (!image_properties.num_strings && !boot) {
3453 /* Read-only operations */
3455 if (image == WIMLIB_NO_IMAGE) {
3456 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3457 image_num_or_name, wimfile);
3458 goto out_wimlib_free;
3461 if (image == WIMLIB_ALL_IMAGES && short_header)
3462 print_wim_information(wimfile, &info);
3465 wimlib_print_header(wim);
3468 if (info.total_parts != 1) {
3469 tfprintf(stderr, T("Warning: Only showing the blobs "
3470 "for part %d of a %d-part WIM.\n"),
3471 info.part_number, info.total_parts);
3477 ret = wimlib_extract_xml_data(wim, stdout);
3479 goto out_wimlib_free;
3485 fp = tfopen(xml_out_file, T("wb"));
3487 imagex_error_with_errno(T("Failed to open the "
3488 "file \"%"TS"\" for "
3492 goto out_wimlib_free;
3494 ret = wimlib_extract_xml_data(wim, fp);
3496 imagex_error(T("Failed to close the file "
3502 goto out_wimlib_free;
3506 wimlib_print_available_images(wim, image);
3510 /* Modification operations */
3511 bool any_property_changes;
3513 if (image == WIMLIB_ALL_IMAGES)
3516 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3517 imagex_error(T("Cannot change image properties "
3518 "when using image 0"));
3520 goto out_wimlib_free;
3524 if (image == info.boot_index) {
3525 imagex_printf(T("Image %d is already marked as "
3526 "bootable.\n"), image);
3529 imagex_printf(T("Marking image %d as bootable.\n"),
3531 info.boot_index = image;
3532 ret = wimlib_set_wim_info(wim, &info,
3533 WIMLIB_CHANGE_BOOT_INDEX);
3535 goto out_wimlib_free;
3539 ret = apply_image_properties(&image_properties, wim, image,
3540 &any_property_changes);
3542 goto out_wimlib_free;
3544 /* Only call wimlib_overwrite() if something actually needs to
3546 if (boot || any_property_changes ||
3547 (check && !info.has_integrity_table) ||
3548 (nocheck && info.has_integrity_table))
3550 int write_flags = 0;
3553 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3555 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3556 ret = wimlib_overwrite(wim, write_flags, 1);
3558 imagex_printf(T("The file \"%"TS"\" was not modified "
3559 "because nothing needed to be done.\n"),
3567 string_set_destroy(&image_properties);
3571 usage(CMD_INFO, stderr);
3577 /* Join split WIMs into one part WIM */
3579 imagex_join(int argc, tchar **argv, int cmd)
3582 int swm_open_flags = 0;
3583 int wim_write_flags = 0;
3584 const tchar *output_path;
3587 for_opt(c, join_options) {
3589 case IMAGEX_CHECK_OPTION:
3590 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3591 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3601 imagex_error(T("Must specify one or more split WIM (.swm) "
3605 output_path = argv[0];
3606 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3611 imagex_progress_func,
3617 usage(CMD_JOIN, stderr);
3622 #if WIM_MOUNTING_SUPPORTED
3624 /* Mounts a WIM image. */
3626 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3629 int mount_flags = 0;
3631 const tchar *staging_dir = NULL;
3632 const tchar *wimfile;
3635 struct wimlib_wim_info info;
3639 STRING_SET(refglobs);
3641 if (cmd == CMD_MOUNTRW) {
3642 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3643 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3646 for_opt(c, mount_options) {
3648 case IMAGEX_ALLOW_OTHER_OPTION:
3649 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3651 case IMAGEX_CHECK_OPTION:
3652 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3654 case IMAGEX_DEBUG_OPTION:
3655 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3657 case IMAGEX_STREAMS_INTERFACE_OPTION:
3658 if (!tstrcasecmp(optarg, T("none")))
3659 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3660 else if (!tstrcasecmp(optarg, T("xattr")))
3661 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3662 else if (!tstrcasecmp(optarg, T("windows")))
3663 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3665 imagex_error(T("Unknown stream interface \"%"TS"\""),
3670 case IMAGEX_REF_OPTION:
3671 ret = string_set_append(&refglobs, optarg);
3673 goto out_free_refglobs;
3675 case IMAGEX_STAGING_DIR_OPTION:
3676 staging_dir = optarg;
3678 case IMAGEX_UNIX_DATA_OPTION:
3679 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3687 if (argc != 2 && argc != 3)
3692 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3693 imagex_progress_func, NULL);
3695 goto out_free_refglobs;
3697 wimlib_get_wim_info(wim, &info);
3700 /* Image explicitly specified. */
3701 image = wimlib_resolve_image(wim, argv[1]);
3703 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3707 /* No image specified; default to image 1, but only if the WIM
3708 * contains exactly one image. */
3710 if (info.image_count != 1) {
3711 imagex_error(T("\"%"TS"\" contains %d images; Please "
3712 "select one."), wimfile, info.image_count);
3720 if (refglobs.num_strings) {
3721 ret = wim_reference_globs(wim, &refglobs, open_flags);
3726 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3728 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3729 do_metadata_not_found_warning(wimfile, &info);
3731 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3733 image, wimfile, dir);
3739 string_set_destroy(&refglobs);
3745 goto out_free_refglobs;
3747 #endif /* WIM_MOUNTING_SUPPORTED */
3749 /* Rebuild a WIM file */
3751 imagex_optimize(int argc, tchar **argv, int cmd)
3754 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3755 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3756 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3757 uint32_t chunk_size = UINT32_MAX;
3758 uint32_t solid_chunk_size = UINT32_MAX;
3759 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3762 const tchar *wimfile;
3765 unsigned num_threads = 0;
3767 for_opt(c, optimize_options) {
3769 case IMAGEX_CHECK_OPTION:
3770 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3771 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3773 case IMAGEX_NOCHECK_OPTION:
3774 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3776 case IMAGEX_COMPRESS_OPTION:
3777 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3778 compression_type = get_compression_type(optarg);
3779 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3782 case IMAGEX_COMPRESS_SLOW_OPTION:
3783 set_compress_slow();
3784 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3786 case IMAGEX_RECOMPRESS_OPTION:
3787 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3789 case IMAGEX_CHUNK_SIZE_OPTION:
3790 chunk_size = parse_chunk_size(optarg);
3791 if (chunk_size == UINT32_MAX)
3794 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3795 solid_chunk_size = parse_chunk_size(optarg);
3796 if (solid_chunk_size == UINT32_MAX)
3799 case IMAGEX_SOLID_COMPRESS_OPTION:
3800 solid_ctype = get_compression_type(optarg);
3801 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3804 case IMAGEX_SOLID_OPTION:
3805 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3806 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3808 case IMAGEX_NO_SOLID_SORT_OPTION:
3809 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3811 case IMAGEX_THREADS_OPTION:
3812 num_threads = parse_num_threads(optarg);
3813 if (num_threads == UINT_MAX)
3816 case IMAGEX_PIPABLE_OPTION:
3817 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3819 case IMAGEX_NOT_PIPABLE_OPTION:
3820 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3822 case IMAGEX_UNSAFE_COMPACT_OPTION:
3823 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3837 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3838 imagex_progress_func, NULL);
3842 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3843 /* Change compression type. */
3844 ret = wimlib_set_output_compression_type(wim, compression_type);
3846 goto out_wimlib_free;
3849 if (chunk_size != UINT32_MAX) {
3850 /* Change chunk size. */
3851 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3853 goto out_wimlib_free;
3855 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3856 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3858 goto out_wimlib_free;
3860 if (solid_chunk_size != UINT32_MAX) {
3861 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3863 goto out_wimlib_free;
3866 old_size = file_get_size(wimfile);
3867 tprintf(T("\"%"TS"\" original size: "), wimfile);
3869 tputs(T("Unknown"));
3871 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3873 ret = wimlib_overwrite(wim, write_flags, num_threads);
3875 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3876 goto out_wimlib_free;
3879 new_size = file_get_size(wimfile);
3880 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3882 tputs(T("Unknown"));
3884 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3886 tfputs(T("Space saved: "), stdout);
3887 if (new_size != -1 && old_size != -1) {
3888 tprintf(T("%lld KiB\n"),
3889 ((long long)old_size - (long long)new_size) >> 10);
3891 tputs(T("Unknown"));
3900 usage(CMD_OPTIMIZE, stderr);
3906 /* Split a WIM into a spanned set */
3908 imagex_split(int argc, tchar **argv, int cmd)
3912 int write_flags = 0;
3913 unsigned long part_size;
3918 for_opt(c, split_options) {
3920 case IMAGEX_CHECK_OPTION:
3921 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3922 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3934 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3935 if (tmp == argv[2] || *tmp) {
3936 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3937 imagex_error(T("The part size must be an integer or "
3938 "floating-point number of megabytes."));
3941 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3942 imagex_progress_func, NULL);
3946 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3952 usage(CMD_SPLIT, stderr);
3958 #if WIM_MOUNTING_SUPPORTED
3959 /* Unmounts a mounted WIM image. */
3961 imagex_unmount(int argc, tchar **argv, int cmd)
3964 int unmount_flags = 0;
3967 for_opt(c, unmount_options) {
3969 case IMAGEX_COMMIT_OPTION:
3970 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3972 case IMAGEX_CHECK_OPTION:
3973 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3975 case IMAGEX_REBUILD_OPTION:
3976 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3978 case IMAGEX_LAZY_OPTION:
3979 case IMAGEX_FORCE_OPTION:
3980 /* Now, unmount is lazy by default. However, committing
3981 * the image will fail with
3982 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3983 * file descriptors on the WIM image. The
3984 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3985 * descriptors to be closed. */
3986 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3988 case IMAGEX_NEW_IMAGE_OPTION:
3989 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4000 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4001 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4002 imagex_error(T("--new-image is meaningless "
4003 "without --commit also specified!"));
4008 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4009 imagex_progress_func, NULL);
4011 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4012 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4014 "\tNote: Use --commit --force to force changes "
4015 "to be committed, regardless\n"
4016 "\t of open files.\n"));
4023 usage(CMD_UNMOUNT, stderr);
4028 #endif /* WIM_MOUNTING_SUPPORTED */
4031 * Add, delete, or rename files in a WIM image.
4034 imagex_update(int argc, tchar **argv, int cmd)
4036 const tchar *wimfile;
4040 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4041 int write_flags = 0;
4042 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4043 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4044 WIMLIB_ADD_FLAG_VERBOSE |
4045 WIMLIB_ADD_FLAG_WINCONFIG;
4046 int default_delete_flags = 0;
4047 unsigned num_threads = 0;
4049 tchar *cmd_file_contents;
4050 size_t cmd_file_nchars;
4051 struct wimlib_update_command *cmds;
4053 tchar *command_str = NULL;
4054 tchar *config_file = NULL;
4055 tchar *wimboot_config = NULL;
4057 for_opt(c, update_options) {
4059 /* Generic or write options */
4060 case IMAGEX_THREADS_OPTION:
4061 num_threads = parse_num_threads(optarg);
4062 if (num_threads == UINT_MAX)
4065 case IMAGEX_CHECK_OPTION:
4066 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4067 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4069 case IMAGEX_REBUILD_OPTION:
4070 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4072 case IMAGEX_COMMAND_OPTION:
4074 imagex_error(T("--command may only be specified "
4075 "one time. Please provide\n"
4076 " the update commands "
4077 "on standard input instead."));
4080 command_str = tstrdup(optarg);
4082 imagex_error(T("Out of memory!"));
4086 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4087 wimboot_config = optarg;
4089 /* Default delete options */
4090 case IMAGEX_FORCE_OPTION:
4091 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4093 case IMAGEX_RECURSIVE_OPTION:
4094 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4097 /* Global add option */
4098 case IMAGEX_CONFIG_OPTION:
4099 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4100 config_file = optarg;
4103 /* Default add options */
4104 case IMAGEX_VERBOSE_OPTION:
4105 /* No longer does anything. */
4107 case IMAGEX_DEREFERENCE_OPTION:
4108 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4110 case IMAGEX_UNIX_DATA_OPTION:
4111 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4113 case IMAGEX_NO_ACLS_OPTION:
4114 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4116 case IMAGEX_STRICT_ACLS_OPTION:
4117 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4119 case IMAGEX_NO_REPLACE_OPTION:
4120 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4122 case IMAGEX_UNSAFE_COMPACT_OPTION:
4123 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4132 if (argc != 1 && argc != 2)
4136 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4137 imagex_progress_func, NULL);
4139 goto out_free_command_str;
4142 /* Image explicitly specified. */
4143 image = wimlib_resolve_image(wim, argv[1]);
4144 ret = verify_image_exists_and_is_single(image, argv[1],
4147 goto out_wimlib_free;
4149 /* No image specified; default to image 1, but only if the WIM
4150 * contains exactly one image. */
4151 struct wimlib_wim_info info;
4153 wimlib_get_wim_info(wim, &info);
4154 if (info.image_count != 1) {
4155 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4156 wimfile, info.image_count);
4163 /* Read update commands from standard input, or the command string if
4166 cmd_file_contents = NULL;
4167 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4171 goto out_free_cmd_file_contents;
4173 } else if (!wimboot_config) {
4174 if (isatty(STDIN_FILENO)) {
4175 tputs(T("Reading update commands from standard input..."));
4176 recommend_man_page(CMD_UPDATE, stdout);
4178 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4179 if (!cmd_file_contents) {
4181 goto out_wimlib_free;
4184 /* Parse the update commands */
4185 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4189 goto out_free_cmd_file_contents;
4192 cmd_file_contents = NULL;
4197 /* Set default flags and capture config on the update commands */
4198 for (size_t i = 0; i < num_cmds; i++) {
4199 switch (cmds[i].op) {
4200 case WIMLIB_UPDATE_OP_ADD:
4201 cmds[i].add.add_flags |= default_add_flags;
4202 cmds[i].add.config_file = config_file;
4204 case WIMLIB_UPDATE_OP_DELETE:
4205 cmds[i].delete_.delete_flags |= default_delete_flags;
4212 /* Execute the update commands */
4213 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4217 if (wimboot_config) {
4218 /* --wimboot-config=FILE is short for an
4219 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4221 struct wimlib_update_command cmd;
4223 cmd.op = WIMLIB_UPDATE_OP_ADD;
4224 cmd.add.fs_source_path = wimboot_config;
4225 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4226 cmd.add.config_file = NULL;
4227 cmd.add.add_flags = 0;
4229 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4234 /* Overwrite the updated WIM */
4235 ret = wimlib_overwrite(wim, write_flags, num_threads);
4238 out_free_cmd_file_contents:
4239 free(cmd_file_contents);
4242 out_free_command_str:
4247 usage(CMD_UPDATE, stderr);
4250 goto out_free_command_str;
4253 /* Verify a WIM file. */
4255 imagex_verify(int argc, tchar **argv, int cmd)
4258 const tchar *wimfile;
4260 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4261 int verify_flags = 0;
4262 STRING_SET(refglobs);
4265 for_opt(c, verify_options) {
4267 case IMAGEX_REF_OPTION:
4268 ret = string_set_append(&refglobs, optarg);
4270 goto out_free_refglobs;
4272 case IMAGEX_NOCHECK_OPTION:
4273 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4285 imagex_error(T("Must specify a WIM file!"));
4287 imagex_error(T("At most one WIM file can be specified!"));
4293 ret = wimlib_open_wim_with_progress(wimfile,
4296 imagex_progress_func,
4299 goto out_free_refglobs;
4301 ret = wim_reference_globs(wim, &refglobs, open_flags);
4303 goto out_wimlib_free;
4305 ret = wimlib_verify_wim(wim, verify_flags);
4307 tputc(T('\n'), stderr);
4308 imagex_error(T("\"%"TS"\" failed verification!"),
4310 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4311 refglobs.num_strings == 0)
4313 imagex_printf(T("Note: if this WIM file is not standalone, "
4314 "use the --ref option to specify the other parts.\n"));
4317 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4324 string_set_destroy(&refglobs);
4328 usage(CMD_VERIFY, stderr);
4330 goto out_free_refglobs;
4333 struct imagex_command {
4335 int (*func)(int argc, tchar **argv, int cmd);
4338 static const struct imagex_command imagex_commands[] = {
4339 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4340 [CMD_APPLY] = {T("apply"), imagex_apply},
4341 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4342 [CMD_DELETE] = {T("delete"), imagex_delete},
4343 [CMD_DIR ] = {T("dir"), imagex_dir},
4344 [CMD_EXPORT] = {T("export"), imagex_export},
4345 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4346 [CMD_INFO] = {T("info"), imagex_info},
4347 [CMD_JOIN] = {T("join"), imagex_join},
4348 #if WIM_MOUNTING_SUPPORTED
4349 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4350 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4352 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4353 [CMD_SPLIT] = {T("split"), imagex_split},
4354 #if WIM_MOUNTING_SUPPORTED
4355 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4357 [CMD_UPDATE] = {T("update"), imagex_update},
4358 [CMD_VERIFY] = {T("verify"), imagex_verify},
4363 /* Can be a directory or source list file. But source list file is probably
4364 * a rare use case, so just say directory. */
4365 # define SOURCE_STR T("DIRECTORY")
4367 /* Can only be a directory */
4368 # define TARGET_STR T("DIRECTORY")
4371 /* Can be a directory, NTFS volume, or source list file. */
4372 # define SOURCE_STR T("SOURCE")
4374 /* Can be a directory or NTFS volume. */
4375 # define TARGET_STR T("TARGET")
4379 static const tchar *usage_strings[] = {
4382 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4383 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4384 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4385 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4386 " [--wimboot] [--unix-data] [--dereference]\n"
4390 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4391 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4392 " [--no-attributes] [--rpfix] [--norpfix]\n"
4393 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4394 " [--compact=FORMAT]\n"
4398 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4399 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4400 " [--config=FILE] [--threads=NUM_THREADS]\n"
4401 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4402 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4403 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4407 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4411 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4415 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4416 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4417 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4418 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4419 " [--wimboot] [--solid]\n"
4423 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4424 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4425 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4426 " [--no-attributes] [--include-invalid-names]\n"
4427 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4431 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4432 " [--boot] [--check] [--nocheck] [--xml]\n"
4433 " [--extract-xml FILE] [--header] [--blobs]\n"
4434 " [--image-property NAME=VALUE]\n"
4438 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4440 #if WIM_MOUNTING_SUPPORTED
4443 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4444 " [--check] [--streams-interface=INTERFACE]\n"
4445 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4449 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4450 " [--check] [--streams-interface=INTERFACE]\n"
4451 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4457 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4458 " [--check] [--nocheck] [--solid]\n"
4463 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4465 #if WIM_MOUNTING_SUPPORTED
4468 " %"TS" DIRECTORY\n"
4469 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4474 " %"TS" WIMFILE [IMAGE]\n"
4475 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4476 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4477 " [--command=STRING] [--wimboot-config=FILE]\n"
4482 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4486 static const tchar *invocation_name;
4487 static int invocation_cmd = CMD_NONE;
4489 static const tchar *get_cmd_string(int cmd, bool nospace)
4491 static tchar buf[50];
4492 if (cmd == CMD_NONE) {
4493 return T("wimlib-imagex");
4494 } else if (invocation_cmd != CMD_NONE) {
4495 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4497 const tchar *format;
4500 format = T("%"TS"-%"TS"");
4502 format = T("%"TS" %"TS"");
4503 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4511 static const tchar *s =
4513 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4514 "Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers\n"
4515 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4516 "This is free software: you are free to change and redistribute it.\n"
4517 "There is NO WARRANTY, to the extent permitted by law.\n"
4519 "Report bugs to "PACKAGE_BUGREPORT".\n"
4526 help_or_version(int argc, tchar **argv, int cmd)
4531 for (i = 1; i < argc; i++) {
4533 if (p[0] == T('-') && p[1] == T('-')) {
4535 if (!tstrcmp(p, T("help"))) {
4536 if (cmd == CMD_NONE)
4541 } else if (!tstrcmp(p, T("version"))) {
4550 print_usage_string(int cmd, FILE *fp)
4552 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4556 recommend_man_page(int cmd, FILE *fp)
4558 const tchar *format_str;
4560 format_str = T("Some uncommon options are not listed;\n"
4561 "See %"TS".pdf in the doc directory for more details.\n");
4563 format_str = T("Some uncommon options are not listed;\n"
4564 "Try `man %"TS"' for more details.\n");
4566 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4570 usage(int cmd, FILE *fp)
4572 tfprintf(fp, T("Usage:\n"));
4573 print_usage_string(cmd, fp);
4574 tfprintf(fp, T("\n"));
4575 recommend_man_page(cmd, fp);
4581 tfprintf(fp, T("Usage:\n"));
4582 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4583 print_usage_string(cmd, fp);
4584 tfprintf(fp, T("\n"));
4586 static const tchar *extra =
4589 " %"TS" --version\n"
4592 tfprintf(fp, extra, invocation_name, invocation_name);
4594 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4595 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4596 "For some commands IMAGE may be \"all\".\n"
4598 recommend_man_page(CMD_NONE, fp);
4601 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4602 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4603 * something else), while on Windows the command arguments will be UTF-16LE
4604 * encoded 'wchar_t' strings. */
4607 wmain(int argc, wchar_t **argv, wchar_t **envp)
4609 main(int argc, char **argv)
4616 imagex_info_file = stdout;
4617 invocation_name = tbasename(argv[0]);
4620 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4621 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4625 setlocale(LC_ALL, "");
4626 codeset = nl_langinfo(CODESET);
4627 if (!strstr(codeset, "UTF-8") &&
4628 !strstr(codeset, "UTF8") &&
4629 !strstr(codeset, "utf-8") &&
4630 !strstr(codeset, "utf8"))
4633 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4634 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4635 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4636 " to any value to force wimlib to use UTF-8.\n",
4642 #endif /* !__WIN32__ */
4645 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4646 if (igcase != NULL) {
4647 if (!tstrcmp(igcase, T("no")) ||
4648 !tstrcmp(igcase, T("0")))
4649 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4650 else if (!tstrcmp(igcase, T("yes")) ||
4651 !tstrcmp(igcase, T("1")))
4652 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4655 "WARNING: Ignoring unknown setting of "
4656 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4661 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4663 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4664 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4665 for (int i = 0; i < CMD_MAX; i++) {
4666 if (!tstrcmp(invocation_name + 3,
4667 imagex_commands[i].name))
4676 /* Unless already known from the invocation name, determine which
4677 * command was specified. */
4678 if (cmd == CMD_NONE) {
4680 imagex_error(T("No command specified!\n"));
4684 for (int i = 0; i < CMD_MAX; i++) {
4685 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4690 if (cmd != CMD_NONE) {
4696 /* Handle --help and --version. --help can be either for the program as
4697 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4698 * CMD_NONE). Note: help_or_version() will not return if a --help or
4699 * --version argument was found. */
4700 help_or_version(argc, argv, cmd);
4702 /* Bail if a valid command was not specified. */
4703 if (cmd == CMD_NONE) {
4704 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4709 /* Enable warning and error messages in wimlib to be more user-friendly.
4711 wimlib_set_print_errors(true);
4713 /* Initialize wimlib. */
4714 ret = wimlib_global_init(init_flags);
4716 goto out_check_status;
4718 /* Call the command handler function. */
4719 ret = imagex_commands[cmd].func(argc, argv, cmd);
4721 /* Check for error writing to standard output, especially since for some
4722 * commands, writing to standard output is part of the program's actual
4723 * behavior and not just for informational purposes. */
4724 if (ferror(stdout) || fclose(stdout)) {
4725 imagex_error_with_errno(T("error writing to standard output"));
4730 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4731 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4732 * error code from which an error message can be printed. */
4734 imagex_error(T("Exiting with error code %d:\n"
4736 wimlib_get_error_string(ret));
4737 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4738 imagex_error_with_errno(T("errno"));
4740 /* Make wimlib free any resources it's holding (although this is not
4741 * strictly necessary because the process is ending anyway). */
4742 wimlib_global_cleanup();