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,
187 IMAGEX_SNAPSHOT_OPTION,
189 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
190 IMAGEX_SOLID_COMPRESS_OPTION,
192 IMAGEX_SOURCE_LIST_OPTION,
193 IMAGEX_STAGING_DIR_OPTION,
194 IMAGEX_STREAMS_INTERFACE_OPTION,
195 IMAGEX_STRICT_ACLS_OPTION,
196 IMAGEX_THREADS_OPTION,
197 IMAGEX_TO_STDOUT_OPTION,
198 IMAGEX_UNIX_DATA_OPTION,
199 IMAGEX_UNSAFE_COMPACT_OPTION,
200 IMAGEX_UPDATE_OF_OPTION,
201 IMAGEX_VERBOSE_OPTION,
202 IMAGEX_WIMBOOT_CONFIG_OPTION,
203 IMAGEX_WIMBOOT_OPTION,
207 static const struct option apply_options[] = {
208 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
209 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
210 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
211 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
212 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
213 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
214 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
215 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
216 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
217 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
218 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
220 /* --resume is undocumented for now as it needs improvement. */
221 {T("resume"), no_argument, NULL, IMAGEX_RESUME_OPTION},
222 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
223 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
227 static const struct option capture_or_append_options[] = {
228 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
229 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
230 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
231 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
232 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
233 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
234 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
235 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
236 {T("pack-streams"), no_argument, NULL, IMAGEX_SOLID_OPTION},
237 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
238 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
239 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
240 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
241 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
242 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
243 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
244 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
245 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
246 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
247 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
248 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
249 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
250 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
251 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
252 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
253 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
254 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
255 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
256 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
257 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
258 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
259 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
260 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
261 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
262 {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
266 static const struct option delete_options[] = {
267 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
268 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
269 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
273 static const struct option dir_options[] = {
274 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
275 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
276 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
277 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
281 static const struct option export_options[] = {
282 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
283 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
284 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
285 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
286 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
287 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
288 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
289 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
290 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
291 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
292 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
293 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
294 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
295 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
296 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
297 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
298 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
299 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
300 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
301 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
302 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
303 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
307 static const struct option extract_options[] = {
308 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
309 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
310 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
311 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
312 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
313 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
314 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
315 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
316 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
317 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
318 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
319 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
320 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
321 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
322 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
323 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
324 {T("compact"), required_argument, NULL, IMAGEX_COMPACT_OPTION},
328 static const struct option info_options[] = {
329 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
330 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
331 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
332 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
333 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
334 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
335 {T("lookup-table"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
336 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
337 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
338 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
339 {T("image-property"), required_argument, NULL, IMAGEX_IMAGE_PROPERTY_OPTION},
343 static const struct option join_options[] = {
344 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
348 static const struct option mount_options[] = {
349 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
350 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
351 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
352 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
353 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
354 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
355 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
359 static const struct option optimize_options[] = {
360 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
361 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
362 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
363 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
364 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
365 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
366 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
367 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
368 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
369 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
370 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
371 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
372 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
373 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
374 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
375 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
376 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
377 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
378 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
382 static const struct option split_options[] = {
383 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
387 static const struct option unmount_options[] = {
388 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
389 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
390 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
391 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
392 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
393 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
397 static const struct option update_options[] = {
398 /* Careful: some of the options here set the defaults for update
399 * commands, but the flags given to an actual update command (and not to
400 * `imagex update' itself are also handled in
401 * update_command_add_option(). */
402 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
403 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
404 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
405 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
406 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
408 /* Default delete options */
409 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
410 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
412 /* Global add option */
413 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
415 /* Default add options */
416 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
417 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
418 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
419 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
420 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
421 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
422 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
423 {T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
428 static const struct option verify_options[] = {
429 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
430 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
436 # define _format_attribute(type, format_str, args_start) \
437 __attribute__((format(type, format_str, args_start)))
439 # define _format_attribute(type, format_str, args_start)
442 /* Print formatted error message to stderr. */
443 static void _format_attribute(printf, 1, 2)
444 imagex_error(const tchar *format, ...)
447 va_start(va, format);
448 tfputs(T("ERROR: "), stderr);
449 tvfprintf(stderr, format, va);
450 tputc(T('\n'), stderr);
454 /* Print formatted error message to stderr. */
455 static void _format_attribute(printf, 1, 2)
456 imagex_error_with_errno(const tchar *format, ...)
458 int errno_save = errno;
460 va_start(va, format);
461 tfputs(T("ERROR: "), stderr);
462 tvfprintf(stderr, format, va);
463 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
468 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
470 if (image == WIMLIB_NO_IMAGE) {
471 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
472 " Please specify a 1-based image index or "
473 "image name. To list the images\n"
474 " contained in the WIM archive, run\n"
476 " %"TS" \"%"TS"\"\n"),
477 image_name, wim_name,
478 get_cmd_string(CMD_INFO, false), wim_name);
479 return WIMLIB_ERR_INVALID_IMAGE;
485 verify_image_is_single(int image)
487 if (image == WIMLIB_ALL_IMAGES) {
488 imagex_error(T("Cannot specify all images for this action!"));
489 return WIMLIB_ERR_INVALID_IMAGE;
495 verify_image_exists_and_is_single(int image, const tchar *image_name,
496 const tchar *wim_name)
499 ret = verify_image_exists(image, image_name, wim_name);
501 ret = verify_image_is_single(image);
506 print_available_compression_types(FILE *fp)
508 static const tchar *s =
510 "Available compression types:\n"
513 " xpress (alias: \"fast\")\n"
514 " lzx (alias: \"maximum\") (default for capture)\n"
515 " lzms (alias: \"recovery\")\n"
521 /* Parse the argument to --compress or --solid-compress */
523 get_compression_type(tchar *optarg, bool solid)
526 unsigned int compression_level = 0;
529 plevel = tstrchr(optarg, T(':'));
535 ultmp = tstrtoul(plevel, &ptmp, 10);
536 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
537 imagex_error(T("Compression level must be a positive integer! "
538 "e.g. --compress=lzx:80"));
539 return WIMLIB_COMPRESSION_TYPE_INVALID;
541 compression_level = ultmp;
544 if (!tstrcasecmp(optarg, T("maximum")) ||
545 !tstrcasecmp(optarg, T("lzx")) ||
546 !tstrcasecmp(optarg, T("max"))) {
547 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
548 } else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress"))) {
549 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
550 } else if (!tstrcasecmp(optarg, T("recovery"))) {
554 "Warning: use of '--compress=recovery' is discouraged because it behaves\n"
555 " differently from DISM. Instead, you typically want to use '--solid' to\n"
556 " create a solid LZMS-compressed WIM or \"ESD file\", similar to DISM's\n"
557 " /compress:recovery. But if you really want *non-solid* LZMS compression,\n"
558 " then you may suppress this warning by specifying '--compress=lzms' instead\n"
559 " of '--compress=recovery'.\n"));
561 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
562 } else if (!tstrcasecmp(optarg, T("lzms"))) {
563 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
564 } else if (!tstrcasecmp(optarg, T("none"))) {
565 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
567 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
568 print_available_compression_types(stderr);
569 return WIMLIB_COMPRESSION_TYPE_INVALID;
572 if (compression_level != 0)
573 wimlib_set_default_compression_level(ctype, compression_level);
577 /* Parse the argument to --compact */
579 set_compact_mode(const tchar *arg, int *extract_flags)
582 if (!tstrcasecmp(arg, T("xpress4k")))
583 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS4K;
584 else if (!tstrcasecmp(arg, T("xpress8k")))
585 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS8K;
586 else if (!tstrcasecmp(arg, T("xpress16k")))
587 flag = WIMLIB_EXTRACT_FLAG_COMPACT_XPRESS16K;
588 else if (!tstrcasecmp(arg, T("lzx")))
589 flag = WIMLIB_EXTRACT_FLAG_COMPACT_LZX;
592 *extract_flags |= flag;
597 "\"%"TS"\" is not a recognized System Compression format. The options are:"
599 " --compact=xpress4k\n"
600 " --compact=xpress8k\n"
601 " --compact=xpress16k\n"
609 set_compress_slow(void)
612 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
613 " Use the '--compress=TYPE:LEVEL' option instead.\n");
615 wimlib_set_default_compression_level(-1, 100);
620 unsigned num_strings;
621 unsigned num_alloc_strings;
624 #define STRING_SET_INITIALIZER \
625 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
627 #define STRING_SET(_strings) \
628 struct string_set _strings = STRING_SET_INITIALIZER
631 string_set_append(struct string_set *set, tchar *glob)
633 unsigned num_alloc_strings = set->num_alloc_strings;
635 if (set->num_strings == num_alloc_strings) {
638 num_alloc_strings += 4;
639 new_strings = realloc(set->strings,
640 sizeof(set->strings[0]) * num_alloc_strings);
642 imagex_error(T("Out of memory!"));
645 set->strings = new_strings;
646 set->num_alloc_strings = num_alloc_strings;
648 set->strings[set->num_strings++] = glob;
653 string_set_destroy(struct string_set *set)
659 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
661 return wimlib_reference_resource_files(wim, (const tchar **)set->strings,
663 WIMLIB_REF_FLAG_GLOB_ENABLE,
668 append_image_property_argument(struct string_set *image_properties)
670 if (!tstrchr(optarg, '=')) {
671 imagex_error(T("'--image-property' argument "
672 "must be in the form NAME=VALUE"));
675 return string_set_append(image_properties, optarg);
679 apply_image_properties(struct string_set *image_properties,
680 WIMStruct *wim, int image, bool *any_changes_ret)
682 bool any_changes = false;
683 for (unsigned i = 0; i < image_properties->num_strings; i++) {
685 const tchar *current_value;
688 name = image_properties->strings[i];
689 value = tstrchr(name, '=');
692 current_value = wimlib_get_image_property(wim, image, name);
693 if (current_value && !tstrcmp(current_value, value)) {
694 imagex_printf(T("The %"TS" property of image %d "
695 "already has value \"%"TS"\".\n"),
698 imagex_printf(T("Setting the %"TS" property of image "
699 "%d to \"%"TS"\".\n"),
701 ret = wimlib_set_image_property(wim, image, name, value);
708 *any_changes_ret = any_changes;
713 do_resource_not_found_warning(const tchar *wimfile,
714 const struct wimlib_wim_info *info,
715 const struct string_set *refglobs)
717 if (info->total_parts > 1) {
718 if (refglobs->num_strings == 0) {
719 imagex_error(T("\"%"TS"\" is part of a split WIM. "
720 "Use --ref to specify the other parts."),
723 imagex_error(T("Perhaps the '--ref' argument did not "
724 "specify all other parts of the split "
728 imagex_error(T("If this is a delta WIM, use the --ref argument "
729 "to specify the WIM(s) on which it is based."));
734 do_metadata_not_found_warning(const tchar *wimfile,
735 const struct wimlib_wim_info *info)
737 if (info->part_number != 1) {
738 imagex_error(T("\"%"TS"\" is not the first part of the split WIM.\n"
739 " You must specify the first part."),
744 /* Returns the size of a file given its name, or -1 if the file does not exist
745 * or its size cannot be determined. */
747 file_get_size(const tchar *filename)
750 if (tstat(filename, &st) == 0)
757 PARSE_STRING_SUCCESS = 0,
758 PARSE_STRING_FAILURE = 1,
759 PARSE_STRING_NONE = 2,
763 * Parses a string token from an array of characters.
765 * Tokens are either whitespace-delimited, or double or single-quoted.
767 * @line_p: Pointer to the pointer to the line of data. Will be updated
768 * to point past the string token iff the return value is
769 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
772 * @len_p: @len_p initially stores the length of the line of data, which may
773 * be 0, and it will be updated to the number of bytes remaining in
774 * the line iff the return value is PARSE_STRING_SUCCESS.
776 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
777 * parsed string token will be returned here.
779 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
780 * PARSE_STRING_FAILURE if the data was invalid due to a missing
781 * closing quote; or PARSE_STRING_NONE if the line ended before the
782 * beginning of a string token was found.
785 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
788 tchar *line = *line_p;
792 /* Skip leading whitespace */
795 return PARSE_STRING_NONE;
796 if (!istspace(*line) && *line != T('\0'))
802 if (quote_char == T('"') || quote_char == T('\'')) {
807 line = tmemchr(line, quote_char, len);
809 imagex_error(T("Missing closing quote: %"TS), fn - 1);
810 return PARSE_STRING_FAILURE;
813 /* Unquoted string. Go until whitespace. Line is terminated
814 * by '\0', so no need to check 'len'. */
818 } while (!istspace(*line) && *line != T('\0'));
825 return PARSE_STRING_SUCCESS;
828 /* Parses a line of data (not an empty line or comment) in the source list file
829 * format. (See the man page for 'wimlib-imagex capture' for details on this
830 * format and the meaning.)
832 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
833 * len == 0. The data in @line will be modified by this function call.
835 * @len: Length of the line of data.
837 * @source: On success, the capture source and target described by the line is
838 * written into this destination. Note that it will contain pointers
839 * to data in the @line array.
841 * Returns true if the line was valid; false otherwise. */
843 parse_source_list_line(tchar *line, size_t len,
844 struct wimlib_capture_source *source)
848 ret = parse_string(&line, &len, &source->fs_source_path);
849 if (ret != PARSE_STRING_SUCCESS)
851 ret = parse_string(&line, &len, &source->wim_target_path);
852 if (ret == PARSE_STRING_NONE)
853 source->wim_target_path = source->fs_source_path;
854 return ret != PARSE_STRING_FAILURE;
857 /* Returns %true if the given line of length @len > 0 is a comment or empty line
858 * in the source list file format. */
860 is_comment_line(const tchar *line, size_t len)
863 if (*line == T('#') || *line == T(';'))
865 if (!istspace(*line) && *line != T('\0'))
875 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
878 tchar *contents = *contents_p;
879 size_t nchars = *nchars_p;
882 for (i = 0; i < nchars; i++)
883 if (contents[i] == T('\n'))
886 /* Handle last line not terminated by a newline */
887 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
888 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
890 imagex_error(T("Out of memory!"));
893 contents[nchars] = T('\n');
894 *contents_p = contents;
902 /* Parses a file in the source list format. (See the man page for
903 * 'wimlib-imagex capture' for details on this format and the meaning.)
905 * @source_list_contents: Contents of the source list file. Note that this
906 * buffer will be modified to save memory allocations,
907 * and cannot be freed until the returned array of
908 * wimlib_capture_source's has also been freed.
910 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
913 * @nsources_ret: On success, the length of the returned array is
916 * Returns: An array of `struct wimlib_capture_source's that can be passed to
917 * the wimlib_add_image_multisource() function to specify how a WIM image is to
919 static struct wimlib_capture_source *
920 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
921 size_t *nsources_ret)
925 struct wimlib_capture_source *sources;
928 nlines = text_file_count_lines(source_list_contents_p,
929 &source_list_nchars);
933 /* Always allocate at least 1 slot, just in case the implementation of
934 * calloc() returns NULL if 0 bytes are requested. */
935 sources = calloc(nlines ?: 1, sizeof(*sources));
937 imagex_error(T("out of memory"));
940 p = *source_list_contents_p;
942 for (i = 0; i < nlines; i++) {
943 /* XXX: Could use rawmemchr() here instead, but it may not be
944 * available on all platforms. */
945 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
946 size_t len = endp - p + 1;
948 if (!is_comment_line(p, len)) {
949 if (!parse_source_list_line(p, len, &sources[j++])) {
961 /* Reads the contents of a file into memory. */
963 file_get_contents(const tchar *filename, size_t *len_ret)
970 if (tstat(filename, &stbuf) != 0) {
971 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
976 fp = tfopen(filename, T("rb"));
978 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
982 buf = malloc(len ? len : 1);
984 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
985 "contents of file \"%"TS"\""), len, filename);
988 if (fread(buf, 1, len, fp) != len) {
989 imagex_error_with_errno(T("Failed to read %zu bytes from the "
990 "file \"%"TS"\""), len, filename);
1004 /* Read standard input until EOF and return the full contents in a malloc()ed
1005 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
1008 stdin_get_contents(size_t *len_ret)
1010 /* stdin can, of course, be a pipe or other non-seekable file, so the
1011 * total length of the data cannot be pre-determined */
1013 size_t newlen = 1024;
1017 char *p = realloc(buf, newlen);
1018 size_t bytes_read, bytes_to_read;
1020 imagex_error(T("out of memory while reading stdin"));
1024 bytes_to_read = newlen - pos;
1025 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
1027 if (bytes_read != bytes_to_read) {
1032 imagex_error_with_errno(T("error reading stdin"));
1046 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
1049 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
1051 *num_tchars_ret = num_bytes;
1053 #else /* !__WIN32__ */
1054 /* On Windows, translate the text to UTF-16LE */
1058 if (num_bytes >= 2 &&
1059 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
1060 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
1062 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
1063 * with something that looks like an ASCII character encoded as
1064 * a UTF-16LE code unit. Assume the file is encoded as
1065 * UTF-16LE. This is not a 100% reliable check. */
1066 num_wchars = num_bytes / 2;
1067 text_wstr = (wchar_t*)text;
1069 /* File does not look like UTF-16LE. Assume it is encoded in
1070 * the current Windows code page. I think these are always
1071 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
1072 * should work as expected. */
1073 text_wstr = win32_mbs_to_wcs(text,
1078 *num_tchars_ret = num_wchars;
1080 #endif /* __WIN32__ */
1084 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
1089 contents = file_get_contents(filename, &num_bytes);
1092 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1096 stdin_get_text_contents(size_t *num_tchars_ret)
1101 contents = stdin_get_contents(&num_bytes);
1104 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
1107 #define TO_PERCENT(numerator, denominator) \
1108 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
1110 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
1111 #define MEBIBYTE_MIN_NBYTES 10000000ULL
1112 #define KIBIBYTE_MIN_NBYTES 10000ULL
1115 get_unit(uint64_t total_bytes, const tchar **name_ret)
1117 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1118 *name_ret = T("GiB");
1120 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1121 *name_ret = T("MiB");
1123 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1124 *name_ret = T("KiB");
1127 *name_ret = T("bytes");
1132 static struct wimlib_progress_info_scan last_scan_progress;
1135 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1137 uint64_t prev_count, cur_count;
1139 prev_count = last_scan_progress.num_nondirs_scanned +
1140 last_scan_progress.num_dirs_scanned;
1141 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1143 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1144 cur_count % 128 == 0)
1146 unsigned unit_shift;
1147 const tchar *unit_name;
1149 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1150 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1151 "%"PRIu64" directories) "),
1152 scan->num_bytes_scanned >> unit_shift,
1154 scan->num_nondirs_scanned,
1155 scan->num_dirs_scanned);
1156 last_scan_progress = *scan;
1159 /* Progress callback function passed to various wimlib functions. */
1160 static enum wimlib_progress_status
1161 imagex_progress_func(enum wimlib_progress_msg msg,
1162 union wimlib_progress_info *info,
1163 void *_ignored_context)
1165 unsigned percent_done;
1166 unsigned unit_shift;
1167 const tchar *unit_name;
1169 if (imagex_be_quiet)
1170 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1172 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1174 static bool started;
1176 if (info->write_streams.compression_type != WIMLIB_COMPRESSION_TYPE_NONE) {
1177 imagex_printf(T("Using %"TS" compression "
1178 "with %u thread%"TS"\n"),
1179 wimlib_get_compression_type_string(
1180 info->write_streams.compression_type),
1181 info->write_streams.num_threads,
1182 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1187 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1188 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1189 info->write_streams.total_bytes);
1191 imagex_printf(T("\rArchiving file data: %"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1192 info->write_streams.completed_bytes >> unit_shift,
1194 info->write_streams.total_bytes >> unit_shift,
1197 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1198 imagex_printf(T("\n"));
1200 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1201 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1202 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1203 imagex_printf(T("\n"));
1205 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1206 info->scan.wim_target_path);
1208 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1210 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1211 switch (info->scan.status) {
1212 case WIMLIB_SCAN_DENTRY_OK:
1213 report_scan_progress(&info->scan, false);
1215 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1216 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1218 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1219 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1220 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1222 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1223 /* Symlink fixups are enabled by default. This is
1224 * mainly intended for Windows, which for some reason
1225 * uses absolute junctions (with drive letters!) in the
1226 * default installation. On UNIX-like systems, warn the
1227 * user when fixing the target of an absolute symbolic
1228 * link, so they know to disable this if they want. */
1230 imagex_printf(T("\nWARNING: Adjusted target of "
1231 "absolute symbolic link \"%"TS"\"\n"
1232 " (Use --norpfix to capture "
1233 "absolute symbolic links as-is)\n"),
1234 info->scan.cur_path);
1241 case WIMLIB_PROGRESS_MSG_SCAN_END:
1242 report_scan_progress(&info->scan, true);
1243 imagex_printf(T("\n"));
1245 case WIMLIB_PROGRESS_MSG_VERIFY_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("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1250 "of %"PRIu64" %"TS" (%u%%) done"),
1251 info->integrity.filename,
1252 info->integrity.completed_bytes >> unit_shift,
1254 info->integrity.total_bytes >> unit_shift,
1257 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1258 imagex_printf(T("\n"));
1260 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1261 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1262 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1263 info->integrity.total_bytes);
1264 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1265 "of %"PRIu64" %"TS" (%u%%) done"),
1266 info->integrity.completed_bytes >> unit_shift,
1268 info->integrity.total_bytes >> unit_shift,
1271 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1272 imagex_printf(T("\n"));
1274 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1275 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1276 "to %"TS" \"%"TS"\"\n"),
1277 info->extract.image,
1278 info->extract.image_name,
1279 info->extract.wimfile_name,
1280 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1281 T("NTFS volume") : T("directory")),
1282 info->extract.target);
1284 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1285 if (info->extract.end_file_count >= 2000) {
1286 percent_done = TO_PERCENT(info->extract.current_file_count,
1287 info->extract.end_file_count);
1288 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1289 info->extract.current_file_count,
1290 info->extract.end_file_count, percent_done);
1291 if (info->extract.current_file_count == info->extract.end_file_count)
1292 imagex_printf(T("\n"));
1295 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1296 percent_done = TO_PERCENT(info->extract.completed_bytes,
1297 info->extract.total_bytes);
1298 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1299 imagex_printf(T("\rExtracting file data: "
1300 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1301 info->extract.completed_bytes >> unit_shift,
1303 info->extract.total_bytes >> unit_shift,
1306 if (info->extract.completed_bytes >= info->extract.total_bytes)
1307 imagex_printf(T("\n"));
1309 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1310 if (info->extract.end_file_count >= 2000) {
1311 percent_done = TO_PERCENT(info->extract.current_file_count,
1312 info->extract.end_file_count);
1313 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1314 info->extract.current_file_count,
1315 info->extract.end_file_count, percent_done);
1316 if (info->extract.current_file_count == info->extract.end_file_count)
1317 imagex_printf(T("\n"));
1320 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1321 if (info->extract.total_parts != 1) {
1322 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1323 info->extract.part_number,
1324 info->extract.total_parts);
1327 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1328 percent_done = TO_PERCENT(info->split.completed_bytes,
1329 info->split.total_bytes);
1330 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1331 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1332 "%"PRIu64" %"TS" (%u%%) written\n"),
1333 info->split.part_name,
1334 info->split.cur_part_number,
1335 info->split.total_parts,
1336 info->split.completed_bytes >> unit_shift,
1338 info->split.total_bytes >> unit_shift,
1342 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1343 if (info->split.completed_bytes == info->split.total_bytes) {
1344 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1345 info->split.cur_part_number,
1346 info->split.total_parts);
1349 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1350 switch (info->update.command->op) {
1351 case WIMLIB_UPDATE_OP_DELETE:
1352 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1353 info->update.command->delete_.wim_path);
1355 case WIMLIB_UPDATE_OP_RENAME:
1356 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1357 info->update.command->rename.wim_source_path,
1358 info->update.command->rename.wim_target_path);
1360 case WIMLIB_UPDATE_OP_ADD:
1365 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1366 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1367 info->replace.path_in_wim);
1369 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1370 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1371 info->wimboot_exclude.path_in_wim);
1373 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1374 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1375 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1376 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1377 info->unmount.mounted_wim,
1378 info->unmount.mounted_image);
1380 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1381 info->unmount.mounted_wim,
1382 info->unmount.mounted_image);
1383 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1387 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1388 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1389 info->verify_image.current_image,
1390 info->verify_image.total_images);
1392 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1393 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1394 info->verify_streams.total_bytes);
1395 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1396 imagex_printf(T("\rVerifying file data: "
1397 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1398 info->verify_streams.completed_bytes >> unit_shift,
1400 info->verify_streams.total_bytes >> unit_shift,
1403 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1404 imagex_printf(T("\n"));
1409 fflush(imagex_info_file);
1410 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1414 parse_num_threads(const tchar *optarg)
1417 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1418 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1419 imagex_error(T("Number of threads must be a non-negative integer!"));
1427 parse_chunk_size(const tchar *optarg)
1430 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1431 if (chunk_size == 0) {
1432 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1433 " with optional K, M, or G suffix"));
1437 if (*tmp == T('k') || *tmp == T('K')) {
1440 } else if (*tmp == T('m') || *tmp == T('M')) {
1443 } else if (*tmp == T('g') || *tmp == T('G')) {
1447 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1448 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1452 if (chunk_size >= UINT32_MAX) {
1453 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1461 * Parse an option passed to an update command.
1463 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1466 * @option: Text string for the option (beginning with --)
1468 * @cmd: `struct wimlib_update_command' that is being constructed for
1471 * Returns true if the option was recognized; false if not.
1474 update_command_add_option(int op, const tchar *option,
1475 struct wimlib_update_command *cmd)
1477 bool recognized = true;
1479 case WIMLIB_UPDATE_OP_ADD:
1480 if (!tstrcmp(option, T("--verbose")))
1481 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1482 else if (!tstrcmp(option, T("--unix-data")))
1483 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1484 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1485 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1486 else if (!tstrcmp(option, T("--strict-acls")))
1487 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1488 else if (!tstrcmp(option, T("--dereference")))
1489 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1490 else if (!tstrcmp(option, T("--no-replace")))
1491 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1495 case WIMLIB_UPDATE_OP_DELETE:
1496 if (!tstrcmp(option, T("--force")))
1497 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1498 else if (!tstrcmp(option, T("--recursive")))
1499 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1510 /* How many nonoption arguments each `imagex update' command expects */
1511 static const unsigned update_command_num_nonoptions[] = {
1512 [WIMLIB_UPDATE_OP_ADD] = 2,
1513 [WIMLIB_UPDATE_OP_DELETE] = 1,
1514 [WIMLIB_UPDATE_OP_RENAME] = 2,
1518 update_command_add_nonoption(int op, const tchar *nonoption,
1519 struct wimlib_update_command *cmd,
1520 unsigned num_nonoptions)
1523 case WIMLIB_UPDATE_OP_ADD:
1524 if (num_nonoptions == 0)
1525 cmd->add.fs_source_path = (tchar*)nonoption;
1527 cmd->add.wim_target_path = (tchar*)nonoption;
1529 case WIMLIB_UPDATE_OP_DELETE:
1530 cmd->delete_.wim_path = (tchar*)nonoption;
1532 case WIMLIB_UPDATE_OP_RENAME:
1533 if (num_nonoptions == 0)
1534 cmd->rename.wim_source_path = (tchar*)nonoption;
1536 cmd->rename.wim_target_path = (tchar*)nonoption;
1542 * Parse a command passed on stdin to `imagex update'.
1544 * @line: Text of the command.
1545 * @len: Length of the line, including a null terminator
1548 * @command: A `struct wimlib_update_command' to fill in from the parsed
1551 * @line_number: Line number of the command, for diagnostics.
1553 * Returns true on success; returns false on parse error.
1556 parse_update_command(tchar *line, size_t len,
1557 struct wimlib_update_command *command,
1561 tchar *command_name;
1563 size_t num_nonoptions;
1565 /* Get the command name ("add", "delete", "rename") */
1566 ret = parse_string(&line, &len, &command_name);
1567 if (ret != PARSE_STRING_SUCCESS)
1570 if (!tstrcasecmp(command_name, T("add"))) {
1571 op = WIMLIB_UPDATE_OP_ADD;
1572 } else if (!tstrcasecmp(command_name, T("delete"))) {
1573 op = WIMLIB_UPDATE_OP_DELETE;
1574 } else if (!tstrcasecmp(command_name, T("rename"))) {
1575 op = WIMLIB_UPDATE_OP_RENAME;
1577 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1578 command_name, line_number);
1583 /* Parse additional options and non-options as needed */
1588 ret = parse_string(&line, &len, &next_string);
1589 if (ret == PARSE_STRING_NONE) /* End of line */
1591 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1593 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1595 if (!update_command_add_option(op, next_string, command))
1597 imagex_error(T("Unrecognized option \"%"TS"\" to "
1598 "update command \"%"TS"\" on line %zu"),
1599 next_string, command_name, line_number);
1605 if (num_nonoptions == update_command_num_nonoptions[op])
1607 imagex_error(T("Unexpected argument \"%"TS"\" in "
1608 "update command on line %zu\n"
1609 " (The \"%"TS"\" command only "
1610 "takes %zu nonoption arguments!)\n"),
1611 next_string, line_number,
1612 command_name, num_nonoptions);
1615 update_command_add_nonoption(op, next_string,
1616 command, num_nonoptions);
1621 if (num_nonoptions != update_command_num_nonoptions[op]) {
1622 imagex_error(T("Not enough arguments to update command "
1623 "\"%"TS"\" on line %zu"), command_name, line_number);
1629 static struct wimlib_update_command *
1630 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1631 size_t *num_cmds_ret)
1635 struct wimlib_update_command *cmds;
1638 nlines = text_file_count_lines(cmd_file_contents_p,
1643 /* Always allocate at least 1 slot, just in case the implementation of
1644 * calloc() returns NULL if 0 bytes are requested. */
1645 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1647 imagex_error(T("out of memory"));
1650 p = *cmd_file_contents_p;
1652 for (i = 0; i < nlines; i++) {
1653 /* XXX: Could use rawmemchr() here instead, but it may not be
1654 * available on all platforms. */
1655 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1656 size_t len = endp - p + 1;
1658 if (!is_comment_line(p, len)) {
1659 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1670 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1671 * one image from a WIM file to an NTFS volume. */
1673 imagex_apply(int argc, tchar **argv, int cmd)
1677 int image = WIMLIB_NO_IMAGE;
1679 struct wimlib_wim_info info;
1681 const tchar *wimfile;
1682 const tchar *target;
1683 const tchar *image_num_or_name = NULL;
1684 int extract_flags = 0;
1686 STRING_SET(refglobs);
1688 for_opt(c, apply_options) {
1690 case IMAGEX_CHECK_OPTION:
1691 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1693 case IMAGEX_VERBOSE_OPTION:
1694 /* No longer does anything. */
1696 case IMAGEX_REF_OPTION:
1697 ret = string_set_append(&refglobs, optarg);
1699 goto out_free_refglobs;
1701 case IMAGEX_UNIX_DATA_OPTION:
1702 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1704 case IMAGEX_NO_ACLS_OPTION:
1705 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1707 case IMAGEX_STRICT_ACLS_OPTION:
1708 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1710 case IMAGEX_NO_ATTRIBUTES_OPTION:
1711 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1713 case IMAGEX_NORPFIX_OPTION:
1714 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1716 case IMAGEX_RPFIX_OPTION:
1717 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1719 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1720 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1721 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1723 case IMAGEX_RESUME_OPTION:
1724 extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME;
1726 case IMAGEX_WIMBOOT_OPTION:
1727 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1729 case IMAGEX_COMPACT_OPTION:
1730 ret = set_compact_mode(optarg, &extract_flags);
1732 goto out_free_refglobs;
1740 if (argc != 2 && argc != 3)
1745 if (!tstrcmp(wimfile, T("-"))) {
1746 /* Attempt to apply pipable WIM from standard input. */
1748 image_num_or_name = NULL;
1751 image_num_or_name = argv[1];
1756 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1757 imagex_progress_func, NULL);
1759 goto out_free_refglobs;
1761 wimlib_get_wim_info(wim, &info);
1764 /* Image explicitly specified. */
1765 image_num_or_name = argv[1];
1766 image = wimlib_resolve_image(wim, image_num_or_name);
1767 ret = verify_image_exists(image, image_num_or_name, wimfile);
1769 goto out_wimlib_free;
1772 /* No image specified; default to image 1, but only if the WIM
1773 * contains exactly one image. */
1775 if (info.image_count != 1) {
1776 imagex_error(T("\"%"TS"\" contains %d images; "
1777 "Please select one (or all)."),
1778 wimfile, info.image_count);
1787 if (refglobs.num_strings) {
1789 imagex_error(T("Can't specify --ref when applying from stdin!"));
1791 goto out_wimlib_free;
1793 ret = wim_reference_globs(wim, &refglobs, open_flags);
1795 goto out_wimlib_free;
1800 /* Interpret a regular file or block device target as an NTFS
1804 if (tstat(target, &stbuf)) {
1805 if (errno != ENOENT) {
1806 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1809 goto out_wimlib_free;
1812 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1813 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1819 ret = wimlib_extract_image(wim, image, target, extract_flags);
1821 set_fd_to_binary_mode(STDIN_FILENO);
1822 ret = wimlib_extract_image_from_pipe_with_progress(
1827 imagex_progress_func,
1831 imagex_printf(T("Done applying WIM image.\n"));
1832 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1834 do_resource_not_found_warning(wimfile, &info, &refglobs);
1836 imagex_error(T( "If you are applying an image "
1837 "from a split pipable WIM,\n"
1838 " make sure you have "
1839 "concatenated together all parts."));
1841 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND && wim) {
1842 do_metadata_not_found_warning(wimfile, &info);
1847 string_set_destroy(&refglobs);
1851 usage(CMD_APPLY, stderr);
1853 goto out_free_refglobs;
1856 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1857 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1858 * the desired image. 'wimlib-imagex append': add a new image to an existing
1861 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1865 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1866 WIMLIB_ADD_FLAG_WINCONFIG |
1867 WIMLIB_ADD_FLAG_VERBOSE;
1868 int write_flags = 0;
1869 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1870 uint32_t chunk_size = UINT32_MAX;
1871 uint32_t solid_chunk_size = UINT32_MAX;
1872 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1873 const tchar *wimfile;
1876 STRING_SET(image_properties);
1879 STRING_SET(base_wimfiles);
1880 WIMStruct **base_wims;
1882 WIMStruct *template_wim;
1883 const tchar *template_wimfile = NULL;
1884 const tchar *template_image_name_or_num = NULL;
1885 int template_image = WIMLIB_NO_IMAGE;
1888 unsigned num_threads = 0;
1893 tchar *config_file = NULL;
1895 bool source_list = false;
1896 size_t source_list_nchars = 0;
1897 tchar *source_list_contents;
1898 bool capture_sources_malloced;
1899 struct wimlib_capture_source *capture_sources;
1901 bool name_defaulted;
1903 for_opt(c, capture_or_append_options) {
1905 case IMAGEX_BOOT_OPTION:
1906 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1908 case IMAGEX_CHECK_OPTION:
1909 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1910 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1912 case IMAGEX_NOCHECK_OPTION:
1913 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1915 case IMAGEX_CONFIG_OPTION:
1916 config_file = optarg;
1917 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1919 case IMAGEX_COMPRESS_OPTION:
1920 compression_type = get_compression_type(optarg, false);
1921 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1924 case IMAGEX_COMPRESS_SLOW_OPTION:
1925 set_compress_slow();
1927 case IMAGEX_CHUNK_SIZE_OPTION:
1928 chunk_size = parse_chunk_size(optarg);
1929 if (chunk_size == UINT32_MAX)
1932 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1933 solid_chunk_size = parse_chunk_size(optarg);
1934 if (solid_chunk_size == UINT32_MAX)
1937 case IMAGEX_SOLID_COMPRESS_OPTION:
1938 solid_ctype = get_compression_type(optarg, true);
1939 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1942 case IMAGEX_SOLID_OPTION:
1943 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1945 case IMAGEX_NO_SOLID_SORT_OPTION:
1946 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1948 case IMAGEX_FLAGS_OPTION: {
1949 tchar *p = alloca((6 + tstrlen(optarg) + 1) * sizeof(tchar));
1950 tsprintf(p, T("FLAGS=%"TS), optarg);
1951 ret = string_set_append(&image_properties, p);
1956 case IMAGEX_IMAGE_PROPERTY_OPTION:
1957 ret = append_image_property_argument(&image_properties);
1961 case IMAGEX_DEREFERENCE_OPTION:
1962 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1964 case IMAGEX_VERBOSE_OPTION:
1965 /* No longer does anything. */
1967 case IMAGEX_THREADS_OPTION:
1968 num_threads = parse_num_threads(optarg);
1969 if (num_threads == UINT_MAX)
1972 case IMAGEX_REBUILD_OPTION:
1973 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1975 case IMAGEX_UNIX_DATA_OPTION:
1976 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1978 case IMAGEX_SOURCE_LIST_OPTION:
1981 case IMAGEX_NO_ACLS_OPTION:
1982 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1984 case IMAGEX_STRICT_ACLS_OPTION:
1985 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1987 case IMAGEX_RPFIX_OPTION:
1988 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1990 case IMAGEX_NORPFIX_OPTION:
1991 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1993 case IMAGEX_PIPABLE_OPTION:
1994 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1996 case IMAGEX_NOT_PIPABLE_OPTION:
1997 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1999 case IMAGEX_UPDATE_OF_OPTION:
2000 if (template_image_name_or_num) {
2001 imagex_error(T("'--update-of' can only be "
2002 "specified one time!"));
2006 colon = tstrrchr(optarg, T(':'));
2009 template_wimfile = optarg;
2011 template_image_name_or_num = colon + 1;
2013 template_wimfile = NULL;
2014 template_image_name_or_num = optarg;
2018 case IMAGEX_DELTA_FROM_OPTION:
2019 if (cmd != CMD_CAPTURE) {
2020 imagex_error(T("'--delta-from' is only "
2021 "valid for capture!"));
2024 ret = string_set_append(&base_wimfiles, optarg);
2027 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
2029 case IMAGEX_WIMBOOT_OPTION:
2030 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
2032 case IMAGEX_UNSAFE_COMPACT_OPTION:
2033 if (cmd != CMD_APPEND) {
2034 imagex_error(T("'--unsafe-compact' is only "
2035 "valid for append!"));
2038 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2040 case IMAGEX_SNAPSHOT_OPTION:
2041 add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
2050 if (argc < 2 || argc > 4)
2056 /* Set default compression type and parameters. */
2059 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2060 /* No compression type specified. Use the default. */
2062 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
2063 /* With --wimboot, default to XPRESS compression. */
2064 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2065 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
2066 /* With --solid, default to LZMS compression. (However,
2067 * this will not affect solid resources!) */
2068 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2070 /* Otherwise, default to LZX compression. */
2071 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
2075 if (!tstrcmp(wimfile, T("-"))) {
2076 /* Writing captured WIM to standard output. */
2078 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2079 imagex_error("Can't write a non-pipable WIM to "
2080 "standard output! Specify --pipable\n"
2081 " if you want to create a pipable WIM "
2082 "(but read the docs first).");
2086 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2088 if (cmd == CMD_APPEND) {
2089 imagex_error(T("Using standard output for append does "
2090 "not make sense."));
2093 wim_fd = STDOUT_FILENO;
2095 imagex_info_file = stderr;
2096 set_fd_to_binary_mode(wim_fd);
2099 /* If template image was specified using --update-of=IMAGE rather
2100 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
2101 if (template_image_name_or_num && !template_wimfile) {
2102 if (base_wimfiles.num_strings == 1) {
2103 /* Capturing delta WIM based on single WIM: default to
2105 template_wimfile = base_wimfiles.strings[0];
2106 } else if (cmd == CMD_APPEND) {
2107 /* Appending to WIM: default to WIM being appended to.
2109 template_wimfile = wimfile;
2111 /* Capturing a normal (non-delta) WIM, so the WIM file
2112 * *must* be explicitly specified. */
2113 if (base_wimfiles.num_strings > 1) {
2114 imagex_error(T("For capture of delta WIM "
2115 "based on multiple existing "
2117 " '--update-of' must "
2118 "specify WIMFILE:IMAGE!"));
2120 imagex_error(T("For capture of non-delta WIM, "
2121 "'--update-of' must specify "
2130 name_defaulted = false;
2132 /* Set default name to SOURCE argument, omitting any directory
2133 * prefixes and trailing slashes. This requires making a copy
2134 * of @source. Leave some free characters at the end in case we
2135 * append a number to keep the name unique. */
2136 size_t source_name_len;
2138 source_name_len = tstrlen(source);
2139 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
2140 name = tbasename(tstrcpy(source_copy, source));
2141 name_defaulted = true;
2144 /* Image description (if given). */
2146 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
2147 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
2148 ret = string_set_append(&image_properties, p);
2154 /* Set up capture sources in source list mode */
2155 if (source[0] == T('-') && source[1] == T('\0')) {
2156 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2158 source_list_contents = file_get_text_contents(source,
2159 &source_list_nchars);
2161 if (!source_list_contents)
2164 capture_sources = parse_source_list(&source_list_contents,
2167 if (!capture_sources) {
2169 goto out_free_source_list_contents;
2171 capture_sources_malloced = true;
2173 /* Set up capture source in non-source-list mode. */
2174 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2175 capture_sources[0].fs_source_path = source;
2176 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2177 capture_sources[0].reserved = 0;
2179 capture_sources_malloced = false;
2180 source_list_contents = NULL;
2183 /* Open the existing WIM, or create a new one. */
2184 if (cmd == CMD_APPEND) {
2185 ret = wimlib_open_wim_with_progress(wimfile,
2186 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2188 imagex_progress_func,
2191 goto out_free_capture_sources;
2193 ret = wimlib_create_new_wim(compression_type, &wim);
2195 goto out_free_capture_sources;
2196 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2199 /* Set chunk size if non-default. */
2200 if (chunk_size != UINT32_MAX) {
2201 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2204 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
2206 int ctype = compression_type;
2208 if (cmd == CMD_APPEND) {
2209 struct wimlib_wim_info info;
2210 wimlib_get_wim_info(wim, &info);
2211 ctype = info.compression_type;
2214 if (ctype == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2215 ret = wimlib_set_output_chunk_size(wim, 4096);
2220 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2221 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2225 if (solid_chunk_size != UINT32_MAX) {
2226 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2232 /* Detect if source is regular file or block device and set NTFS volume
2237 if (tstat(source, &stbuf) == 0) {
2238 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2239 imagex_printf(T("Capturing WIM image from NTFS "
2240 "filesystem on \"%"TS"\"\n"), source);
2241 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2244 if (errno != ENOENT) {
2245 imagex_error_with_errno(T("Failed to stat "
2246 "\"%"TS"\""), source);
2254 /* If the user did not specify an image name, and the basename of the
2255 * source already exists as an image name in the WIM file, append a
2256 * suffix to make it unique. */
2257 if (cmd == CMD_APPEND && name_defaulted) {
2258 unsigned long conflict_idx;
2259 tchar *name_end = tstrchr(name, T('\0'));
2260 for (conflict_idx = 1;
2261 wimlib_image_name_in_use(wim, name);
2264 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2268 /* If capturing a delta WIM, reference resources from the base WIMs
2269 * before adding the new image. */
2270 if (base_wimfiles.num_strings) {
2271 base_wims = calloc(base_wimfiles.num_strings,
2272 sizeof(base_wims[0]));
2273 if (base_wims == NULL) {
2274 imagex_error(T("Out of memory!"));
2279 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2280 ret = wimlib_open_wim_with_progress(
2281 base_wimfiles.strings[i], open_flags,
2282 &base_wims[i], imagex_progress_func, NULL);
2284 goto out_free_base_wims;
2288 ret = wimlib_reference_resources(wim, base_wims,
2289 base_wimfiles.num_strings, 0);
2291 goto out_free_base_wims;
2293 if (base_wimfiles.num_strings == 1) {
2294 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2295 base_wimfiles.strings[0]);
2297 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2298 base_wimfiles.num_strings);
2305 /* If capturing or appending as an update of an existing (template) image,
2306 * open the WIM if needed and parse the image index. */
2307 if (template_image_name_or_num) {
2310 if (base_wimfiles.num_strings == 1 &&
2311 template_wimfile == base_wimfiles.strings[0]) {
2312 template_wim = base_wims[0];
2313 } else if (template_wimfile == wimfile) {
2316 ret = wimlib_open_wim_with_progress(template_wimfile,
2319 imagex_progress_func,
2322 goto out_free_base_wims;
2325 template_image = wimlib_resolve_image(template_wim,
2326 template_image_name_or_num);
2328 if (template_image_name_or_num[0] == T('-')) {
2331 struct wimlib_wim_info info;
2333 wimlib_get_wim_info(template_wim, &info);
2334 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2335 if (n >= 1 && n <= info.image_count &&
2337 tmp != template_image_name_or_num + 1)
2339 template_image = info.image_count - (n - 1);
2342 ret = verify_image_exists_and_is_single(template_image,
2343 template_image_name_or_num,
2346 goto out_free_template_wim;
2348 template_wim = NULL;
2351 ret = wimlib_add_image_multisource(wim,
2358 goto out_free_template_wim;
2360 if (image_properties.num_strings || template_image_name_or_num) {
2361 /* User asked to set additional image properties, or an image on
2362 * which the added one is to be based has been specified with
2364 struct wimlib_wim_info info;
2366 wimlib_get_wim_info(wim, &info);
2368 ret = apply_image_properties(&image_properties, wim,
2369 info.image_count, NULL);
2371 goto out_free_template_wim;
2373 /* Reference template image if the user provided one. */
2374 if (template_image_name_or_num) {
2375 imagex_printf(T("Using image %d "
2376 "from \"%"TS"\" as template\n"),
2377 template_image, template_wimfile);
2378 ret = wimlib_reference_template_image(wim,
2384 goto out_free_template_wim;
2388 /* Write the new WIM or overwrite the existing WIM with the new image
2390 if (cmd == CMD_APPEND) {
2391 ret = wimlib_overwrite(wim, write_flags, num_threads);
2392 } else if (wimfile) {
2393 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2394 write_flags, num_threads);
2396 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2397 write_flags, num_threads);
2399 out_free_template_wim:
2400 /* template_wim may alias base_wims[0] or wim. */
2401 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2402 template_wim != wim)
2403 wimlib_free(template_wim);
2405 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2406 wimlib_free(base_wims[i]);
2410 out_free_capture_sources:
2411 if (capture_sources_malloced)
2412 free(capture_sources);
2413 out_free_source_list_contents:
2414 free(source_list_contents);
2416 string_set_destroy(&image_properties);
2417 string_set_destroy(&base_wimfiles);
2427 /* Remove image(s) from a WIM. */
2429 imagex_delete(int argc, tchar **argv, int cmd)
2432 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2433 int write_flags = 0;
2434 const tchar *wimfile;
2435 const tchar *image_num_or_name;
2440 for_opt(c, delete_options) {
2442 case IMAGEX_CHECK_OPTION:
2443 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2444 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2446 case IMAGEX_SOFT_OPTION:
2447 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2449 case IMAGEX_UNSAFE_COMPACT_OPTION:
2450 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2461 imagex_error(T("Must specify a WIM file"));
2463 imagex_error(T("Must specify an image"));
2467 image_num_or_name = argv[1];
2469 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2470 imagex_progress_func, NULL);
2474 image = wimlib_resolve_image(wim, image_num_or_name);
2476 ret = verify_image_exists(image, image_num_or_name, wimfile);
2478 goto out_wimlib_free;
2480 ret = wimlib_delete_image(wim, image);
2482 imagex_error(T("Failed to delete image from \"%"TS"\""),
2484 goto out_wimlib_free;
2487 ret = wimlib_overwrite(wim, write_flags, 0);
2489 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2490 "deleted"), wimfile);
2498 usage(CMD_DELETE, stderr);
2503 struct print_dentry_options {
2508 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2510 tprintf(T("%"TS"\n"), dentry->full_path);
2513 static const struct {
2516 } file_attr_flags[] = {
2517 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2518 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2519 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2520 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2521 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2522 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2523 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2524 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2525 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2526 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2527 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2528 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2529 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2530 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2531 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2534 #define TIMESTR_MAX 100
2537 timespec_to_string(const struct timespec *spec, tchar *buf)
2539 time_t t = spec->tv_sec;
2542 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2543 buf[TIMESTR_MAX - 1] = '\0';
2547 print_time(const tchar *type, const struct timespec *spec)
2549 tchar timestr[TIMESTR_MAX];
2551 timespec_to_string(spec, timestr);
2553 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2556 static void print_byte_field(const uint8_t field[], size_t len)
2559 tprintf(T("%02hhx"), *field++);
2563 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2565 tchar attr_string[256];
2568 tputs(T("WIM Information:"));
2569 tputs(T("----------------"));
2570 tprintf(T("Path: %"TS"\n"), wimfile);
2571 tprintf(T("GUID: 0x"));
2572 print_byte_field(info->guid, sizeof(info->guid));
2574 tprintf(T("Version: %u\n"), info->wim_version);
2575 tprintf(T("Image Count: %d\n"), info->image_count);
2576 tprintf(T("Compression: %"TS"\n"),
2577 wimlib_get_compression_type_string(info->compression_type));
2578 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2580 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2581 tprintf(T("Boot Index: %d\n"), info->boot_index);
2582 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2584 attr_string[0] = T('\0');
2587 tstrcat(attr_string, T("Pipable, "));
2589 if (info->has_integrity_table)
2590 tstrcat(attr_string, T("Integrity info, "));
2592 if (info->has_rpfix)
2593 tstrcat(attr_string, T("Relative path junction, "));
2595 if (info->resource_only)
2596 tstrcat(attr_string, T("Resource only, "));
2598 if (info->metadata_only)
2599 tstrcat(attr_string, T("Metadata only, "));
2601 if (info->is_marked_readonly)
2602 tstrcat(attr_string, T("Readonly, "));
2604 p = tstrchr(attr_string, T('\0'));
2605 if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2608 tprintf(T("Attributes: %"TS"\n\n"), attr_string);
2612 print_resource(const struct wimlib_resource_entry *resource,
2615 tprintf(T("Hash = 0x"));
2616 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2619 if (!resource->is_missing) {
2620 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2621 resource->uncompressed_size);
2622 if (resource->packed) {
2623 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2624 "bytes @ offset %"PRIu64"\n"),
2625 resource->raw_resource_uncompressed_size,
2626 resource->raw_resource_compressed_size,
2627 resource->raw_resource_offset_in_wim);
2629 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2632 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2633 resource->compressed_size);
2635 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2639 tprintf(T("Part Number = %u\n"), resource->part_number);
2640 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2642 tprintf(T("Flags = "));
2643 if (resource->is_compressed)
2644 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2645 if (resource->is_metadata)
2646 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2647 if (resource->is_free)
2648 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2649 if (resource->is_spanned)
2650 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2651 if (resource->packed)
2652 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2660 print_blobs(WIMStruct *wim)
2662 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2666 default_print_security_descriptor(const uint8_t *sd, size_t size)
2668 tprintf(T("Security Descriptor = "));
2669 print_byte_field(sd, size);
2674 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2678 "----------------------------------------------------------------------------\n"));
2679 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2680 if (dentry->dos_name)
2681 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2682 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2683 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2684 if (file_attr_flags[i].flag & dentry->attributes)
2685 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2686 file_attr_flags[i].name);
2688 if (dentry->security_descriptor) {
2689 print_security_descriptor(dentry->security_descriptor,
2690 dentry->security_descriptor_size);
2693 print_time(T("Creation Time"), &dentry->creation_time);
2694 print_time(T("Last Write Time"), &dentry->last_write_time);
2695 print_time(T("Last Access Time"), &dentry->last_access_time);
2698 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2699 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2701 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2702 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2704 if (dentry->unix_mode != 0) {
2705 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2706 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2707 dentry->unix_uid, dentry->unix_gid,
2708 dentry->unix_mode, dentry->unix_rdev);
2711 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2712 if (dentry->streams[i].stream_name) {
2713 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2714 dentry->streams[i].stream_name);
2715 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2716 tprintf(T("\tRaw encrypted data stream:\n"));
2717 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2718 tprintf(T("\tReparse point stream:\n"));
2720 tprintf(T("\tUnnamed data stream:\n"));
2722 print_resource(&dentry->streams[i].resource, NULL);
2727 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2729 const struct print_dentry_options *options = _options;
2730 if (!options->detailed)
2731 print_dentry_full_path(dentry);
2733 print_dentry_detailed(dentry);
2737 /* Print the files contained in an image(s) in a WIM file. */
2739 imagex_dir(int argc, tchar **argv, int cmd)
2741 const tchar *wimfile;
2742 WIMStruct *wim = NULL;
2745 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2747 struct print_dentry_options options = {
2750 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2752 STRING_SET(refglobs);
2754 for_opt(c, dir_options) {
2756 case IMAGEX_PATH_OPTION:
2759 case IMAGEX_DETAILED_OPTION:
2760 options.detailed = true;
2762 case IMAGEX_ONE_FILE_ONLY_OPTION:
2763 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2765 case IMAGEX_REF_OPTION:
2766 ret = string_set_append(&refglobs, optarg);
2768 goto out_free_refglobs;
2778 imagex_error(T("Must specify a WIM file"));
2782 imagex_error(T("Too many arguments"));
2787 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2788 imagex_progress_func, NULL);
2790 goto out_free_refglobs;
2793 image = wimlib_resolve_image(wim, argv[1]);
2794 ret = verify_image_exists(image, argv[1], wimfile);
2796 goto out_wimlib_free;
2798 /* No image specified; default to image 1, but only if the WIM
2799 * contains exactly one image. */
2801 struct wimlib_wim_info info;
2803 wimlib_get_wim_info(wim, &info);
2804 if (info.image_count != 1) {
2805 imagex_error(T("\"%"TS"\" contains %d images; Please "
2806 "select one (or all)."),
2807 wimfile, info.image_count);
2814 if (refglobs.num_strings) {
2815 ret = wim_reference_globs(wim, &refglobs, 0);
2817 goto out_wimlib_free;
2820 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2821 print_dentry, &options);
2822 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
2823 struct wimlib_wim_info info;
2825 wimlib_get_wim_info(wim, &info);
2826 do_metadata_not_found_warning(wimfile, &info);
2831 string_set_destroy(&refglobs);
2835 usage(CMD_DIR, stderr);
2837 goto out_free_refglobs;
2840 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2843 imagex_export(int argc, tchar **argv, int cmd)
2847 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2848 int write_flags = 0;
2849 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2850 const tchar *src_wimfile;
2851 const tchar *src_image_num_or_name;
2852 const tchar *dest_wimfile;
2854 const tchar *dest_name;
2855 const tchar *dest_desc;
2857 struct wimlib_wim_info src_info;
2858 WIMStruct *dest_wim;
2863 STRING_SET(refglobs);
2864 unsigned num_threads = 0;
2865 uint32_t chunk_size = UINT32_MAX;
2866 uint32_t solid_chunk_size = UINT32_MAX;
2867 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2869 for_opt(c, export_options) {
2871 case IMAGEX_BOOT_OPTION:
2872 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2874 case IMAGEX_CHECK_OPTION:
2875 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2876 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2878 case IMAGEX_NOCHECK_OPTION:
2879 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2881 case IMAGEX_COMPRESS_OPTION:
2882 compression_type = get_compression_type(optarg, false);
2883 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2886 case IMAGEX_COMPRESS_SLOW_OPTION:
2887 set_compress_slow();
2888 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2890 case IMAGEX_RECOMPRESS_OPTION:
2891 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2893 case IMAGEX_SOLID_OPTION:
2894 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2896 case IMAGEX_NO_SOLID_SORT_OPTION:
2897 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2899 case IMAGEX_CHUNK_SIZE_OPTION:
2900 chunk_size = parse_chunk_size(optarg);
2901 if (chunk_size == UINT32_MAX)
2904 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2905 solid_chunk_size = parse_chunk_size(optarg);
2906 if (solid_chunk_size == UINT32_MAX)
2909 case IMAGEX_SOLID_COMPRESS_OPTION:
2910 solid_ctype = get_compression_type(optarg, true);
2911 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2914 case IMAGEX_REF_OPTION:
2915 ret = string_set_append(&refglobs, optarg);
2917 goto out_free_refglobs;
2919 case IMAGEX_THREADS_OPTION:
2920 num_threads = parse_num_threads(optarg);
2921 if (num_threads == UINT_MAX)
2924 case IMAGEX_REBUILD_OPTION:
2925 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2927 case IMAGEX_PIPABLE_OPTION:
2928 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2930 case IMAGEX_NOT_PIPABLE_OPTION:
2931 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2933 case IMAGEX_WIMBOOT_OPTION:
2934 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2936 case IMAGEX_UNSAFE_COMPACT_OPTION:
2937 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
2945 if (argc < 3 || argc > 5)
2947 src_wimfile = argv[0];
2948 src_image_num_or_name = argv[1];
2949 dest_wimfile = argv[2];
2950 dest_name = (argc >= 4) ? argv[3] : NULL;
2951 dest_desc = (argc >= 5) ? argv[4] : NULL;
2952 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2953 imagex_progress_func, NULL);
2955 goto out_free_refglobs;
2957 wimlib_get_wim_info(src_wim, &src_info);
2959 /* Determine if the destination is an existing file or not. If so, we
2960 * try to append the exported image(s) to it; otherwise, we create a new
2961 * WIM containing the exported image(s). Furthermore, determine if we
2962 * need to write a pipable WIM directly to standard output. */
2964 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2966 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2967 imagex_error("Can't write a non-pipable WIM to "
2968 "standard output! Specify --pipable\n"
2969 " if you want to create a pipable WIM "
2970 "(but read the docs first).");
2972 goto out_free_src_wim;
2975 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2977 dest_wimfile = NULL;
2978 dest_wim_fd = STDOUT_FILENO;
2979 imagex_info_file = stderr;
2980 set_fd_to_binary_mode(dest_wim_fd);
2983 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2985 /* Destination file exists. */
2987 if (!S_ISREG(stbuf.st_mode)) {
2988 imagex_error(T("\"%"TS"\" is not a regular file"),
2991 goto out_free_src_wim;
2993 ret = wimlib_open_wim_with_progress(dest_wimfile,
2995 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2997 imagex_progress_func,
3000 goto out_free_src_wim;
3002 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3003 /* The user specified a compression type, but we're
3004 * exporting to an existing WIM. Make sure the
3005 * specified compression type is the same as the
3006 * compression type of the existing destination WIM. */
3007 struct wimlib_wim_info dest_info;
3009 wimlib_get_wim_info(dest_wim, &dest_info);
3010 if (compression_type != dest_info.compression_type) {
3011 imagex_error(T("Cannot specify a compression type that is "
3012 "not the same as that used in the "
3013 "destination WIM"));
3015 goto out_free_dest_wim;
3021 if (errno != ENOENT) {
3022 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
3025 goto out_free_src_wim;
3028 if (write_flags & WIMLIB_WRITE_FLAG_UNSAFE_COMPACT) {
3029 imagex_error(T("'--unsafe-compact' is only valid when "
3030 "exporting to an existing WIM file!"));
3032 goto out_free_src_wim;
3035 /* dest_wimfile is not an existing file, so create a new WIM. */
3037 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
3038 /* The user did not specify a compression type; default
3039 * to that of the source WIM, unless --solid or
3040 * --wimboot was specified. */
3042 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
3043 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
3044 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3045 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
3047 compression_type = src_info.compression_type;
3049 ret = wimlib_create_new_wim(compression_type, &dest_wim);
3051 goto out_free_src_wim;
3053 wimlib_register_progress_function(dest_wim,
3054 imagex_progress_func, NULL);
3056 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
3057 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
3059 /* For --wimboot export, use small XPRESS chunks. */
3060 wimlib_set_output_chunk_size(dest_wim, 4096);
3061 } else if (compression_type == src_info.compression_type &&
3062 chunk_size == UINT32_MAX)
3064 /* Use same chunk size if compression type is the same. */
3065 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
3069 if (chunk_size != UINT32_MAX) {
3070 /* Set destination chunk size. */
3071 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
3073 goto out_free_dest_wim;
3075 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3076 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
3078 goto out_free_dest_wim;
3080 if (solid_chunk_size != UINT32_MAX) {
3081 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
3083 goto out_free_dest_wim;
3086 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
3087 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
3089 goto out_free_dest_wim;
3091 if (refglobs.num_strings) {
3092 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
3094 goto out_free_dest_wim;
3097 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
3098 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
3100 imagex_error(T("--boot specified for all-images export, but source WIM "
3101 "has no bootable image."));
3103 goto out_free_dest_wim;
3106 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
3107 dest_desc, export_flags);
3109 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3110 do_resource_not_found_warning(src_wimfile,
3111 &src_info, &refglobs);
3112 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3113 do_metadata_not_found_warning(src_wimfile, &src_info);
3115 goto out_free_dest_wim;
3119 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
3120 else if (dest_wimfile)
3121 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
3122 write_flags, num_threads);
3124 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
3125 WIMLIB_ALL_IMAGES, write_flags,
3128 wimlib_free(dest_wim);
3130 wimlib_free(src_wim);
3132 string_set_destroy(&refglobs);
3136 usage(CMD_EXPORT, stderr);
3139 goto out_free_refglobs;
3142 /* Extract files or directories from a WIM image */
3144 imagex_extract(int argc, tchar **argv, int cmd)
3151 const tchar *wimfile;
3152 const tchar *image_num_or_name;
3153 tchar *dest_dir = T(".");
3154 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
3155 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3156 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3157 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3159 STRING_SET(refglobs);
3161 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
3163 for_opt(c, extract_options) {
3165 case IMAGEX_CHECK_OPTION:
3166 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3168 case IMAGEX_VERBOSE_OPTION:
3169 /* No longer does anything. */
3171 case IMAGEX_REF_OPTION:
3172 ret = string_set_append(&refglobs, optarg);
3174 goto out_free_refglobs;
3176 case IMAGEX_UNIX_DATA_OPTION:
3177 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
3179 case IMAGEX_NO_ACLS_OPTION:
3180 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3182 case IMAGEX_STRICT_ACLS_OPTION:
3183 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3185 case IMAGEX_NO_ATTRIBUTES_OPTION:
3186 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3188 case IMAGEX_DEST_DIR_OPTION:
3191 case IMAGEX_TO_STDOUT_OPTION:
3192 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3193 imagex_info_file = stderr;
3194 imagex_be_quiet = true;
3195 set_fd_to_binary_mode(STDOUT_FILENO);
3197 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3198 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3199 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3201 case IMAGEX_NO_GLOBS_OPTION:
3202 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3204 case IMAGEX_NULLGLOB_OPTION:
3205 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3207 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3208 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3210 case IMAGEX_WIMBOOT_OPTION:
3211 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3213 case IMAGEX_COMPACT_OPTION:
3214 ret = set_compact_mode(optarg, &extract_flags);
3216 goto out_free_refglobs;
3228 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3229 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3231 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3236 image_num_or_name = argv[1];
3241 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3242 imagex_progress_func, NULL);
3244 goto out_free_refglobs;
3246 image = wimlib_resolve_image(wim, image_num_or_name);
3247 ret = verify_image_exists_and_is_single(image,
3251 goto out_wimlib_free;
3253 if (refglobs.num_strings) {
3254 ret = wim_reference_globs(wim, &refglobs, open_flags);
3256 goto out_wimlib_free;
3262 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3265 while (argc != 0 && ret == 0) {
3269 num_paths < argc && argv[num_paths][0] != T('@');
3274 ret = wimlib_extract_paths(wim, image, dest_dir,
3275 (const tchar **)argv,
3277 extract_flags | notlist_extract_flags);
3281 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3290 if (!imagex_be_quiet)
3291 imagex_printf(T("Done extracting files.\n"));
3292 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3293 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3294 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3295 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3296 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3299 T("Note: You can use the '--nullglob' "
3300 "option to ignore missing files.\n"));
3302 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3303 "files and directories\n"
3304 " are in the WIM image.\n"),
3305 get_cmd_string(CMD_DIR, false));
3306 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3307 struct wimlib_wim_info info;
3309 wimlib_get_wim_info(wim, &info);
3310 do_resource_not_found_warning(wimfile, &info, &refglobs);
3311 } else if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3312 struct wimlib_wim_info info;
3314 wimlib_get_wim_info(wim, &info);
3315 do_metadata_not_found_warning(wimfile, &info);
3320 string_set_destroy(&refglobs);
3324 usage(CMD_EXTRACT, stderr);
3327 goto out_free_refglobs;
3330 /* Prints information about a WIM file; also can mark an image as bootable,
3331 * change the name of an image, or change the description of an image. */
3333 imagex_info(int argc, tchar **argv, int cmd)
3338 bool nocheck = false;
3339 bool header = false;
3342 bool short_header = true;
3343 const tchar *xml_out_file = NULL;
3344 const tchar *wimfile;
3345 const tchar *image_num_or_name;
3346 STRING_SET(image_properties);
3351 struct wimlib_wim_info info;
3353 for_opt(c, info_options) {
3355 case IMAGEX_BOOT_OPTION:
3358 case IMAGEX_CHECK_OPTION:
3361 case IMAGEX_NOCHECK_OPTION:
3364 case IMAGEX_HEADER_OPTION:
3366 short_header = false;
3368 case IMAGEX_BLOBS_OPTION:
3370 short_header = false;
3372 case IMAGEX_XML_OPTION:
3374 short_header = false;
3376 case IMAGEX_EXTRACT_XML_OPTION:
3377 xml_out_file = optarg;
3378 short_header = false;
3380 case IMAGEX_METADATA_OPTION:
3381 imagex_error(T("The --metadata option has been removed. "
3382 "Use 'wimdir --detail' instead."));
3384 case IMAGEX_IMAGE_PROPERTY_OPTION:
3385 ret = append_image_property_argument(&image_properties);
3396 if (argc < 1 || argc > 4)
3400 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3404 tchar *p = alloca((5 + tstrlen(argv[2]) + 1) * sizeof(tchar));
3405 tsprintf(p, T("NAME=%"TS), argv[2]);
3406 ret = string_set_append(&image_properties, p);
3413 tchar *p = alloca((12 + tstrlen(argv[3]) + 1) * sizeof(tchar));
3414 tsprintf(p, T("DESCRIPTION=%"TS), argv[3]);
3415 ret = string_set_append(&image_properties, p);
3420 if (check && nocheck) {
3421 imagex_error(T("Can't specify both --check and --nocheck"));
3426 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3428 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3429 imagex_progress_func, NULL);
3433 wimlib_get_wim_info(wim, &info);
3435 image = wimlib_resolve_image(wim, image_num_or_name);
3436 ret = WIMLIB_ERR_INVALID_IMAGE;
3437 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3438 verify_image_exists(image, image_num_or_name, wimfile);
3440 imagex_error(T("If you would like to set the boot "
3441 "index to 0, specify image \"0\" with "
3442 "the --boot flag."));
3444 goto out_wimlib_free;
3447 if (boot && info.image_count == 0) {
3448 imagex_error(T("--boot is meaningless on a WIM with no images"));
3449 goto out_wimlib_free;
3452 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3454 imagex_error(T("Cannot specify the --boot flag "
3455 "without specifying a specific "
3456 "image in a multi-image WIM"));
3457 goto out_wimlib_free;
3459 if (image_properties.num_strings) {
3460 imagex_error(T("Can't change image properties without "
3461 "specifying a specific image in a "
3462 "multi-image WIM"));
3463 goto out_wimlib_free;
3467 /* Operations that print information are separated from operations that
3468 * recreate the WIM file. */
3469 if (!image_properties.num_strings && !boot) {
3471 /* Read-only operations */
3473 if (image == WIMLIB_NO_IMAGE) {
3474 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3475 image_num_or_name, wimfile);
3476 goto out_wimlib_free;
3479 if (image == WIMLIB_ALL_IMAGES && short_header)
3480 print_wim_information(wimfile, &info);
3483 wimlib_print_header(wim);
3486 if (info.total_parts != 1) {
3487 tfprintf(stderr, T("Warning: Only showing the blobs "
3488 "for part %d of a %d-part WIM.\n"),
3489 info.part_number, info.total_parts);
3495 ret = wimlib_extract_xml_data(wim, stdout);
3497 goto out_wimlib_free;
3503 fp = tfopen(xml_out_file, T("wb"));
3505 imagex_error_with_errno(T("Failed to open the "
3506 "file \"%"TS"\" for "
3510 goto out_wimlib_free;
3512 ret = wimlib_extract_xml_data(wim, fp);
3514 imagex_error(T("Failed to close the file "
3520 goto out_wimlib_free;
3524 wimlib_print_available_images(wim, image);
3528 /* Modification operations */
3529 bool any_property_changes;
3531 if (image == WIMLIB_ALL_IMAGES)
3534 if (image == WIMLIB_NO_IMAGE && image_properties.num_strings) {
3535 imagex_error(T("Cannot change image properties "
3536 "when using image 0"));
3538 goto out_wimlib_free;
3542 if (image == info.boot_index) {
3543 imagex_printf(T("Image %d is already marked as "
3544 "bootable.\n"), image);
3547 imagex_printf(T("Marking image %d as bootable.\n"),
3549 info.boot_index = image;
3550 ret = wimlib_set_wim_info(wim, &info,
3551 WIMLIB_CHANGE_BOOT_INDEX);
3553 goto out_wimlib_free;
3557 ret = apply_image_properties(&image_properties, wim, image,
3558 &any_property_changes);
3560 goto out_wimlib_free;
3562 /* Only call wimlib_overwrite() if something actually needs to
3564 if (boot || any_property_changes ||
3565 (check && !info.has_integrity_table) ||
3566 (nocheck && info.has_integrity_table))
3568 int write_flags = 0;
3571 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3573 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3574 ret = wimlib_overwrite(wim, write_flags, 1);
3576 imagex_printf(T("The file \"%"TS"\" was not modified "
3577 "because nothing needed to be done.\n"),
3585 string_set_destroy(&image_properties);
3589 usage(CMD_INFO, stderr);
3595 /* Join split WIMs into one part WIM */
3597 imagex_join(int argc, tchar **argv, int cmd)
3600 int swm_open_flags = 0;
3601 int wim_write_flags = 0;
3602 const tchar *output_path;
3605 for_opt(c, join_options) {
3607 case IMAGEX_CHECK_OPTION:
3608 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3609 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3619 imagex_error(T("Must specify one or more split WIM (.swm) "
3623 output_path = argv[0];
3624 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3629 imagex_progress_func,
3635 usage(CMD_JOIN, stderr);
3640 #if WIM_MOUNTING_SUPPORTED
3642 /* Mounts a WIM image. */
3644 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3647 int mount_flags = 0;
3649 const tchar *staging_dir = NULL;
3650 const tchar *wimfile;
3653 struct wimlib_wim_info info;
3657 STRING_SET(refglobs);
3659 if (cmd == CMD_MOUNTRW) {
3660 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3661 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3664 for_opt(c, mount_options) {
3666 case IMAGEX_ALLOW_OTHER_OPTION:
3667 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3669 case IMAGEX_CHECK_OPTION:
3670 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3672 case IMAGEX_DEBUG_OPTION:
3673 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3675 case IMAGEX_STREAMS_INTERFACE_OPTION:
3676 if (!tstrcasecmp(optarg, T("none")))
3677 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3678 else if (!tstrcasecmp(optarg, T("xattr")))
3679 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3680 else if (!tstrcasecmp(optarg, T("windows")))
3681 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3683 imagex_error(T("Unknown stream interface \"%"TS"\""),
3688 case IMAGEX_REF_OPTION:
3689 ret = string_set_append(&refglobs, optarg);
3691 goto out_free_refglobs;
3693 case IMAGEX_STAGING_DIR_OPTION:
3694 staging_dir = optarg;
3696 case IMAGEX_UNIX_DATA_OPTION:
3697 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3705 if (argc != 2 && argc != 3)
3710 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3711 imagex_progress_func, NULL);
3713 goto out_free_refglobs;
3715 wimlib_get_wim_info(wim, &info);
3718 /* Image explicitly specified. */
3719 image = wimlib_resolve_image(wim, argv[1]);
3721 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3725 /* No image specified; default to image 1, but only if the WIM
3726 * contains exactly one image. */
3728 if (info.image_count != 1) {
3729 imagex_error(T("\"%"TS"\" contains %d images; Please "
3730 "select one."), wimfile, info.image_count);
3738 if (refglobs.num_strings) {
3739 ret = wim_reference_globs(wim, &refglobs, open_flags);
3744 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3746 if (ret == WIMLIB_ERR_METADATA_NOT_FOUND) {
3747 do_metadata_not_found_warning(wimfile, &info);
3749 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3751 image, wimfile, dir);
3757 string_set_destroy(&refglobs);
3763 goto out_free_refglobs;
3765 #endif /* WIM_MOUNTING_SUPPORTED */
3767 /* Rebuild a WIM file */
3769 imagex_optimize(int argc, tchar **argv, int cmd)
3772 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3773 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3774 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3775 uint32_t chunk_size = UINT32_MAX;
3776 uint32_t solid_chunk_size = UINT32_MAX;
3777 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3780 const tchar *wimfile;
3783 unsigned num_threads = 0;
3785 for_opt(c, optimize_options) {
3787 case IMAGEX_CHECK_OPTION:
3788 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3789 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3791 case IMAGEX_NOCHECK_OPTION:
3792 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3794 case IMAGEX_COMPRESS_OPTION:
3795 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3796 compression_type = get_compression_type(optarg, false);
3797 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3800 case IMAGEX_COMPRESS_SLOW_OPTION:
3801 set_compress_slow();
3802 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3804 case IMAGEX_RECOMPRESS_OPTION:
3805 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3807 case IMAGEX_CHUNK_SIZE_OPTION:
3808 chunk_size = parse_chunk_size(optarg);
3809 if (chunk_size == UINT32_MAX)
3812 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3813 solid_chunk_size = parse_chunk_size(optarg);
3814 if (solid_chunk_size == UINT32_MAX)
3817 case IMAGEX_SOLID_COMPRESS_OPTION:
3818 solid_ctype = get_compression_type(optarg, true);
3819 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3822 case IMAGEX_SOLID_OPTION:
3823 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3824 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3826 case IMAGEX_NO_SOLID_SORT_OPTION:
3827 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3829 case IMAGEX_THREADS_OPTION:
3830 num_threads = parse_num_threads(optarg);
3831 if (num_threads == UINT_MAX)
3834 case IMAGEX_PIPABLE_OPTION:
3835 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3837 case IMAGEX_NOT_PIPABLE_OPTION:
3838 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3840 case IMAGEX_UNSAFE_COMPACT_OPTION:
3841 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
3855 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3856 imagex_progress_func, NULL);
3860 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3861 /* Change compression type. */
3862 ret = wimlib_set_output_compression_type(wim, compression_type);
3864 goto out_wimlib_free;
3867 if (chunk_size != UINT32_MAX) {
3868 /* Change chunk size. */
3869 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3871 goto out_wimlib_free;
3873 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3874 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3876 goto out_wimlib_free;
3878 if (solid_chunk_size != UINT32_MAX) {
3879 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3881 goto out_wimlib_free;
3884 old_size = file_get_size(wimfile);
3885 tprintf(T("\"%"TS"\" original size: "), wimfile);
3887 tputs(T("Unknown"));
3889 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3891 ret = wimlib_overwrite(wim, write_flags, num_threads);
3893 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3894 goto out_wimlib_free;
3897 new_size = file_get_size(wimfile);
3898 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3900 tputs(T("Unknown"));
3902 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3904 tfputs(T("Space saved: "), stdout);
3905 if (new_size != -1 && old_size != -1) {
3906 tprintf(T("%lld KiB\n"),
3907 ((long long)old_size - (long long)new_size) >> 10);
3909 tputs(T("Unknown"));
3918 usage(CMD_OPTIMIZE, stderr);
3924 /* Split a WIM into a spanned set */
3926 imagex_split(int argc, tchar **argv, int cmd)
3930 int write_flags = 0;
3931 unsigned long part_size;
3936 for_opt(c, split_options) {
3938 case IMAGEX_CHECK_OPTION:
3939 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3940 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3952 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3953 if (tmp == argv[2] || *tmp) {
3954 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3955 imagex_error(T("The part size must be an integer or "
3956 "floating-point number of megabytes."));
3959 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3960 imagex_progress_func, NULL);
3964 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3970 usage(CMD_SPLIT, stderr);
3976 #if WIM_MOUNTING_SUPPORTED
3977 /* Unmounts a mounted WIM image. */
3979 imagex_unmount(int argc, tchar **argv, int cmd)
3982 int unmount_flags = 0;
3985 for_opt(c, unmount_options) {
3987 case IMAGEX_COMMIT_OPTION:
3988 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3990 case IMAGEX_CHECK_OPTION:
3991 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3993 case IMAGEX_REBUILD_OPTION:
3994 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3996 case IMAGEX_LAZY_OPTION:
3997 case IMAGEX_FORCE_OPTION:
3998 /* Now, unmount is lazy by default. However, committing
3999 * the image will fail with
4000 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
4001 * file descriptors on the WIM image. The
4002 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
4003 * descriptors to be closed. */
4004 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
4006 case IMAGEX_NEW_IMAGE_OPTION:
4007 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
4018 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
4019 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
4020 imagex_error(T("--new-image is meaningless "
4021 "without --commit also specified!"));
4026 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
4027 imagex_progress_func, NULL);
4029 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
4030 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
4032 "\tNote: Use --commit --force to force changes "
4033 "to be committed, regardless\n"
4034 "\t of open files.\n"));
4041 usage(CMD_UNMOUNT, stderr);
4046 #endif /* WIM_MOUNTING_SUPPORTED */
4049 * Add, delete, or rename files in a WIM image.
4052 imagex_update(int argc, tchar **argv, int cmd)
4054 const tchar *wimfile;
4058 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
4059 int write_flags = 0;
4060 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
4061 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
4062 WIMLIB_ADD_FLAG_VERBOSE |
4063 WIMLIB_ADD_FLAG_WINCONFIG;
4064 int default_delete_flags = 0;
4065 unsigned num_threads = 0;
4067 tchar *cmd_file_contents;
4068 size_t cmd_file_nchars;
4069 struct wimlib_update_command *cmds;
4071 tchar *command_str = NULL;
4072 tchar *config_file = NULL;
4073 tchar *wimboot_config = NULL;
4075 for_opt(c, update_options) {
4077 /* Generic or write options */
4078 case IMAGEX_THREADS_OPTION:
4079 num_threads = parse_num_threads(optarg);
4080 if (num_threads == UINT_MAX)
4083 case IMAGEX_CHECK_OPTION:
4084 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4085 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
4087 case IMAGEX_REBUILD_OPTION:
4088 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
4090 case IMAGEX_COMMAND_OPTION:
4092 imagex_error(T("--command may only be specified "
4093 "one time. Please provide\n"
4094 " the update commands "
4095 "on standard input instead."));
4098 command_str = tstrdup(optarg);
4100 imagex_error(T("Out of memory!"));
4104 case IMAGEX_WIMBOOT_CONFIG_OPTION:
4105 wimboot_config = optarg;
4107 /* Default delete options */
4108 case IMAGEX_FORCE_OPTION:
4109 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
4111 case IMAGEX_RECURSIVE_OPTION:
4112 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
4115 /* Global add option */
4116 case IMAGEX_CONFIG_OPTION:
4117 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
4118 config_file = optarg;
4121 /* Default add options */
4122 case IMAGEX_VERBOSE_OPTION:
4123 /* No longer does anything. */
4125 case IMAGEX_DEREFERENCE_OPTION:
4126 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
4128 case IMAGEX_UNIX_DATA_OPTION:
4129 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
4131 case IMAGEX_NO_ACLS_OPTION:
4132 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
4134 case IMAGEX_STRICT_ACLS_OPTION:
4135 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
4137 case IMAGEX_NO_REPLACE_OPTION:
4138 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
4140 case IMAGEX_UNSAFE_COMPACT_OPTION:
4141 write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
4150 if (argc != 1 && argc != 2)
4154 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
4155 imagex_progress_func, NULL);
4157 goto out_free_command_str;
4160 /* Image explicitly specified. */
4161 image = wimlib_resolve_image(wim, argv[1]);
4162 ret = verify_image_exists_and_is_single(image, argv[1],
4165 goto out_wimlib_free;
4167 /* No image specified; default to image 1, but only if the WIM
4168 * contains exactly one image. */
4169 struct wimlib_wim_info info;
4171 wimlib_get_wim_info(wim, &info);
4172 if (info.image_count != 1) {
4173 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
4174 wimfile, info.image_count);
4181 /* Read update commands from standard input, or the command string if
4184 cmd_file_contents = NULL;
4185 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
4189 goto out_free_cmd_file_contents;
4191 } else if (!wimboot_config) {
4192 if (isatty(STDIN_FILENO)) {
4193 tputs(T("Reading update commands from standard input..."));
4194 recommend_man_page(CMD_UPDATE, stdout);
4196 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4197 if (!cmd_file_contents) {
4199 goto out_wimlib_free;
4202 /* Parse the update commands */
4203 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4207 goto out_free_cmd_file_contents;
4210 cmd_file_contents = NULL;
4215 /* Set default flags and capture config on the update commands */
4216 for (size_t i = 0; i < num_cmds; i++) {
4217 switch (cmds[i].op) {
4218 case WIMLIB_UPDATE_OP_ADD:
4219 cmds[i].add.add_flags |= default_add_flags;
4220 cmds[i].add.config_file = config_file;
4222 case WIMLIB_UPDATE_OP_DELETE:
4223 cmds[i].delete_.delete_flags |= default_delete_flags;
4230 /* Execute the update commands */
4231 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4235 if (wimboot_config) {
4236 /* --wimboot-config=FILE is short for an
4237 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4239 struct wimlib_update_command cmd;
4241 cmd.op = WIMLIB_UPDATE_OP_ADD;
4242 cmd.add.fs_source_path = wimboot_config;
4243 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4244 cmd.add.config_file = NULL;
4245 cmd.add.add_flags = 0;
4247 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4252 /* Overwrite the updated WIM */
4253 ret = wimlib_overwrite(wim, write_flags, num_threads);
4256 out_free_cmd_file_contents:
4257 free(cmd_file_contents);
4260 out_free_command_str:
4265 usage(CMD_UPDATE, stderr);
4268 goto out_free_command_str;
4271 /* Verify a WIM file. */
4273 imagex_verify(int argc, tchar **argv, int cmd)
4276 const tchar *wimfile;
4278 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4279 int verify_flags = 0;
4280 STRING_SET(refglobs);
4283 for_opt(c, verify_options) {
4285 case IMAGEX_REF_OPTION:
4286 ret = string_set_append(&refglobs, optarg);
4288 goto out_free_refglobs;
4290 case IMAGEX_NOCHECK_OPTION:
4291 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4303 imagex_error(T("Must specify a WIM file!"));
4305 imagex_error(T("At most one WIM file can be specified!"));
4311 ret = wimlib_open_wim_with_progress(wimfile,
4314 imagex_progress_func,
4317 goto out_free_refglobs;
4319 ret = wim_reference_globs(wim, &refglobs, open_flags);
4321 goto out_wimlib_free;
4323 ret = wimlib_verify_wim(wim, verify_flags);
4325 tputc(T('\n'), stderr);
4326 imagex_error(T("\"%"TS"\" failed verification!"),
4328 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4329 refglobs.num_strings == 0)
4331 imagex_printf(T("Note: if this WIM file is not standalone, "
4332 "use the --ref option to specify the other parts.\n"));
4335 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4342 string_set_destroy(&refglobs);
4346 usage(CMD_VERIFY, stderr);
4348 goto out_free_refglobs;
4351 struct imagex_command {
4353 int (*func)(int argc, tchar **argv, int cmd);
4356 static const struct imagex_command imagex_commands[] = {
4357 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4358 [CMD_APPLY] = {T("apply"), imagex_apply},
4359 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4360 [CMD_DELETE] = {T("delete"), imagex_delete},
4361 [CMD_DIR ] = {T("dir"), imagex_dir},
4362 [CMD_EXPORT] = {T("export"), imagex_export},
4363 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4364 [CMD_INFO] = {T("info"), imagex_info},
4365 [CMD_JOIN] = {T("join"), imagex_join},
4366 #if WIM_MOUNTING_SUPPORTED
4367 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4368 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4370 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4371 [CMD_SPLIT] = {T("split"), imagex_split},
4372 #if WIM_MOUNTING_SUPPORTED
4373 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4375 [CMD_UPDATE] = {T("update"), imagex_update},
4376 [CMD_VERIFY] = {T("verify"), imagex_verify},
4381 /* Can be a directory or source list file. But source list file is probably
4382 * a rare use case, so just say directory. */
4383 # define SOURCE_STR T("DIRECTORY")
4385 /* Can only be a directory */
4386 # define TARGET_STR T("DIRECTORY")
4389 /* Can be a directory, NTFS volume, or source list file. */
4390 # define SOURCE_STR T("SOURCE")
4392 /* Can be a directory or NTFS volume. */
4393 # define TARGET_STR T("TARGET")
4397 static const tchar *usage_strings[] = {
4400 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4401 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4402 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4403 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4404 " [--wimboot] [--unix-data] [--dereference] [--snapshot]\n"
4408 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4409 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4410 " [--no-attributes] [--rpfix] [--norpfix]\n"
4411 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4412 " [--compact=FORMAT]\n"
4416 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4417 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4418 " [--config=FILE] [--threads=NUM_THREADS]\n"
4419 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4420 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4421 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4426 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4430 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4434 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4435 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4436 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4437 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4438 " [--wimboot] [--solid]\n"
4442 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4443 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4444 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4445 " [--no-attributes] [--include-invalid-names]\n"
4446 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4450 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4451 " [--boot] [--check] [--nocheck] [--xml]\n"
4452 " [--extract-xml FILE] [--header] [--blobs]\n"
4453 " [--image-property NAME=VALUE]\n"
4457 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4459 #if WIM_MOUNTING_SUPPORTED
4462 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4463 " [--check] [--streams-interface=INTERFACE]\n"
4464 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4468 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4469 " [--check] [--streams-interface=INTERFACE]\n"
4470 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4476 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4477 " [--check] [--nocheck] [--solid]\n"
4482 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4484 #if WIM_MOUNTING_SUPPORTED
4487 " %"TS" DIRECTORY\n"
4488 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4493 " %"TS" WIMFILE [IMAGE]\n"
4494 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4495 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4496 " [--command=STRING] [--wimboot-config=FILE]\n"
4501 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4505 static const tchar *invocation_name;
4506 static int invocation_cmd = CMD_NONE;
4508 static const tchar *get_cmd_string(int cmd, bool nospace)
4510 static tchar buf[50];
4511 if (cmd == CMD_NONE) {
4512 return T("wimlib-imagex");
4513 } else if (invocation_cmd != CMD_NONE) {
4514 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4516 const tchar *format;
4519 format = T("%"TS"-%"TS"");
4521 format = T("%"TS" %"TS"");
4522 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4530 static const tchar *s =
4532 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4533 "Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers\n"
4534 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4535 "This is free software: you are free to change and redistribute it.\n"
4536 "There is NO WARRANTY, to the extent permitted by law.\n"
4538 "Report bugs to "PACKAGE_BUGREPORT".\n"
4545 help_or_version(int argc, tchar **argv, int cmd)
4550 for (i = 1; i < argc; i++) {
4552 if (p[0] == T('-') && p[1] == T('-')) {
4554 if (!tstrcmp(p, T("help"))) {
4555 if (cmd == CMD_NONE)
4560 } else if (!tstrcmp(p, T("version"))) {
4569 print_usage_string(int cmd, FILE *fp)
4571 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4575 recommend_man_page(int cmd, FILE *fp)
4577 const tchar *format_str;
4579 format_str = T("Some uncommon options are not listed;\n"
4580 "See %"TS".pdf in the doc directory for more details.\n");
4582 format_str = T("Some uncommon options are not listed;\n"
4583 "Try `man %"TS"' for more details.\n");
4585 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4589 usage(int cmd, FILE *fp)
4591 tfprintf(fp, T("Usage:\n"));
4592 print_usage_string(cmd, fp);
4593 tfprintf(fp, T("\n"));
4594 recommend_man_page(cmd, fp);
4600 tfprintf(fp, T("Usage:\n"));
4601 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4602 print_usage_string(cmd, fp);
4603 tfprintf(fp, T("\n"));
4605 static const tchar *extra =
4608 " %"TS" --version\n"
4611 tfprintf(fp, extra, invocation_name, invocation_name);
4613 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4614 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4615 "For some commands IMAGE may be \"all\".\n"
4617 recommend_man_page(CMD_NONE, fp);
4620 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4621 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4622 * something else), while on Windows the command arguments will be UTF-16LE
4623 * encoded 'wchar_t' strings. */
4626 wmain(int argc, wchar_t **argv, wchar_t **envp)
4628 main(int argc, char **argv)
4635 imagex_info_file = stdout;
4636 invocation_name = tbasename(argv[0]);
4639 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4640 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4644 setlocale(LC_ALL, "");
4645 codeset = nl_langinfo(CODESET);
4646 if (!strstr(codeset, "UTF-8") &&
4647 !strstr(codeset, "UTF8") &&
4648 !strstr(codeset, "utf-8") &&
4649 !strstr(codeset, "utf8"))
4652 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4653 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4654 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4655 " to any value to force wimlib to use UTF-8.\n",
4661 #endif /* !__WIN32__ */
4664 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4665 if (igcase != NULL) {
4666 if (!tstrcmp(igcase, T("no")) ||
4667 !tstrcmp(igcase, T("0")))
4668 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4669 else if (!tstrcmp(igcase, T("yes")) ||
4670 !tstrcmp(igcase, T("1")))
4671 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4674 "WARNING: Ignoring unknown setting of "
4675 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4680 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4682 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4683 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4684 for (int i = 0; i < CMD_MAX; i++) {
4685 if (!tstrcmp(invocation_name + 3,
4686 imagex_commands[i].name))
4695 /* Unless already known from the invocation name, determine which
4696 * command was specified. */
4697 if (cmd == CMD_NONE) {
4699 imagex_error(T("No command specified!\n"));
4703 for (int i = 0; i < CMD_MAX; i++) {
4704 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4709 if (cmd != CMD_NONE) {
4715 /* Handle --help and --version. --help can be either for the program as
4716 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4717 * CMD_NONE). Note: help_or_version() will not return if a --help or
4718 * --version argument was found. */
4719 help_or_version(argc, argv, cmd);
4721 /* Bail if a valid command was not specified. */
4722 if (cmd == CMD_NONE) {
4723 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4728 /* Enable warning and error messages in wimlib to be more user-friendly.
4730 wimlib_set_print_errors(true);
4732 /* Initialize wimlib. */
4733 ret = wimlib_global_init(init_flags);
4735 goto out_check_status;
4737 /* Call the command handler function. */
4738 ret = imagex_commands[cmd].func(argc, argv, cmd);
4740 /* Check for error writing to standard output, especially since for some
4741 * commands, writing to standard output is part of the program's actual
4742 * behavior and not just for informational purposes. */
4743 if (ferror(stdout) || fclose(stdout)) {
4744 imagex_error_with_errno(T("error writing to standard output"));
4749 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4750 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4751 * error code from which an error message can be printed. */
4753 imagex_error(T("Exiting with error code %d:\n"
4755 wimlib_get_error_string(ret));
4756 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4757 imagex_error_with_errno(T("errno"));
4759 /* Make wimlib free any resources it's holding (although this is not
4760 * strictly necessary because the process is ending anyway). */
4761 wimlib_global_cleanup();