4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012, 2013, 2014 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,
146 IMAGEX_CHUNK_SIZE_OPTION,
147 IMAGEX_COMMAND_OPTION,
148 IMAGEX_COMMIT_OPTION,
149 IMAGEX_COMPRESS_OPTION,
150 IMAGEX_COMPRESS_SLOW_OPTION,
151 IMAGEX_CONFIG_OPTION,
153 IMAGEX_DELTA_FROM_OPTION,
154 IMAGEX_DEREFERENCE_OPTION,
155 IMAGEX_DEST_DIR_OPTION,
156 IMAGEX_DETAILED_OPTION,
157 IMAGEX_EXTRACT_XML_OPTION,
160 IMAGEX_HEADER_OPTION,
161 IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
163 IMAGEX_LOOKUP_TABLE_OPTION,
164 IMAGEX_METADATA_OPTION,
165 IMAGEX_NEW_IMAGE_OPTION,
166 IMAGEX_NOCHECK_OPTION,
167 IMAGEX_NORPFIX_OPTION,
168 IMAGEX_NOT_PIPABLE_OPTION,
169 IMAGEX_NO_ACLS_OPTION,
170 IMAGEX_NO_ATTRIBUTES_OPTION,
171 IMAGEX_NO_REPLACE_OPTION,
172 IMAGEX_NO_GLOBS_OPTION,
173 IMAGEX_NULLGLOB_OPTION,
174 IMAGEX_ONE_FILE_ONLY_OPTION,
176 IMAGEX_PIPABLE_OPTION,
177 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
178 IMAGEX_REBUILD_OPTION,
179 IMAGEX_RECOMPRESS_OPTION,
180 IMAGEX_RECURSIVE_OPTION,
182 IMAGEX_RESUME_OPTION,
186 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
187 IMAGEX_SOLID_COMPRESS_OPTION,
188 IMAGEX_SOURCE_LIST_OPTION,
189 IMAGEX_STAGING_DIR_OPTION,
190 IMAGEX_STREAMS_INTERFACE_OPTION,
191 IMAGEX_STRICT_ACLS_OPTION,
192 IMAGEX_THREADS_OPTION,
193 IMAGEX_TO_STDOUT_OPTION,
194 IMAGEX_UNIX_DATA_OPTION,
195 IMAGEX_UPDATE_OF_OPTION,
196 IMAGEX_VERBOSE_OPTION,
197 IMAGEX_WIMBOOT_OPTION,
198 IMAGEX_WIMBOOT_CONFIG_OPTION,
202 static const struct option apply_options[] = {
203 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
204 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
205 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
206 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
207 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
208 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
209 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
210 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
211 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
212 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
213 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
215 /* --resume is undocumented for now as it needs improvement. */
216 {T("resume"), no_argument, NULL, IMAGEX_RESUME_OPTION},
217 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
221 static const struct option capture_or_append_options[] = {
222 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
223 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
224 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
225 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
226 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
227 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
228 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
229 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
230 {T("pack-streams"), no_argument, NULL, IMAGEX_SOLID_OPTION},
231 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
232 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
233 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
234 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
235 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
236 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
237 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
238 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
239 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
240 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
241 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
242 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
243 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
244 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
245 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
246 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
247 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
248 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
249 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
250 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
251 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
252 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
256 static const struct option delete_options[] = {
257 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
258 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
262 static const struct option dir_options[] = {
263 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
264 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
265 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
269 static const struct option export_options[] = {
270 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
271 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
272 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
273 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
274 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
275 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
276 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
277 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
278 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
279 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
280 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
281 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
282 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
283 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
284 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
285 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
286 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
287 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
288 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
289 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
293 static const struct option extract_options[] = {
294 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
295 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
296 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
297 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
298 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
299 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
300 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
301 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
302 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
303 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
304 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
305 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
306 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
307 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
308 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
309 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
313 static const struct option info_options[] = {
314 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
315 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
316 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
317 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
318 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
319 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
320 {T("lookup-table"), no_argument, NULL, IMAGEX_LOOKUP_TABLE_OPTION},
321 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
322 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
326 static const struct option join_options[] = {
327 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
331 static const struct option mount_options[] = {
332 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
333 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
334 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
335 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
336 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
337 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
338 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
342 static const struct option optimize_options[] = {
343 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
344 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
345 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
346 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
347 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
348 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
349 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
350 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
351 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
352 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
353 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
354 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
355 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
356 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
357 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
358 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
359 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
363 static const struct option split_options[] = {
364 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
368 static const struct option unmount_options[] = {
369 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
370 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
371 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
372 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
373 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
374 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
378 static const struct option update_options[] = {
379 /* Careful: some of the options here set the defaults for update
380 * commands, but the flags given to an actual update command (and not to
381 * `imagex update' itself are also handled in
382 * update_command_add_option(). */
383 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
384 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
385 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
386 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
387 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
389 /* Default delete options */
390 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
391 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
393 /* Global add option */
394 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
396 /* Default add options */
397 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
398 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
399 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
400 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
401 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
402 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
403 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
408 static const struct option verify_options[] = {
409 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
415 # define _format_attribute(type, format_str, args_start) \
416 __attribute__((format(type, format_str, args_start)))
418 # define _format_attribute(type, format_str, args_start)
421 /* Print formatted error message to stderr. */
422 static void _format_attribute(printf, 1, 2)
423 imagex_error(const tchar *format, ...)
426 va_start(va, format);
427 tfputs(T("ERROR: "), stderr);
428 tvfprintf(stderr, format, va);
429 tputc(T('\n'), stderr);
433 /* Print formatted error message to stderr. */
434 static void _format_attribute(printf, 1, 2)
435 imagex_error_with_errno(const tchar *format, ...)
437 int errno_save = errno;
439 va_start(va, format);
440 tfputs(T("ERROR: "), stderr);
441 tvfprintf(stderr, format, va);
442 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
447 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
449 if (image == WIMLIB_NO_IMAGE) {
450 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
451 " Please specify a 1-based image index or "
452 "image name. To list the images\n"
453 " contained in the WIM archive, run\n"
455 " %"TS" \"%"TS"\"\n"),
456 image_name, wim_name,
457 get_cmd_string(CMD_INFO, false), wim_name);
458 return WIMLIB_ERR_INVALID_IMAGE;
464 verify_image_is_single(int image)
466 if (image == WIMLIB_ALL_IMAGES) {
467 imagex_error(T("Cannot specify all images for this action!"));
468 return WIMLIB_ERR_INVALID_IMAGE;
474 verify_image_exists_and_is_single(int image, const tchar *image_name,
475 const tchar *wim_name)
478 ret = verify_image_exists(image, image_name, wim_name);
480 ret = verify_image_is_single(image);
485 print_available_compression_types(FILE *fp)
487 static const tchar *s =
489 "Available compression types:\n"
492 " xpress (alias: \"fast\")\n"
493 " lzx (alias: \"maximum\") (default for capture)\n"
494 " lzms (alias: \"recovery\")\n"
500 /* Parse the argument to --compress */
502 get_compression_type(tchar *optarg)
505 unsigned int compression_level = 0;
508 plevel = tstrchr(optarg, T(':'));
514 ultmp = tstrtoul(plevel, &ptmp, 10);
515 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
516 imagex_error(T("Compression level must be a positive integer! "
517 "e.g. --compress=lzx:80"));
518 return WIMLIB_COMPRESSION_TYPE_INVALID;
520 compression_level = ultmp;
523 if (!tstrcasecmp(optarg, T("maximum")) ||
524 !tstrcasecmp(optarg, T("lzx")) ||
525 !tstrcasecmp(optarg, T("max")))
526 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
527 else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress")))
528 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
529 else if (!tstrcasecmp(optarg, T("recovery")) || !tstrcasecmp(optarg, T("lzms")))
530 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
531 else if (!tstrcasecmp(optarg, T("none")))
532 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
534 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
535 print_available_compression_types(stderr);
536 return WIMLIB_COMPRESSION_TYPE_INVALID;
539 if (compression_level != 0)
540 wimlib_set_default_compression_level(ctype, compression_level);
545 set_compress_slow(void)
548 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
549 " Use the '--compress=TYPE:LEVEL' option instead.\n");
551 wimlib_set_default_compression_level(-1, 100);
555 const tchar **strings;
556 unsigned num_strings;
557 unsigned num_alloc_strings;
560 #define STRING_SET_INITIALIZER \
561 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
563 #define STRING_SET(_strings) \
564 struct string_set _strings = STRING_SET_INITIALIZER
567 string_set_append(struct string_set *set, const tchar *glob)
569 unsigned num_alloc_strings = set->num_alloc_strings;
571 if (set->num_strings == num_alloc_strings) {
572 const tchar **new_strings;
574 num_alloc_strings += 4;
575 new_strings = realloc(set->strings,
576 sizeof(set->strings[0]) * num_alloc_strings);
578 imagex_error(T("Out of memory!"));
581 set->strings = new_strings;
582 set->num_alloc_strings = num_alloc_strings;
584 set->strings[set->num_strings++] = glob;
589 string_set_destroy(struct string_set *set)
595 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
597 return wimlib_reference_resource_files(wim, set->strings,
599 WIMLIB_REF_FLAG_GLOB_ENABLE,
604 do_resource_not_found_warning(const tchar *wimfile,
605 const struct wimlib_wim_info *info,
606 const struct string_set *refglobs)
608 if (info->total_parts > 1) {
609 if (refglobs->num_strings == 0) {
610 imagex_error(T("\"%"TS"\" is part of a split WIM. "
611 "Use --ref to specify the other parts."),
614 imagex_error(T("Perhaps the '--ref' argument did not "
615 "specify all other parts of the split "
619 imagex_error(T("If this is a delta WIM, use the --ref argument "
620 "to specify the WIM(s) on which it is based."));
624 /* Returns the size of a file given its name, or -1 if the file does not exist
625 * or its size cannot be determined. */
627 file_get_size(const tchar *filename)
630 if (tstat(filename, &st) == 0)
637 PARSE_STRING_SUCCESS = 0,
638 PARSE_STRING_FAILURE = 1,
639 PARSE_STRING_NONE = 2,
643 * Parses a string token from an array of characters.
645 * Tokens are either whitespace-delimited, or double or single-quoted.
647 * @line_p: Pointer to the pointer to the line of data. Will be updated
648 * to point past the string token iff the return value is
649 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
652 * @len_p: @len_p initially stores the length of the line of data, which may
653 * be 0, and it will be updated to the number of bytes remaining in
654 * the line iff the return value is PARSE_STRING_SUCCESS.
656 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
657 * parsed string token will be returned here.
659 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
660 * PARSE_STRING_FAILURE if the data was invalid due to a missing
661 * closing quote; or PARSE_STRING_NONE if the line ended before the
662 * beginning of a string token was found.
665 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
668 tchar *line = *line_p;
672 /* Skip leading whitespace */
675 return PARSE_STRING_NONE;
676 if (!istspace(*line) && *line != T('\0'))
682 if (quote_char == T('"') || quote_char == T('\'')) {
687 line = tmemchr(line, quote_char, len);
689 imagex_error(T("Missing closing quote: %"TS), fn - 1);
690 return PARSE_STRING_FAILURE;
693 /* Unquoted string. Go until whitespace. Line is terminated
694 * by '\0', so no need to check 'len'. */
698 } while (!istspace(*line) && *line != T('\0'));
705 return PARSE_STRING_SUCCESS;
708 /* Parses a line of data (not an empty line or comment) in the source list file
709 * format. (See the man page for 'wimlib-imagex capture' for details on this
710 * format and the meaning.)
712 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
713 * len == 0. The data in @line will be modified by this function call.
715 * @len: Length of the line of data.
717 * @source: On success, the capture source and target described by the line is
718 * written into this destination. Note that it will contain pointers
719 * to data in the @line array.
721 * Returns true if the line was valid; false otherwise. */
723 parse_source_list_line(tchar *line, size_t len,
724 struct wimlib_capture_source *source)
728 ret = parse_string(&line, &len, &source->fs_source_path);
729 if (ret != PARSE_STRING_SUCCESS)
731 ret = parse_string(&line, &len, &source->wim_target_path);
732 if (ret == PARSE_STRING_NONE)
733 source->wim_target_path = source->fs_source_path;
734 return ret != PARSE_STRING_FAILURE;
737 /* Returns %true if the given line of length @len > 0 is a comment or empty line
738 * in the source list file format. */
740 is_comment_line(const tchar *line, size_t len)
743 if (*line == T('#') || *line == T(';'))
745 if (!istspace(*line) && *line != T('\0'))
755 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
758 tchar *contents = *contents_p;
759 size_t nchars = *nchars_p;
762 for (i = 0; i < nchars; i++)
763 if (contents[i] == T('\n'))
766 /* Handle last line not terminated by a newline */
767 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
768 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
770 imagex_error(T("Out of memory!"));
773 contents[nchars] = T('\n');
774 *contents_p = contents;
782 /* Parses a file in the source list format. (See the man page for
783 * 'wimlib-imagex capture' for details on this format and the meaning.)
785 * @source_list_contents: Contents of the source list file. Note that this
786 * buffer will be modified to save memory allocations,
787 * and cannot be freed until the returned array of
788 * wimlib_capture_source's has also been freed.
790 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
793 * @nsources_ret: On success, the length of the returned array is
796 * Returns: An array of `struct wimlib_capture_source's that can be passed to
797 * the wimlib_add_image_multisource() function to specify how a WIM image is to
799 static struct wimlib_capture_source *
800 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
801 size_t *nsources_ret)
805 struct wimlib_capture_source *sources;
808 nlines = text_file_count_lines(source_list_contents_p,
809 &source_list_nchars);
813 /* Always allocate at least 1 slot, just in case the implementation of
814 * calloc() returns NULL if 0 bytes are requested. */
815 sources = calloc(nlines ?: 1, sizeof(*sources));
817 imagex_error(T("out of memory"));
820 p = *source_list_contents_p;
822 for (i = 0; i < nlines; i++) {
823 /* XXX: Could use rawmemchr() here instead, but it may not be
824 * available on all platforms. */
825 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
826 size_t len = endp - p + 1;
828 if (!is_comment_line(p, len)) {
829 if (!parse_source_list_line(p, len, &sources[j++])) {
841 /* Reads the contents of a file into memory. */
843 file_get_contents(const tchar *filename, size_t *len_ret)
850 if (tstat(filename, &stbuf) != 0) {
851 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
856 fp = tfopen(filename, T("rb"));
858 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
862 buf = malloc(len ? len : 1);
864 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
865 "contents of file \"%"TS"\""), len, filename);
868 if (fread(buf, 1, len, fp) != len) {
869 imagex_error_with_errno(T("Failed to read %zu bytes from the "
870 "file \"%"TS"\""), len, filename);
884 /* Read standard input until EOF and return the full contents in a malloc()ed
885 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
888 stdin_get_contents(size_t *len_ret)
890 /* stdin can, of course, be a pipe or other non-seekable file, so the
891 * total length of the data cannot be pre-determined */
893 size_t newlen = 1024;
897 char *p = realloc(buf, newlen);
898 size_t bytes_read, bytes_to_read;
900 imagex_error(T("out of memory while reading stdin"));
904 bytes_to_read = newlen - pos;
905 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
907 if (bytes_read != bytes_to_read) {
912 imagex_error_with_errno(T("error reading stdin"));
926 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
929 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
931 *num_tchars_ret = num_bytes;
933 #else /* !__WIN32__ */
934 /* On Windows, translate the text to UTF-16LE */
938 if (num_bytes >= 2 &&
939 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
940 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
942 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
943 * with something that looks like an ASCII character encoded as
944 * a UTF-16LE code unit. Assume the file is encoded as
945 * UTF-16LE. This is not a 100% reliable check. */
946 num_wchars = num_bytes / 2;
947 text_wstr = (wchar_t*)text;
949 /* File does not look like UTF-16LE. Assume it is encoded in
950 * the current Windows code page. I think these are always
951 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
952 * should work as expected. */
953 text_wstr = win32_mbs_to_wcs(text,
958 *num_tchars_ret = num_wchars;
960 #endif /* __WIN32__ */
964 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
969 contents = file_get_contents(filename, &num_bytes);
972 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
976 stdin_get_text_contents(size_t *num_tchars_ret)
981 contents = stdin_get_contents(&num_bytes);
984 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
987 #define TO_PERCENT(numerator, denominator) \
988 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
990 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
991 #define MEBIBYTE_MIN_NBYTES 10000000ULL
992 #define KIBIBYTE_MIN_NBYTES 10000ULL
995 get_unit(uint64_t total_bytes, const tchar **name_ret)
997 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
998 *name_ret = T("GiB");
1000 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1001 *name_ret = T("MiB");
1003 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1004 *name_ret = T("KiB");
1007 *name_ret = T("bytes");
1012 static struct wimlib_progress_info_scan last_scan_progress;
1015 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1017 uint64_t prev_count, cur_count;
1019 prev_count = last_scan_progress.num_nondirs_scanned +
1020 last_scan_progress.num_dirs_scanned;
1021 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1023 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1024 cur_count % 128 == 0)
1026 unsigned unit_shift;
1027 const tchar *unit_name;
1029 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1030 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1031 "%"PRIu64" directories) "),
1032 scan->num_bytes_scanned >> unit_shift,
1034 scan->num_nondirs_scanned,
1035 scan->num_dirs_scanned);
1036 last_scan_progress = *scan;
1039 /* Progress callback function passed to various wimlib functions. */
1040 static enum wimlib_progress_status
1041 imagex_progress_func(enum wimlib_progress_msg msg,
1042 union wimlib_progress_info *info,
1043 void *_ignored_context)
1045 unsigned percent_done;
1046 unsigned unit_shift;
1047 const tchar *unit_name;
1049 if (imagex_be_quiet)
1050 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1052 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1054 static bool first = true;
1056 imagex_printf(T("Writing %"TS"-compressed data "
1057 "using %u thread%"TS"\n"),
1058 wimlib_get_compression_type_string(
1059 info->write_streams.compression_type),
1060 info->write_streams.num_threads,
1061 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1065 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1066 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1067 info->write_streams.total_bytes);
1069 imagex_printf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
1070 "written (%u%% done)"),
1071 info->write_streams.completed_bytes >> unit_shift,
1073 info->write_streams.total_bytes >> unit_shift,
1076 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1077 imagex_printf(T("\n"));
1079 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1080 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1081 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1082 imagex_printf(T("\n"));
1084 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1085 info->scan.wim_target_path);
1087 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1089 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1090 switch (info->scan.status) {
1091 case WIMLIB_SCAN_DENTRY_OK:
1092 report_scan_progress(&info->scan, false);
1094 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1095 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1097 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1098 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1099 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1101 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1102 /* Symlink fixups are enabled by default. This is
1103 * mainly intended for Windows, which for some reason
1104 * uses absolute junctions (with drive letters!) in the
1105 * default installation. On UNIX-like systems, warn the
1106 * user when fixing the target of an absolute symbolic
1107 * link, so they know to disable this if they want. */
1109 imagex_printf(T("\nWARNING: Adjusted target of "
1110 "absolute symbolic link \"%"TS"\"\n"
1111 " (Use --norpfix to capture "
1112 "absolute symbolic links as-is)\n"),
1113 info->scan.cur_path);
1120 case WIMLIB_PROGRESS_MSG_SCAN_END:
1121 report_scan_progress(&info->scan, true);
1122 imagex_printf(T("\n"));
1124 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1125 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1126 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1127 info->integrity.total_bytes);
1128 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1129 "of %"PRIu64" %"TS" (%u%%) done"),
1130 info->integrity.filename,
1131 info->integrity.completed_bytes >> unit_shift,
1133 info->integrity.total_bytes >> unit_shift,
1136 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1137 imagex_printf(T("\n"));
1139 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1140 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1141 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1142 info->integrity.total_bytes);
1143 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1144 "of %"PRIu64" %"TS" (%u%%) done"),
1145 info->integrity.completed_bytes >> unit_shift,
1147 info->integrity.total_bytes >> unit_shift,
1150 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1151 imagex_printf(T("\n"));
1153 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1154 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1155 "to %"TS" \"%"TS"\"\n"),
1156 info->extract.image,
1157 info->extract.image_name,
1158 info->extract.wimfile_name,
1159 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1160 T("NTFS volume") : T("directory")),
1161 info->extract.target);
1163 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1164 percent_done = TO_PERCENT(info->extract.completed_bytes,
1165 info->extract.total_bytes);
1166 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1167 imagex_printf(T("\rExtracting files: "
1168 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1169 info->extract.completed_bytes >> unit_shift,
1171 info->extract.total_bytes >> unit_shift,
1174 if (info->extract.completed_bytes >= info->extract.total_bytes)
1175 imagex_printf(T("\n"));
1177 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1178 if (info->extract.total_parts != 1) {
1179 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1180 info->extract.part_number,
1181 info->extract.total_parts);
1184 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1185 percent_done = TO_PERCENT(info->split.completed_bytes,
1186 info->split.total_bytes);
1187 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1188 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1189 "%"PRIu64" %"TS" (%u%%) written\n"),
1190 info->split.part_name,
1191 info->split.cur_part_number,
1192 info->split.total_parts,
1193 info->split.completed_bytes >> unit_shift,
1195 info->split.total_bytes >> unit_shift,
1199 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1200 if (info->split.completed_bytes == info->split.total_bytes) {
1201 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1202 info->split.cur_part_number,
1203 info->split.total_parts);
1206 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1207 switch (info->update.command->op) {
1208 case WIMLIB_UPDATE_OP_DELETE:
1209 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1210 info->update.command->delete_.wim_path);
1212 case WIMLIB_UPDATE_OP_RENAME:
1213 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1214 info->update.command->rename.wim_source_path,
1215 info->update.command->rename.wim_target_path);
1217 case WIMLIB_UPDATE_OP_ADD:
1222 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1223 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1224 info->replace.path_in_wim);
1226 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1227 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1228 info->wimboot_exclude.path_in_wim);
1230 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1231 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1232 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1233 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1234 info->unmount.mounted_wim,
1235 info->unmount.mounted_image);
1237 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1238 info->unmount.mounted_wim,
1239 info->unmount.mounted_image);
1240 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1244 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1245 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1246 info->verify_image.current_image,
1247 info->verify_image.total_images);
1249 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1250 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1251 info->verify_streams.total_bytes);
1252 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1253 imagex_printf(T("\rVerifying streams: "
1254 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1255 info->verify_streams.completed_bytes >> unit_shift,
1257 info->verify_streams.total_bytes >> unit_shift,
1260 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1261 imagex_printf(T("\n"));
1266 fflush(imagex_info_file);
1267 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1271 parse_num_threads(const tchar *optarg)
1274 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1275 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1276 imagex_error(T("Number of threads must be a non-negative integer!"));
1283 static uint32_t parse_chunk_size(const tchar *optarg)
1286 unsigned long chunk_size = tstrtoul(optarg, &tmp, 10);
1287 if (chunk_size >= UINT32_MAX || *tmp || tmp == optarg) {
1288 imagex_error(T("Chunk size must be a non-negative integer!"));
1297 * Parse an option passed to an update command.
1299 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1302 * @option: Text string for the option (beginning with --)
1304 * @cmd: `struct wimlib_update_command' that is being constructed for
1307 * Returns true if the option was recognized; false if not.
1310 update_command_add_option(int op, const tchar *option,
1311 struct wimlib_update_command *cmd)
1313 bool recognized = true;
1315 case WIMLIB_UPDATE_OP_ADD:
1316 if (!tstrcmp(option, T("--verbose")))
1317 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1318 else if (!tstrcmp(option, T("--unix-data")))
1319 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1320 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1321 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1322 else if (!tstrcmp(option, T("--strict-acls")))
1323 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1324 else if (!tstrcmp(option, T("--dereference")))
1325 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1326 else if (!tstrcmp(option, T("--no-replace")))
1327 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1331 case WIMLIB_UPDATE_OP_DELETE:
1332 if (!tstrcmp(option, T("--force")))
1333 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1334 else if (!tstrcmp(option, T("--recursive")))
1335 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1346 /* How many nonoption arguments each `imagex update' command expects */
1347 static const unsigned update_command_num_nonoptions[] = {
1348 [WIMLIB_UPDATE_OP_ADD] = 2,
1349 [WIMLIB_UPDATE_OP_DELETE] = 1,
1350 [WIMLIB_UPDATE_OP_RENAME] = 2,
1354 update_command_add_nonoption(int op, const tchar *nonoption,
1355 struct wimlib_update_command *cmd,
1356 unsigned num_nonoptions)
1359 case WIMLIB_UPDATE_OP_ADD:
1360 if (num_nonoptions == 0)
1361 cmd->add.fs_source_path = (tchar*)nonoption;
1363 cmd->add.wim_target_path = (tchar*)nonoption;
1365 case WIMLIB_UPDATE_OP_DELETE:
1366 cmd->delete_.wim_path = (tchar*)nonoption;
1368 case WIMLIB_UPDATE_OP_RENAME:
1369 if (num_nonoptions == 0)
1370 cmd->rename.wim_source_path = (tchar*)nonoption;
1372 cmd->rename.wim_target_path = (tchar*)nonoption;
1378 * Parse a command passed on stdin to `imagex update'.
1380 * @line: Text of the command.
1381 * @len: Length of the line, including a null terminator
1384 * @command: A `struct wimlib_update_command' to fill in from the parsed
1387 * @line_number: Line number of the command, for diagnostics.
1389 * Returns true on success; returns false on parse error.
1392 parse_update_command(tchar *line, size_t len,
1393 struct wimlib_update_command *command,
1397 tchar *command_name;
1399 size_t num_nonoptions;
1401 /* Get the command name ("add", "delete", "rename") */
1402 ret = parse_string(&line, &len, &command_name);
1403 if (ret != PARSE_STRING_SUCCESS)
1406 if (!tstrcasecmp(command_name, T("add"))) {
1407 op = WIMLIB_UPDATE_OP_ADD;
1408 } else if (!tstrcasecmp(command_name, T("delete"))) {
1409 op = WIMLIB_UPDATE_OP_DELETE;
1410 } else if (!tstrcasecmp(command_name, T("rename"))) {
1411 op = WIMLIB_UPDATE_OP_RENAME;
1413 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1414 command_name, line_number);
1419 /* Parse additional options and non-options as needed */
1424 ret = parse_string(&line, &len, &next_string);
1425 if (ret == PARSE_STRING_NONE) /* End of line */
1427 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1429 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1431 if (!update_command_add_option(op, next_string, command))
1433 imagex_error(T("Unrecognized option \"%"TS"\" to "
1434 "update command \"%"TS"\" on line %zu"),
1435 next_string, command_name, line_number);
1441 if (num_nonoptions == update_command_num_nonoptions[op])
1443 imagex_error(T("Unexpected argument \"%"TS"\" in "
1444 "update command on line %zu\n"
1445 " (The \"%"TS"\" command only "
1446 "takes %zu nonoption arguments!)\n"),
1447 next_string, line_number,
1448 command_name, num_nonoptions);
1451 update_command_add_nonoption(op, next_string,
1452 command, num_nonoptions);
1457 if (num_nonoptions != update_command_num_nonoptions[op]) {
1458 imagex_error(T("Not enough arguments to update command "
1459 "\"%"TS"\" on line %zu"), command_name, line_number);
1465 static struct wimlib_update_command *
1466 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1467 size_t *num_cmds_ret)
1471 struct wimlib_update_command *cmds;
1474 nlines = text_file_count_lines(cmd_file_contents_p,
1479 /* Always allocate at least 1 slot, just in case the implementation of
1480 * calloc() returns NULL if 0 bytes are requested. */
1481 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1483 imagex_error(T("out of memory"));
1486 p = *cmd_file_contents_p;
1488 for (i = 0; i < nlines; i++) {
1489 /* XXX: Could use rawmemchr() here instead, but it may not be
1490 * available on all platforms. */
1491 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1492 size_t len = endp - p + 1;
1494 if (!is_comment_line(p, len)) {
1495 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1506 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1507 * one image from a WIM file to an NTFS volume. */
1509 imagex_apply(int argc, tchar **argv, int cmd)
1513 int image = WIMLIB_NO_IMAGE;
1515 struct wimlib_wim_info info;
1517 const tchar *wimfile;
1518 const tchar *target;
1519 const tchar *image_num_or_name = NULL;
1520 int extract_flags = 0;
1522 STRING_SET(refglobs);
1524 for_opt(c, apply_options) {
1526 case IMAGEX_CHECK_OPTION:
1527 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1529 case IMAGEX_VERBOSE_OPTION:
1530 /* No longer does anything. */
1532 case IMAGEX_REF_OPTION:
1533 ret = string_set_append(&refglobs, optarg);
1535 goto out_free_refglobs;
1537 case IMAGEX_UNIX_DATA_OPTION:
1538 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1540 case IMAGEX_NO_ACLS_OPTION:
1541 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1543 case IMAGEX_STRICT_ACLS_OPTION:
1544 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1546 case IMAGEX_NO_ATTRIBUTES_OPTION:
1547 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1549 case IMAGEX_NORPFIX_OPTION:
1550 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1552 case IMAGEX_RPFIX_OPTION:
1553 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1555 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1556 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1557 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1559 case IMAGEX_RESUME_OPTION:
1560 extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME;
1562 case IMAGEX_WIMBOOT_OPTION:
1563 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1571 if (argc != 2 && argc != 3)
1576 if (!tstrcmp(wimfile, T("-"))) {
1577 /* Attempt to apply pipable WIM from standard input. */
1579 image_num_or_name = NULL;
1582 image_num_or_name = argv[1];
1587 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1588 imagex_progress_func, NULL);
1590 goto out_free_refglobs;
1592 wimlib_get_wim_info(wim, &info);
1595 /* Image explicitly specified. */
1596 image_num_or_name = argv[1];
1597 image = wimlib_resolve_image(wim, image_num_or_name);
1598 ret = verify_image_exists(image, image_num_or_name, wimfile);
1600 goto out_wimlib_free;
1603 /* No image specified; default to image 1, but only if the WIM
1604 * contains exactly one image. */
1606 if (info.image_count != 1) {
1607 imagex_error(T("\"%"TS"\" contains %d images; "
1608 "Please select one (or all)."),
1609 wimfile, info.image_count);
1618 if (refglobs.num_strings) {
1620 imagex_error(T("Can't specify --ref when applying from stdin!"));
1622 goto out_wimlib_free;
1624 ret = wim_reference_globs(wim, &refglobs, open_flags);
1626 goto out_wimlib_free;
1631 /* Interpret a regular file or block device target as an NTFS
1635 if (tstat(target, &stbuf)) {
1636 if (errno != ENOENT) {
1637 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1640 goto out_wimlib_free;
1643 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1644 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1650 ret = wimlib_extract_image(wim, image, target, extract_flags);
1652 set_fd_to_binary_mode(STDIN_FILENO);
1653 ret = wimlib_extract_image_from_pipe_with_progress(
1658 imagex_progress_func,
1662 imagex_printf(T("Done applying WIM image.\n"));
1663 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1665 do_resource_not_found_warning(wimfile, &info, &refglobs);
1667 imagex_error(T( "If you are applying an image "
1668 "from a split pipable WIM,\n"
1669 " make sure you have "
1670 "concatenated together all parts."));
1676 string_set_destroy(&refglobs);
1680 usage(CMD_APPLY, stderr);
1682 goto out_free_refglobs;
1685 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1686 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1687 * the desired image. 'wimlib-imagex append': add a new image to an existing
1690 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1693 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
1694 int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE |
1695 WIMLIB_ADD_IMAGE_FLAG_WINCONFIG |
1696 WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
1697 int write_flags = 0;
1698 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1699 uint32_t chunk_size = UINT32_MAX;
1700 uint32_t solid_chunk_size = UINT32_MAX;
1701 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1702 const tchar *wimfile;
1706 const tchar *flags_element = NULL;
1709 STRING_SET(base_wimfiles);
1710 WIMStruct **base_wims;
1712 WIMStruct *template_wim;
1713 const tchar *template_wimfile = NULL;
1714 const tchar *template_image_name_or_num = NULL;
1715 int template_image = WIMLIB_NO_IMAGE;
1718 unsigned num_threads = 0;
1723 tchar *config_file = NULL;
1725 bool source_list = false;
1726 size_t source_list_nchars = 0;
1727 tchar *source_list_contents;
1728 bool capture_sources_malloced;
1729 struct wimlib_capture_source *capture_sources;
1731 bool name_defaulted;
1733 for_opt(c, capture_or_append_options) {
1735 case IMAGEX_BOOT_OPTION:
1736 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
1738 case IMAGEX_CHECK_OPTION:
1739 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1740 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1742 case IMAGEX_NOCHECK_OPTION:
1743 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1745 case IMAGEX_CONFIG_OPTION:
1746 config_file = optarg;
1747 add_image_flags &= ~WIMLIB_ADD_IMAGE_FLAG_WINCONFIG;
1749 case IMAGEX_COMPRESS_OPTION:
1750 compression_type = get_compression_type(optarg);
1751 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1754 case IMAGEX_COMPRESS_SLOW_OPTION:
1755 set_compress_slow();
1757 case IMAGEX_CHUNK_SIZE_OPTION:
1758 chunk_size = parse_chunk_size(optarg);
1759 if (chunk_size == UINT32_MAX)
1762 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1763 solid_chunk_size = parse_chunk_size(optarg);
1764 if (solid_chunk_size == UINT32_MAX)
1767 case IMAGEX_SOLID_COMPRESS_OPTION:
1768 solid_ctype = get_compression_type(optarg);
1769 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1772 case IMAGEX_SOLID_OPTION:
1773 write_flags |= WIMLIB_WRITE_FLAG_PACK_STREAMS;
1775 case IMAGEX_FLAGS_OPTION:
1776 flags_element = optarg;
1778 case IMAGEX_DEREFERENCE_OPTION:
1779 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
1781 case IMAGEX_VERBOSE_OPTION:
1782 /* No longer does anything. */
1784 case IMAGEX_THREADS_OPTION:
1785 num_threads = parse_num_threads(optarg);
1786 if (num_threads == UINT_MAX)
1789 case IMAGEX_REBUILD_OPTION:
1790 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1792 case IMAGEX_UNIX_DATA_OPTION:
1793 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
1795 case IMAGEX_SOURCE_LIST_OPTION:
1798 case IMAGEX_NO_ACLS_OPTION:
1799 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS;
1801 case IMAGEX_STRICT_ACLS_OPTION:
1802 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS;
1804 case IMAGEX_RPFIX_OPTION:
1805 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_RPFIX;
1807 case IMAGEX_NORPFIX_OPTION:
1808 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NORPFIX;
1810 case IMAGEX_PIPABLE_OPTION:
1811 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1813 case IMAGEX_NOT_PIPABLE_OPTION:
1814 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1816 case IMAGEX_UPDATE_OF_OPTION:
1817 if (template_image_name_or_num) {
1818 imagex_error(T("'--update-of' can only be "
1819 "specified one time!"));
1823 colon = tstrrchr(optarg, T(':'));
1826 template_wimfile = optarg;
1828 template_image_name_or_num = colon + 1;
1830 template_wimfile = NULL;
1831 template_image_name_or_num = optarg;
1835 case IMAGEX_DELTA_FROM_OPTION:
1836 if (cmd != CMD_CAPTURE) {
1837 imagex_error(T("'--delta-from' is only "
1838 "valid for capture!"));
1841 ret = string_set_append(&base_wimfiles, optarg);
1843 goto out_free_base_wimfiles;
1844 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
1846 case IMAGEX_WIMBOOT_OPTION:
1847 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_WIMBOOT;
1856 if (argc < 2 || argc > 4)
1862 /* Set default compression type and parameters. */
1865 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
1866 /* No compression type specified. Use the default. */
1868 if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_WIMBOOT) {
1869 /* With --wimboot, default to XPRESS compression. */
1870 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
1871 } else if (write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS) {
1872 /* With --solid, default to LZMS compression. (However,
1873 * this will not affect solid blocks!) */
1874 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
1876 /* Otherwise, default to LZX compression. */
1877 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
1881 if (!tstrcmp(wimfile, T("-"))) {
1882 /* Writing captured WIM to standard output. */
1884 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
1885 imagex_error("Can't write a non-pipable WIM to "
1886 "standard output! Specify --pipable\n"
1887 " if you want to create a pipable WIM "
1888 "(but read the docs first).");
1892 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1894 if (cmd == CMD_APPEND) {
1895 imagex_error(T("Using standard output for append does "
1896 "not make sense."));
1899 wim_fd = STDOUT_FILENO;
1901 imagex_info_file = stderr;
1902 set_fd_to_binary_mode(wim_fd);
1905 /* If template image was specified using --update-of=IMAGE rather
1906 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
1907 if (template_image_name_or_num && !template_wimfile) {
1908 if (base_wimfiles.num_strings == 1) {
1909 /* Capturing delta WIM based on single WIM: default to
1911 template_wimfile = base_wimfiles.strings[0];
1912 } else if (cmd == CMD_APPEND) {
1913 /* Appending to WIM: default to WIM being appended to.
1915 template_wimfile = wimfile;
1917 /* Capturing a normal (non-delta) WIM, so the WIM file
1918 * *must* be explicitly specified. */
1919 if (base_wimfiles.num_strings > 1) {
1920 imagex_error(T("For capture of delta WIM "
1921 "based on multiple existing "
1923 " '--update-of' must "
1924 "specify WIMFILE:IMAGE!"));
1926 imagex_error(T("For capture of non-delta WIM, "
1927 "'--update-of' must specify "
1936 name_defaulted = false;
1938 /* Set default name to SOURCE argument, omitting any directory
1939 * prefixes and trailing slashes. This requires making a copy
1940 * of @source. Leave some free characters at the end in case we
1941 * append a number to keep the name unique. */
1942 size_t source_name_len;
1944 source_name_len = tstrlen(source);
1945 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
1946 name = tbasename(tstrcpy(source_copy, source));
1947 name_defaulted = true;
1949 /* Image description defaults to NULL if not given. */
1956 /* Set up capture sources in source list mode */
1957 if (source[0] == T('-') && source[1] == T('\0')) {
1958 source_list_contents = stdin_get_text_contents(&source_list_nchars);
1960 source_list_contents = file_get_text_contents(source,
1961 &source_list_nchars);
1963 if (!source_list_contents)
1966 capture_sources = parse_source_list(&source_list_contents,
1969 if (!capture_sources) {
1971 goto out_free_source_list_contents;
1973 capture_sources_malloced = true;
1975 /* Set up capture source in non-source-list mode. */
1976 capture_sources = alloca(sizeof(struct wimlib_capture_source));
1977 capture_sources[0].fs_source_path = source;
1978 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
1979 capture_sources[0].reserved = 0;
1981 capture_sources_malloced = false;
1982 source_list_contents = NULL;
1985 /* Open the existing WIM, or create a new one. */
1986 if (cmd == CMD_APPEND) {
1987 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1988 imagex_progress_func, NULL);
1990 goto out_free_capture_sources;
1992 ret = wimlib_create_new_wim(compression_type, &wim);
1994 goto out_free_capture_sources;
1995 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
1998 /* Set chunk size if non-default. */
1999 if (chunk_size != UINT32_MAX) {
2000 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2003 } else if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_WIMBOOT) &&
2004 compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2005 ret = wimlib_set_output_chunk_size(wim, 4096);
2009 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2010 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2014 if (solid_chunk_size != UINT32_MAX) {
2015 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2021 /* Detect if source is regular file or block device and set NTFS volume
2026 if (tstat(source, &stbuf) == 0) {
2027 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2028 imagex_printf(T("Capturing WIM image from NTFS "
2029 "filesystem on \"%"TS"\"\n"), source);
2030 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
2033 if (errno != ENOENT) {
2034 imagex_error_with_errno(T("Failed to stat "
2035 "\"%"TS"\""), source);
2043 /* If the user did not specify an image name, and the basename of the
2044 * source already exists as an image name in the WIM file, append a
2045 * suffix to make it unique. */
2046 if (cmd == CMD_APPEND && name_defaulted) {
2047 unsigned long conflict_idx;
2048 tchar *name_end = tstrchr(name, T('\0'));
2049 for (conflict_idx = 1;
2050 wimlib_image_name_in_use(wim, name);
2053 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2057 /* If capturing a delta WIM, reference resources from the base WIMs
2058 * before adding the new image. */
2059 if (base_wimfiles.num_strings) {
2060 base_wims = calloc(base_wimfiles.num_strings,
2061 sizeof(base_wims[0]));
2062 if (base_wims == NULL) {
2063 imagex_error(T("Out of memory!"));
2068 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2069 ret = wimlib_open_wim_with_progress(
2070 base_wimfiles.strings[i],
2071 open_flags, &base_wims[i],
2072 imagex_progress_func, NULL);
2074 goto out_free_base_wims;
2078 ret = wimlib_reference_resources(wim, base_wims,
2079 base_wimfiles.num_strings, 0);
2081 goto out_free_base_wims;
2083 if (base_wimfiles.num_strings == 1) {
2084 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2085 base_wimfiles.strings[0]);
2087 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2088 base_wimfiles.num_strings);
2095 /* If capturing or appending as an update of an existing (template) image,
2096 * open the WIM if needed and parse the image index. */
2097 if (template_image_name_or_num) {
2100 if (base_wimfiles.num_strings == 1 &&
2101 template_wimfile == base_wimfiles.strings[0]) {
2102 template_wim = base_wims[0];
2103 } else if (template_wimfile == wimfile) {
2106 ret = wimlib_open_wim_with_progress(template_wimfile,
2109 imagex_progress_func,
2112 goto out_free_base_wims;
2115 template_image = wimlib_resolve_image(template_wim,
2116 template_image_name_or_num);
2118 if (template_image_name_or_num[0] == T('-')) {
2121 struct wimlib_wim_info info;
2123 wimlib_get_wim_info(template_wim, &info);
2124 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2125 if (n >= 1 && n <= info.image_count &&
2127 tmp != template_image_name_or_num + 1)
2129 template_image = info.image_count - (n - 1);
2132 ret = verify_image_exists_and_is_single(template_image,
2133 template_image_name_or_num,
2136 goto out_free_template_wim;
2138 template_wim = NULL;
2141 ret = wimlib_add_image_multisource(wim,
2148 goto out_free_template_wim;
2150 if (desc || flags_element || template_image_name_or_num) {
2151 /* User provided <DESCRIPTION> or <FLAGS> element, or an image
2152 * on which the added one is to be based has been specified with
2153 * --update-of. Get the index of the image we just
2154 * added, then use it to call the appropriate functions. */
2155 struct wimlib_wim_info info;
2157 wimlib_get_wim_info(wim, &info);
2160 ret = wimlib_set_image_descripton(wim,
2164 goto out_free_template_wim;
2167 if (flags_element) {
2168 ret = wimlib_set_image_flags(wim, info.image_count,
2171 goto out_free_template_wim;
2174 /* Reference template image if the user provided one. */
2175 if (template_image_name_or_num) {
2176 imagex_printf(T("Using image %d "
2177 "from \"%"TS"\" as template\n"),
2178 template_image, template_wimfile);
2179 ret = wimlib_reference_template_image(wim,
2185 goto out_free_template_wim;
2189 /* Write the new WIM or overwrite the existing WIM with the new image
2191 if (cmd == CMD_APPEND) {
2192 ret = wimlib_overwrite(wim, write_flags, num_threads);
2193 } else if (wimfile) {
2194 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2195 write_flags, num_threads);
2197 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2198 write_flags, num_threads);
2200 out_free_template_wim:
2201 /* template_wim may alias base_wims[0] or wim. */
2202 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2203 template_wim != wim)
2204 wimlib_free(template_wim);
2206 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2207 wimlib_free(base_wims[i]);
2211 out_free_capture_sources:
2212 if (capture_sources_malloced)
2213 free(capture_sources);
2214 out_free_source_list_contents:
2215 free(source_list_contents);
2216 out_free_base_wimfiles:
2217 string_set_destroy(&base_wimfiles);
2224 goto out_free_base_wimfiles;
2227 /* Remove image(s) from a WIM. */
2229 imagex_delete(int argc, tchar **argv, int cmd)
2232 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2233 int write_flags = 0;
2234 const tchar *wimfile;
2235 const tchar *image_num_or_name;
2240 for_opt(c, delete_options) {
2242 case IMAGEX_CHECK_OPTION:
2243 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2244 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2246 case IMAGEX_SOFT_OPTION:
2247 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2258 imagex_error(T("Must specify a WIM file"));
2260 imagex_error(T("Must specify an image"));
2264 image_num_or_name = argv[1];
2266 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2267 imagex_progress_func, NULL);
2271 image = wimlib_resolve_image(wim, image_num_or_name);
2273 ret = verify_image_exists(image, image_num_or_name, wimfile);
2275 goto out_wimlib_free;
2277 ret = wimlib_delete_image(wim, image);
2279 imagex_error(T("Failed to delete image from \"%"TS"\""),
2281 goto out_wimlib_free;
2284 ret = wimlib_overwrite(wim, write_flags, 0);
2286 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2287 "deleted"), wimfile);
2295 usage(CMD_DELETE, stderr);
2300 struct print_dentry_options {
2305 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2307 tprintf(T("%"TS"\n"), dentry->full_path);
2310 static const struct {
2313 } file_attr_flags[] = {
2314 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2315 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2316 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2317 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2318 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2319 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2320 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2321 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2322 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2323 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2324 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2325 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2326 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2327 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2328 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2331 #define TIMESTR_MAX 100
2334 timespec_to_string(const struct timespec *spec, tchar *buf)
2336 time_t t = spec->tv_sec;
2339 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2340 buf[TIMESTR_MAX - 1] = '\0';
2344 print_time(const tchar *type, const struct timespec *spec)
2346 tchar timestr[TIMESTR_MAX];
2348 timespec_to_string(spec, timestr);
2350 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2353 static void print_byte_field(const uint8_t field[], size_t len)
2356 tprintf(T("%02hhx"), *field++);
2360 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2362 tputs(T("WIM Information:"));
2363 tputs(T("----------------"));
2364 tprintf(T("Path: %"TS"\n"), wimfile);
2365 tprintf(T("GUID: 0x"));
2366 print_byte_field(info->guid, sizeof(info->guid));
2368 tprintf(T("Version: %u\n"), info->wim_version);
2369 tprintf(T("Image Count: %d\n"), info->image_count);
2370 tprintf(T("Compression: %"TS"\n"),
2371 wimlib_get_compression_type_string(info->compression_type));
2372 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2374 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2375 tprintf(T("Boot Index: %d\n"), info->boot_index);
2376 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2377 tprintf(T("Integrity Info: %"TS"\n"),
2378 info->has_integrity_table ? T("yes") : T("no"));
2379 tprintf(T("Relative path junction: %"TS"\n"),
2380 info->has_rpfix ? T("yes") : T("no"));
2381 tprintf(T("Pipable: %"TS"\n"),
2382 info->pipable ? T("yes") : T("no"));
2387 print_resource(const struct wimlib_resource_entry *resource,
2390 tprintf(T("Hash = 0x"));
2391 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2394 if (!resource->is_missing) {
2395 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2396 resource->uncompressed_size);
2397 if (resource->packed) {
2398 tprintf(T("Raw compressed size = %"PRIu64" bytes\n"),
2399 resource->raw_resource_compressed_size);
2401 tprintf(T("Raw offset in WIM = %"PRIu64" bytes\n"),
2402 resource->raw_resource_offset_in_wim);
2404 tprintf(T("Offset in raw = %"PRIu64" bytes\n"),
2407 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2408 resource->compressed_size);
2410 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2414 tprintf(T("Part Number = %u\n"), resource->part_number);
2415 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2417 tprintf(T("Flags = "));
2418 if (resource->is_compressed)
2419 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2420 if (resource->is_metadata)
2421 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2422 if (resource->is_free)
2423 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2424 if (resource->is_spanned)
2425 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2426 if (resource->packed)
2427 tprintf(T("WIM_RESHDR_FLAG_PACKED_STREAMS "));
2435 print_lookup_table(WIMStruct *wim)
2437 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2441 default_print_security_descriptor(const uint8_t *sd, size_t size)
2443 tprintf(T("Security Descriptor = "));
2444 print_byte_field(sd, size);
2449 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2453 "----------------------------------------------------------------------------\n"));
2454 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2455 if (dentry->dos_name)
2456 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2457 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2458 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2459 if (file_attr_flags[i].flag & dentry->attributes)
2460 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2461 file_attr_flags[i].name);
2463 if (dentry->security_descriptor) {
2464 print_security_descriptor(dentry->security_descriptor,
2465 dentry->security_descriptor_size);
2468 print_time(T("Creation Time"), &dentry->creation_time);
2469 print_time(T("Last Write Time"), &dentry->last_write_time);
2470 print_time(T("Last Access Time"), &dentry->last_access_time);
2473 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2474 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2476 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2477 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2479 if (dentry->unix_mode != 0) {
2480 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2481 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2482 dentry->unix_uid, dentry->unix_gid,
2483 dentry->unix_mode, dentry->unix_rdev);
2486 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2487 if (dentry->streams[i].stream_name) {
2488 tprintf(T("\tData stream \"%"TS"\":\n"),
2489 dentry->streams[i].stream_name);
2491 tprintf(T("\tUnnamed data stream:\n"));
2493 print_resource(&dentry->streams[i].resource, NULL);
2498 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2500 const struct print_dentry_options *options = _options;
2501 if (!options->detailed)
2502 print_dentry_full_path(dentry);
2504 print_dentry_detailed(dentry);
2508 /* Print the files contained in an image(s) in a WIM file. */
2510 imagex_dir(int argc, tchar **argv, int cmd)
2512 const tchar *wimfile;
2513 WIMStruct *wim = NULL;
2516 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2518 struct print_dentry_options options = {
2521 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2523 for_opt(c, dir_options) {
2525 case IMAGEX_PATH_OPTION:
2528 case IMAGEX_DETAILED_OPTION:
2529 options.detailed = true;
2531 case IMAGEX_ONE_FILE_ONLY_OPTION:
2532 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2542 imagex_error(T("Must specify a WIM file"));
2546 imagex_error(T("Too many arguments"));
2551 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2552 imagex_progress_func, NULL);
2557 image = wimlib_resolve_image(wim, argv[1]);
2558 ret = verify_image_exists(image, argv[1], wimfile);
2560 goto out_wimlib_free;
2562 /* No image specified; default to image 1, but only if the WIM
2563 * contains exactly one image. */
2565 struct wimlib_wim_info info;
2567 wimlib_get_wim_info(wim, &info);
2568 if (info.image_count != 1) {
2569 imagex_error(T("\"%"TS"\" contains %d images; Please "
2570 "select one (or all)."),
2571 wimfile, info.image_count);
2578 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2579 print_dentry, &options);
2586 usage(CMD_DIR, stderr);
2591 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2594 imagex_export(int argc, tchar **argv, int cmd)
2598 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2599 int write_flags = 0;
2600 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2601 const tchar *src_wimfile;
2602 const tchar *src_image_num_or_name;
2603 const tchar *dest_wimfile;
2605 const tchar *dest_name;
2606 const tchar *dest_desc;
2608 struct wimlib_wim_info src_info;
2609 WIMStruct *dest_wim;
2614 STRING_SET(refglobs);
2615 unsigned num_threads = 0;
2616 uint32_t chunk_size = UINT32_MAX;
2617 uint32_t solid_chunk_size = UINT32_MAX;
2618 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2620 for_opt(c, export_options) {
2622 case IMAGEX_BOOT_OPTION:
2623 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2625 case IMAGEX_CHECK_OPTION:
2626 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2627 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2629 case IMAGEX_NOCHECK_OPTION:
2630 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2632 case IMAGEX_COMPRESS_OPTION:
2633 compression_type = get_compression_type(optarg);
2634 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2637 case IMAGEX_COMPRESS_SLOW_OPTION:
2638 set_compress_slow();
2639 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2641 case IMAGEX_RECOMPRESS_OPTION:
2642 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2644 case IMAGEX_SOLID_OPTION:
2645 write_flags |= WIMLIB_WRITE_FLAG_PACK_STREAMS;
2647 case IMAGEX_CHUNK_SIZE_OPTION:
2648 chunk_size = parse_chunk_size(optarg);
2649 if (chunk_size == UINT32_MAX)
2652 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2653 solid_chunk_size = parse_chunk_size(optarg);
2654 if (solid_chunk_size == UINT32_MAX)
2657 case IMAGEX_SOLID_COMPRESS_OPTION:
2658 solid_ctype = get_compression_type(optarg);
2659 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2662 case IMAGEX_REF_OPTION:
2663 ret = string_set_append(&refglobs, optarg);
2665 goto out_free_refglobs;
2667 case IMAGEX_THREADS_OPTION:
2668 num_threads = parse_num_threads(optarg);
2669 if (num_threads == UINT_MAX)
2672 case IMAGEX_REBUILD_OPTION:
2673 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2675 case IMAGEX_PIPABLE_OPTION:
2676 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2678 case IMAGEX_NOT_PIPABLE_OPTION:
2679 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2681 case IMAGEX_WIMBOOT_OPTION:
2682 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2690 if (argc < 3 || argc > 5)
2692 src_wimfile = argv[0];
2693 src_image_num_or_name = argv[1];
2694 dest_wimfile = argv[2];
2695 dest_name = (argc >= 4) ? argv[3] : NULL;
2696 dest_desc = (argc >= 5) ? argv[4] : NULL;
2697 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2698 imagex_progress_func, NULL);
2700 goto out_free_refglobs;
2702 wimlib_get_wim_info(src_wim, &src_info);
2704 /* Determine if the destination is an existing file or not. If so, we
2705 * try to append the exported image(s) to it; otherwise, we create a new
2706 * WIM containing the exported image(s). Furthermore, determine if we
2707 * need to write a pipable WIM directly to standard output. */
2709 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2711 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2712 imagex_error("Can't write a non-pipable WIM to "
2713 "standard output! Specify --pipable\n"
2714 " if you want to create a pipable WIM "
2715 "(but read the docs first).");
2717 goto out_free_src_wim;
2720 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2722 dest_wimfile = NULL;
2723 dest_wim_fd = STDOUT_FILENO;
2724 imagex_info_file = stderr;
2725 set_fd_to_binary_mode(dest_wim_fd);
2728 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2730 /* Destination file exists. */
2732 if (!S_ISREG(stbuf.st_mode)) {
2733 imagex_error(T("\"%"TS"\" is not a regular file"),
2736 goto out_free_src_wim;
2738 ret = wimlib_open_wim_with_progress(dest_wimfile,
2740 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2742 imagex_progress_func,
2745 goto out_free_src_wim;
2747 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2748 /* The user specified a compression type, but we're
2749 * exporting to an existing WIM. Make sure the
2750 * specified compression type is the same as the
2751 * compression type of the existing destination WIM. */
2752 struct wimlib_wim_info dest_info;
2754 wimlib_get_wim_info(dest_wim, &dest_info);
2755 if (compression_type != dest_info.compression_type) {
2756 imagex_error(T("Cannot specify a compression type that is "
2757 "not the same as that used in the "
2758 "destination WIM"));
2760 goto out_free_dest_wim;
2766 if (errno != ENOENT) {
2767 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
2770 goto out_free_src_wim;
2773 /* dest_wimfile is not an existing file, so create a new WIM. */
2775 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2776 /* The user did not specify a compression type; default
2777 * to that of the source WIM, unless --solid or
2778 * --wimboot was specified. */
2780 if (write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS)
2781 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2782 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2783 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2785 compression_type = src_info.compression_type;
2787 ret = wimlib_create_new_wim(compression_type, &dest_wim);
2789 goto out_free_src_wim;
2791 wimlib_register_progress_function(dest_wim,
2792 imagex_progress_func, NULL);
2794 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2795 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
2797 /* For --wimboot export, use small XPRESS chunks. */
2798 wimlib_set_output_chunk_size(dest_wim, 4096);
2799 } else if (compression_type == src_info.compression_type &&
2800 chunk_size == UINT32_MAX)
2802 /* Use same chunk size if compression type is the same. */
2803 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
2807 if (chunk_size != UINT32_MAX) {
2808 /* Set destination chunk size. */
2809 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
2811 goto out_free_dest_wim;
2813 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2814 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
2816 goto out_free_dest_wim;
2818 if (solid_chunk_size != UINT32_MAX) {
2819 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
2821 goto out_free_dest_wim;
2824 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
2825 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
2827 goto out_free_dest_wim;
2829 if (refglobs.num_strings) {
2830 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
2832 goto out_free_dest_wim;
2835 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
2836 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
2838 imagex_error(T("--boot specified for all-images export, but source WIM "
2839 "has no bootable image."));
2841 goto out_free_dest_wim;
2844 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
2845 dest_desc, export_flags);
2847 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
2848 do_resource_not_found_warning(src_wimfile,
2849 &src_info, &refglobs);
2851 goto out_free_dest_wim;
2855 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
2856 else if (dest_wimfile)
2857 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
2858 write_flags, num_threads);
2860 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
2861 WIMLIB_ALL_IMAGES, write_flags,
2864 wimlib_free(dest_wim);
2866 wimlib_free(src_wim);
2868 string_set_destroy(&refglobs);
2872 usage(CMD_EXPORT, stderr);
2875 goto out_free_refglobs;
2878 /* Extract files or directories from a WIM image */
2880 imagex_extract(int argc, tchar **argv, int cmd)
2887 const tchar *wimfile;
2888 const tchar *image_num_or_name;
2889 tchar *dest_dir = T(".");
2890 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
2891 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
2892 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
2893 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
2895 STRING_SET(refglobs);
2897 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
2899 for_opt(c, extract_options) {
2901 case IMAGEX_CHECK_OPTION:
2902 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2904 case IMAGEX_VERBOSE_OPTION:
2905 /* No longer does anything. */
2907 case IMAGEX_REF_OPTION:
2908 ret = string_set_append(&refglobs, optarg);
2910 goto out_free_refglobs;
2912 case IMAGEX_UNIX_DATA_OPTION:
2913 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
2915 case IMAGEX_NO_ACLS_OPTION:
2916 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
2918 case IMAGEX_STRICT_ACLS_OPTION:
2919 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
2921 case IMAGEX_NO_ATTRIBUTES_OPTION:
2922 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
2924 case IMAGEX_DEST_DIR_OPTION:
2927 case IMAGEX_TO_STDOUT_OPTION:
2928 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
2929 imagex_info_file = stderr;
2930 imagex_be_quiet = true;
2931 set_fd_to_binary_mode(STDOUT_FILENO);
2933 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
2934 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
2935 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
2937 case IMAGEX_NO_GLOBS_OPTION:
2938 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
2940 case IMAGEX_NULLGLOB_OPTION:
2941 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
2943 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
2944 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
2946 case IMAGEX_WIMBOOT_OPTION:
2947 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
2959 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
2960 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
2962 imagex_error(T("Can't combine --no-globs and --nullglob!"));
2967 image_num_or_name = argv[1];
2972 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2973 imagex_progress_func, NULL);
2975 goto out_free_refglobs;
2977 image = wimlib_resolve_image(wim, image_num_or_name);
2978 ret = verify_image_exists_and_is_single(image,
2982 goto out_wimlib_free;
2984 if (refglobs.num_strings) {
2985 ret = wim_reference_globs(wim, &refglobs, open_flags);
2987 goto out_wimlib_free;
2993 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
2996 while (argc != 0 && ret == 0) {
3000 num_paths < argc && argv[num_paths][0] != T('@');
3005 ret = wimlib_extract_paths(wim, image, dest_dir,
3006 (const tchar **)argv,
3008 extract_flags | notlist_extract_flags);
3012 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3021 if (!imagex_be_quiet)
3022 imagex_printf(T("Done extracting files.\n"));
3023 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3024 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3025 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3026 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3027 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3030 T("Note: You can use the '--nullglob' "
3031 "option to ignore missing files.\n"));
3033 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3034 "files and directories\n"
3035 " are in the WIM image.\n"),
3036 get_cmd_string(CMD_DIR, false));
3037 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3038 struct wimlib_wim_info info;
3040 wimlib_get_wim_info(wim, &info);
3041 do_resource_not_found_warning(wimfile, &info, &refglobs);
3046 string_set_destroy(&refglobs);
3050 usage(CMD_EXTRACT, stderr);
3053 goto out_free_refglobs;
3056 /* Prints information about a WIM file; also can mark an image as bootable,
3057 * change the name of an image, or change the description of an image. */
3059 imagex_info(int argc, tchar **argv, int cmd)
3064 bool nocheck = false;
3065 bool header = false;
3066 bool lookup_table = false;
3068 bool short_header = true;
3069 const tchar *xml_out_file = NULL;
3070 const tchar *wimfile;
3071 const tchar *image_num_or_name;
3072 const tchar *new_name;
3073 const tchar *new_desc;
3078 struct wimlib_wim_info info;
3080 for_opt(c, info_options) {
3082 case IMAGEX_BOOT_OPTION:
3085 case IMAGEX_CHECK_OPTION:
3088 case IMAGEX_NOCHECK_OPTION:
3091 case IMAGEX_HEADER_OPTION:
3093 short_header = false;
3095 case IMAGEX_LOOKUP_TABLE_OPTION:
3096 lookup_table = true;
3097 short_header = false;
3099 case IMAGEX_XML_OPTION:
3101 short_header = false;
3103 case IMAGEX_EXTRACT_XML_OPTION:
3104 xml_out_file = optarg;
3105 short_header = false;
3107 case IMAGEX_METADATA_OPTION:
3108 imagex_error(T("The --metadata option has been removed. "
3109 "Use 'wimdir --detail' instead."));
3118 if (argc < 1 || argc > 4)
3122 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3123 new_name = (argc >= 3) ? argv[2] : NULL;
3124 new_desc = (argc >= 4) ? argv[3] : NULL;
3126 if (check && nocheck) {
3127 imagex_error(T("Can't specify both --check and --nocheck"));
3132 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3134 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3135 imagex_progress_func, NULL);
3139 wimlib_get_wim_info(wim, &info);
3141 image = wimlib_resolve_image(wim, image_num_or_name);
3142 ret = WIMLIB_ERR_INVALID_IMAGE;
3143 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3144 verify_image_exists(image, image_num_or_name, wimfile);
3146 imagex_error(T("If you would like to set the boot "
3147 "index to 0, specify image \"0\" with "
3148 "the --boot flag."));
3150 goto out_wimlib_free;
3153 if (boot && info.image_count == 0) {
3154 imagex_error(T("--boot is meaningless on a WIM with no images"));
3155 goto out_wimlib_free;
3158 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3160 imagex_error(T("Cannot specify the --boot flag "
3161 "without specifying a specific "
3162 "image in a multi-image WIM"));
3163 goto out_wimlib_free;
3166 imagex_error(T("Cannot specify the NEW_NAME "
3167 "without specifying a specific "
3168 "image in a multi-image WIM"));
3169 goto out_wimlib_free;
3173 /* Operations that print information are separated from operations that
3174 * recreate the WIM file. */
3175 if (!new_name && !boot) {
3177 /* Read-only operations */
3179 if (image == WIMLIB_NO_IMAGE) {
3180 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3181 image_num_or_name, wimfile);
3182 goto out_wimlib_free;
3185 if (image == WIMLIB_ALL_IMAGES && short_header)
3186 print_wim_information(wimfile, &info);
3189 wimlib_print_header(wim);
3192 if (info.total_parts != 1) {
3193 tfprintf(stderr, T("Warning: Only showing the lookup table "
3194 "for part %d of a %d-part WIM.\n"),
3195 info.part_number, info.total_parts);
3197 print_lookup_table(wim);
3201 ret = wimlib_extract_xml_data(wim, stdout);
3203 goto out_wimlib_free;
3209 fp = tfopen(xml_out_file, T("wb"));
3211 imagex_error_with_errno(T("Failed to open the "
3212 "file \"%"TS"\" for "
3216 goto out_wimlib_free;
3218 ret = wimlib_extract_xml_data(wim, fp);
3220 imagex_error(T("Failed to close the file "
3226 goto out_wimlib_free;
3230 wimlib_print_available_images(wim, image);
3235 /* Modification operations */
3237 if (image == WIMLIB_ALL_IMAGES)
3240 if (image == WIMLIB_NO_IMAGE && new_name) {
3241 imagex_error(T("Cannot specify new_name (\"%"TS"\") "
3242 "when using image 0"), new_name);
3244 goto out_wimlib_free;
3248 if (image == info.boot_index) {
3249 imagex_printf(T("Image %d is already marked as "
3250 "bootable.\n"), image);
3253 imagex_printf(T("Marking image %d as bootable.\n"),
3255 info.boot_index = image;
3256 ret = wimlib_set_wim_info(wim, &info,
3257 WIMLIB_CHANGE_BOOT_INDEX);
3259 goto out_wimlib_free;
3263 if (!tstrcmp(wimlib_get_image_name(wim, image), new_name))
3265 imagex_printf(T("Image %d is already named \"%"TS"\".\n"),
3269 imagex_printf(T("Changing the name of image %d to "
3270 "\"%"TS"\".\n"), image, new_name);
3271 ret = wimlib_set_image_name(wim, image, new_name);
3273 goto out_wimlib_free;
3277 const tchar *old_desc;
3278 old_desc = wimlib_get_image_description(wim, image);
3279 if (old_desc && !tstrcmp(old_desc, new_desc)) {
3280 imagex_printf(T("The description of image %d is already "
3281 "\"%"TS"\".\n"), image, new_desc);
3284 imagex_printf(T("Changing the description of image %d "
3285 "to \"%"TS"\".\n"), image, new_desc);
3286 ret = wimlib_set_image_descripton(wim, image,
3289 goto out_wimlib_free;
3293 /* Only call wimlib_overwrite() if something actually needs to
3295 if (boot || new_name || new_desc ||
3296 (check && !info.has_integrity_table) ||
3297 (nocheck && info.has_integrity_table))
3299 int write_flags = 0;
3302 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3304 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3305 ret = wimlib_overwrite(wim, write_flags, 1);
3307 imagex_printf(T("The file \"%"TS"\" was not modified "
3308 "because nothing needed to be done.\n"),
3319 usage(CMD_INFO, stderr);
3325 /* Join split WIMs into one part WIM */
3327 imagex_join(int argc, tchar **argv, int cmd)
3330 int swm_open_flags = 0;
3331 int wim_write_flags = 0;
3332 const tchar *output_path;
3335 for_opt(c, join_options) {
3337 case IMAGEX_CHECK_OPTION:
3338 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3339 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3349 imagex_error(T("Must specify one or more split WIM (.swm) "
3353 output_path = argv[0];
3354 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3359 imagex_progress_func,
3365 usage(CMD_JOIN, stderr);
3370 #if WIM_MOUNTING_SUPPORTED
3372 /* Mounts a WIM image. */
3374 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3377 int mount_flags = 0;
3379 const tchar *staging_dir = NULL;
3380 const tchar *wimfile;
3383 struct wimlib_wim_info info;
3387 STRING_SET(refglobs);
3389 if (cmd == CMD_MOUNTRW) {
3390 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3391 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3394 for_opt(c, mount_options) {
3396 case IMAGEX_ALLOW_OTHER_OPTION:
3397 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3399 case IMAGEX_CHECK_OPTION:
3400 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3402 case IMAGEX_DEBUG_OPTION:
3403 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3405 case IMAGEX_STREAMS_INTERFACE_OPTION:
3406 if (!tstrcasecmp(optarg, T("none")))
3407 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3408 else if (!tstrcasecmp(optarg, T("xattr")))
3409 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3410 else if (!tstrcasecmp(optarg, T("windows")))
3411 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3413 imagex_error(T("Unknown stream interface \"%"TS"\""),
3418 case IMAGEX_REF_OPTION:
3419 ret = string_set_append(&refglobs, optarg);
3421 goto out_free_refglobs;
3423 case IMAGEX_STAGING_DIR_OPTION:
3424 staging_dir = optarg;
3426 case IMAGEX_UNIX_DATA_OPTION:
3427 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3435 if (argc != 2 && argc != 3)
3440 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3441 imagex_progress_func, NULL);
3443 goto out_free_refglobs;
3445 wimlib_get_wim_info(wim, &info);
3448 /* Image explicitly specified. */
3449 image = wimlib_resolve_image(wim, argv[1]);
3451 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3455 /* No image specified; default to image 1, but only if the WIM
3456 * contains exactly one image. */
3458 if (info.image_count != 1) {
3459 imagex_error(T("\"%"TS"\" contains %d images; Please "
3460 "select one."), wimfile, info.image_count);
3468 if (refglobs.num_strings) {
3469 ret = wim_reference_globs(wim, &refglobs, open_flags);
3474 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3476 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3478 image, wimfile, dir);
3483 string_set_destroy(&refglobs);
3489 goto out_free_refglobs;
3491 #endif /* WIM_MOUNTING_SUPPORTED */
3493 /* Rebuild a WIM file */
3495 imagex_optimize(int argc, tchar **argv, int cmd)
3498 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3499 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3500 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3501 uint32_t chunk_size = UINT32_MAX;
3502 uint32_t solid_chunk_size = UINT32_MAX;
3503 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3506 const tchar *wimfile;
3509 unsigned num_threads = 0;
3511 for_opt(c, optimize_options) {
3513 case IMAGEX_CHECK_OPTION:
3514 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3515 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3517 case IMAGEX_NOCHECK_OPTION:
3518 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3520 case IMAGEX_COMPRESS_OPTION:
3521 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3522 compression_type = get_compression_type(optarg);
3523 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3526 case IMAGEX_COMPRESS_SLOW_OPTION:
3527 set_compress_slow();
3528 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3530 case IMAGEX_RECOMPRESS_OPTION:
3531 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3533 case IMAGEX_CHUNK_SIZE_OPTION:
3534 chunk_size = parse_chunk_size(optarg);
3535 if (chunk_size == UINT32_MAX)
3538 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3539 solid_chunk_size = parse_chunk_size(optarg);
3540 if (solid_chunk_size == UINT32_MAX)
3543 case IMAGEX_SOLID_COMPRESS_OPTION:
3544 solid_ctype = get_compression_type(optarg);
3545 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3548 case IMAGEX_SOLID_OPTION:
3549 write_flags |= WIMLIB_WRITE_FLAG_PACK_STREAMS;
3550 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3552 case IMAGEX_THREADS_OPTION:
3553 num_threads = parse_num_threads(optarg);
3554 if (num_threads == UINT_MAX)
3557 case IMAGEX_PIPABLE_OPTION:
3558 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3560 case IMAGEX_NOT_PIPABLE_OPTION:
3561 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3575 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3576 imagex_progress_func, NULL);
3580 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3581 /* Change compression type. */
3582 ret = wimlib_set_output_compression_type(wim, compression_type);
3584 goto out_wimlib_free;
3587 if (chunk_size != UINT32_MAX) {
3588 /* Change chunk size. */
3589 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3591 goto out_wimlib_free;
3593 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3594 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3596 goto out_wimlib_free;
3598 if (solid_chunk_size != UINT32_MAX) {
3599 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3601 goto out_wimlib_free;
3604 old_size = file_get_size(wimfile);
3605 tprintf(T("\"%"TS"\" original size: "), wimfile);
3607 tputs(T("Unknown"));
3609 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3611 ret = wimlib_overwrite(wim, write_flags, num_threads);
3613 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3614 goto out_wimlib_free;
3617 new_size = file_get_size(wimfile);
3618 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3620 tputs(T("Unknown"));
3622 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3624 tfputs(T("Space saved: "), stdout);
3625 if (new_size != -1 && old_size != -1) {
3626 tprintf(T("%lld KiB\n"),
3627 ((long long)old_size - (long long)new_size) >> 10);
3629 tputs(T("Unknown"));
3638 usage(CMD_OPTIMIZE, stderr);
3644 /* Split a WIM into a spanned set */
3646 imagex_split(int argc, tchar **argv, int cmd)
3650 int write_flags = 0;
3651 unsigned long part_size;
3656 for_opt(c, split_options) {
3658 case IMAGEX_CHECK_OPTION:
3659 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3660 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3672 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3673 if (tmp == argv[2] || *tmp) {
3674 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3675 imagex_error(T("The part size must be an integer or "
3676 "floating-point number of megabytes."));
3679 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3680 imagex_progress_func, NULL);
3684 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3690 usage(CMD_SPLIT, stderr);
3696 #if WIM_MOUNTING_SUPPORTED
3697 /* Unmounts a mounted WIM image. */
3699 imagex_unmount(int argc, tchar **argv, int cmd)
3702 int unmount_flags = 0;
3705 for_opt(c, unmount_options) {
3707 case IMAGEX_COMMIT_OPTION:
3708 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3710 case IMAGEX_CHECK_OPTION:
3711 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3713 case IMAGEX_REBUILD_OPTION:
3714 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3716 case IMAGEX_LAZY_OPTION:
3717 case IMAGEX_FORCE_OPTION:
3718 /* Now, unmount is lazy by default. However, committing
3719 * the image will fail with
3720 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3721 * file descriptors on the WIM image. The
3722 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3723 * descriptors to be closed. */
3724 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3726 case IMAGEX_NEW_IMAGE_OPTION:
3727 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
3738 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
3739 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
3740 imagex_error(T("--new-image is meaningless "
3741 "without --commit also specified!"));
3746 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
3747 imagex_progress_func, NULL);
3749 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
3750 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
3752 "\tNote: Use --commit --force to force changes "
3753 "to be committed, regardless\n"
3754 "\t of open files.\n"));
3761 usage(CMD_UNMOUNT, stderr);
3766 #endif /* WIM_MOUNTING_SUPPORTED */
3769 * Add, delete, or rename files in a WIM image.
3772 imagex_update(int argc, tchar **argv, int cmd)
3774 const tchar *wimfile;
3778 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3779 int write_flags = 0;
3780 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
3781 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
3782 WIMLIB_ADD_FLAG_VERBOSE |
3783 WIMLIB_ADD_FLAG_WINCONFIG;
3784 int default_delete_flags = 0;
3785 unsigned num_threads = 0;
3787 tchar *cmd_file_contents;
3788 size_t cmd_file_nchars;
3789 struct wimlib_update_command *cmds;
3791 tchar *command_str = NULL;
3792 tchar *config_file = NULL;
3793 tchar *wimboot_config = NULL;
3795 for_opt(c, update_options) {
3797 /* Generic or write options */
3798 case IMAGEX_THREADS_OPTION:
3799 num_threads = parse_num_threads(optarg);
3800 if (num_threads == UINT_MAX)
3803 case IMAGEX_CHECK_OPTION:
3804 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3805 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3807 case IMAGEX_REBUILD_OPTION:
3808 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
3810 case IMAGEX_COMMAND_OPTION:
3812 imagex_error(T("--command may only be specified "
3813 "one time. Please provide\n"
3814 " the update commands "
3815 "on standard input instead."));
3818 command_str = tstrdup(optarg);
3820 imagex_error(T("Out of memory!"));
3824 case IMAGEX_WIMBOOT_CONFIG_OPTION:
3825 wimboot_config = optarg;
3827 /* Default delete options */
3828 case IMAGEX_FORCE_OPTION:
3829 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
3831 case IMAGEX_RECURSIVE_OPTION:
3832 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
3835 /* Global add option */
3836 case IMAGEX_CONFIG_OPTION:
3837 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
3838 config_file = optarg;
3841 /* Default add options */
3842 case IMAGEX_VERBOSE_OPTION:
3843 /* No longer does anything. */
3845 case IMAGEX_DEREFERENCE_OPTION:
3846 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
3848 case IMAGEX_UNIX_DATA_OPTION:
3849 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
3851 case IMAGEX_NO_ACLS_OPTION:
3852 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
3854 case IMAGEX_STRICT_ACLS_OPTION:
3855 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
3857 case IMAGEX_NO_REPLACE_OPTION:
3858 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
3867 if (argc != 1 && argc != 2)
3871 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3872 imagex_progress_func, NULL);
3874 goto out_free_command_str;
3877 /* Image explicitly specified. */
3878 image = wimlib_resolve_image(wim, argv[1]);
3879 ret = verify_image_exists_and_is_single(image, argv[1],
3882 goto out_wimlib_free;
3884 /* No image specified; default to image 1, but only if the WIM
3885 * contains exactly one image. */
3886 struct wimlib_wim_info info;
3888 wimlib_get_wim_info(wim, &info);
3889 if (info.image_count != 1) {
3890 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
3891 wimfile, info.image_count);
3898 /* Read update commands from standard input, or the command string if
3901 cmd_file_contents = NULL;
3902 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
3906 goto out_free_cmd_file_contents;
3908 } else if (!wimboot_config) {
3909 if (isatty(STDIN_FILENO)) {
3910 tputs(T("Reading update commands from standard input..."));
3911 recommend_man_page(CMD_UPDATE, stdout);
3913 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
3914 if (!cmd_file_contents) {
3916 goto out_wimlib_free;
3919 /* Parse the update commands */
3920 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
3924 goto out_free_cmd_file_contents;
3927 cmd_file_contents = NULL;
3932 /* Set default flags and capture config on the update commands */
3933 for (size_t i = 0; i < num_cmds; i++) {
3934 switch (cmds[i].op) {
3935 case WIMLIB_UPDATE_OP_ADD:
3936 cmds[i].add.add_flags |= default_add_flags;
3937 cmds[i].add.config_file = config_file;
3939 case WIMLIB_UPDATE_OP_DELETE:
3940 cmds[i].delete_.delete_flags |= default_delete_flags;
3947 /* Execute the update commands */
3948 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
3952 if (wimboot_config) {
3953 /* --wimboot-config=FILE is short for an
3954 * "add FILE /Windows/System32/WimBootCompress.ini" command.
3956 struct wimlib_update_command cmd;
3958 cmd.op = WIMLIB_UPDATE_OP_ADD;
3959 cmd.add.fs_source_path = wimboot_config;
3960 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
3961 cmd.add.config_file = NULL;
3962 cmd.add.add_flags = 0;
3964 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
3969 /* Overwrite the updated WIM */
3970 ret = wimlib_overwrite(wim, write_flags, num_threads);
3973 out_free_cmd_file_contents:
3974 free(cmd_file_contents);
3977 out_free_command_str:
3982 usage(CMD_UPDATE, stderr);
3985 goto out_free_command_str;
3988 /* Verify a WIM file. */
3990 imagex_verify(int argc, tchar **argv, int cmd)
3993 const tchar *wimfile;
3995 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3996 int verify_flags = 0;
3997 STRING_SET(refglobs);
4000 for_opt(c, verify_options) {
4002 case IMAGEX_REF_OPTION:
4003 ret = string_set_append(&refglobs, optarg);
4005 goto out_free_refglobs;
4017 imagex_error(T("Must specify a WIM file!"));
4019 imagex_error(T("At most one WIM file can be specified!"));
4025 ret = wimlib_open_wim_with_progress(wimfile,
4028 imagex_progress_func,
4031 goto out_free_refglobs;
4033 ret = wim_reference_globs(wim, &refglobs, open_flags);
4035 goto out_wimlib_free;
4037 ret = wimlib_verify_wim(wim, verify_flags);
4039 tputc(T('\n'), stderr);
4040 imagex_error(T("\"%"TS"\" failed verification!"),
4042 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4043 refglobs.num_strings == 0)
4045 imagex_printf(T("Note: if this WIM file is not standalone, "
4046 "use the --ref option to specify the other parts.\n"));
4049 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4056 string_set_destroy(&refglobs);
4060 usage(CMD_VERIFY, stderr);
4062 goto out_free_refglobs;
4065 struct imagex_command {
4067 int (*func)(int argc, tchar **argv, int cmd);
4070 static const struct imagex_command imagex_commands[] = {
4071 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4072 [CMD_APPLY] = {T("apply"), imagex_apply},
4073 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4074 [CMD_DELETE] = {T("delete"), imagex_delete},
4075 [CMD_DIR ] = {T("dir"), imagex_dir},
4076 [CMD_EXPORT] = {T("export"), imagex_export},
4077 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4078 [CMD_INFO] = {T("info"), imagex_info},
4079 [CMD_JOIN] = {T("join"), imagex_join},
4080 #if WIM_MOUNTING_SUPPORTED
4081 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4082 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4084 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4085 [CMD_SPLIT] = {T("split"), imagex_split},
4086 #if WIM_MOUNTING_SUPPORTED
4087 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4089 [CMD_UPDATE] = {T("update"), imagex_update},
4090 [CMD_VERIFY] = {T("verify"), imagex_verify},
4095 /* Can be a directory or source list file. But source list file is probably
4096 * a rare use case, so just say directory. */
4097 # define SOURCE_STR T("DIRECTORY")
4099 /* Can only be a directory */
4100 # define TARGET_STR T("DIRECTORY")
4103 /* Can be a directory, NTFS volume, or source list file. */
4104 # define SOURCE_STR T("SOURCE")
4106 /* Can be a directory or NTFS volume. */
4107 # define TARGET_STR T("TARGET")
4111 static const tchar *usage_strings[] = {
4114 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4115 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4116 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4117 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4118 " [--wimboot] [--unix-data] [--dereference]\n"
4122 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4123 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4124 " [--no-attributes] [--rpfix] [--norpfix]\n"
4125 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4129 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4130 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4131 " [--config=FILE] [--threads=NUM_THREADS]\n"
4132 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4133 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4134 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4138 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4142 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4146 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4147 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4148 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4149 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4154 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4155 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4156 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4157 " [--no-attributes] [--include-invalid-names]\n"
4158 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4162 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4163 " [--boot] [--check] [--nocheck] [--xml]\n"
4164 " [--extract-xml FILE] [--header] [--lookup-table]\n"
4168 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4170 #if WIM_MOUNTING_SUPPORTED
4173 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4174 " [--check] [--streams-interface=INTERFACE]\n"
4175 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4179 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4180 " [--check] [--streams-interface=INTERFACE]\n"
4181 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4187 " [--recompress] [--compress=TYPE]\n"
4188 " [--threads=NUM_THREADS] [--check] [--nocheck]\n"
4193 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4195 #if WIM_MOUNTING_SUPPORTED
4198 " %"TS" DIRECTORY\n"
4199 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4204 " %"TS" WIMFILE [IMAGE]\n"
4205 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4206 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4207 " [--command=STRING] [--wimboot-config=FILE]\n"
4212 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4216 static const tchar *invocation_name;
4217 static int invocation_cmd = CMD_NONE;
4219 static const tchar *get_cmd_string(int cmd, bool nospace)
4221 static tchar buf[50];
4222 if (cmd == CMD_NONE) {
4223 tsprintf(buf, T("%"TS), T(IMAGEX_PROGNAME));
4224 } else if (invocation_cmd != CMD_NONE) {
4225 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4227 const tchar *format;
4230 format = T("%"TS"-%"TS"");
4232 format = T("%"TS" %"TS"");
4233 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4241 static const tchar *s =
4243 IMAGEX_PROGNAME " (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4244 "Copyright (C) 2012, 2013, 2014 Eric Biggers\n"
4245 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4246 "This is free software: you are free to change and redistribute it.\n"
4247 "There is NO WARRANTY, to the extent permitted by law.\n"
4249 "Report bugs to "PACKAGE_BUGREPORT".\n"
4256 help_or_version(int argc, tchar **argv, int cmd)
4261 for (i = 1; i < argc; i++) {
4263 if (p[0] == T('-') && p[1] == T('-')) {
4265 if (!tstrcmp(p, T("help"))) {
4266 if (cmd == CMD_NONE)
4271 } else if (!tstrcmp(p, T("version"))) {
4280 print_usage_string(int cmd, FILE *fp)
4282 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4286 recommend_man_page(int cmd, FILE *fp)
4288 const tchar *format_str;
4290 format_str = T("Some uncommon options are not listed;\n"
4291 "See %"TS".pdf in the doc directory for more details.\n");
4293 format_str = T("Some uncommon options are not listed;\n"
4294 "Try `man %"TS"' for more details.\n");
4296 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4300 usage(int cmd, FILE *fp)
4302 tfprintf(fp, T("Usage:\n"));
4303 print_usage_string(cmd, fp);
4304 tfprintf(fp, T("\n"));
4305 recommend_man_page(cmd, fp);
4311 tfprintf(fp, T("Usage:\n"));
4312 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4313 print_usage_string(cmd, fp);
4314 tfprintf(fp, T("\n"));
4316 static const tchar *extra =
4319 " %"TS" --version\n"
4322 tfprintf(fp, extra, invocation_name, invocation_name);
4324 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4325 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4326 "For some commands IMAGE may be \"all\".\n"
4328 recommend_man_page(CMD_NONE, fp);
4331 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4332 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4333 * something else), while on Windows the command arguments will be UTF-16LE
4334 * encoded 'wchar_t' strings. */
4337 wmain(int argc, wchar_t **argv, wchar_t **envp)
4339 main(int argc, char **argv)
4346 imagex_info_file = stdout;
4347 invocation_name = tbasename(argv[0]);
4350 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4351 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4355 setlocale(LC_ALL, "");
4356 codeset = nl_langinfo(CODESET);
4357 if (!strstr(codeset, "UTF-8") &&
4358 !strstr(codeset, "UTF8") &&
4359 !strstr(codeset, "utf-8") &&
4360 !strstr(codeset, "utf8"))
4363 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4364 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4365 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4366 " to any value to force wimlib to use UTF-8.\n",
4372 #endif /* !__WIN32__ */
4375 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4376 if (igcase != NULL) {
4377 if (!tstrcmp(igcase, T("no")) ||
4378 !tstrcmp(igcase, T("0")))
4379 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4380 else if (!tstrcmp(igcase, T("yes")) ||
4381 !tstrcmp(igcase, T("1")))
4382 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4385 "WARNING: Ignoring unknown setting of "
4386 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4391 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4393 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4394 tstrcmp(invocation_name, T(IMAGEX_PROGNAME))) {
4395 for (int i = 0; i < CMD_MAX; i++) {
4396 if (!tstrcmp(invocation_name + 3,
4397 imagex_commands[i].name))
4406 /* Unless already known from the invocation name, determine which
4407 * command was specified. */
4408 if (cmd == CMD_NONE) {
4410 imagex_error(T("No command specified!\n"));
4414 for (int i = 0; i < CMD_MAX; i++) {
4415 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4420 if (cmd != CMD_NONE) {
4426 /* Handle --help and --version. --help can be either for the program as
4427 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4428 * CMD_NONE). Note: help_or_version() will not return if a --help or
4429 * --version argument was found. */
4430 help_or_version(argc, argv, cmd);
4432 /* Bail if a valid command was not specified. */
4433 if (cmd == CMD_NONE) {
4434 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4439 /* Enable warning and error messages in wimlib to be more user-friendly.
4441 wimlib_set_print_errors(true);
4443 /* Initialize wimlib. */
4444 ret = wimlib_global_init(init_flags);
4446 goto out_check_status;
4448 /* Call the command handler function. */
4449 ret = imagex_commands[cmd].func(argc, argv, cmd);
4451 /* Check for error writing to standard output, especially since for some
4452 * commands, writing to standard output is part of the program's actual
4453 * behavior and not just for informational purposes. */
4454 if (ferror(stdout) || fclose(stdout)) {
4455 imagex_error_with_errno(T("error writing to standard output"));
4460 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4461 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4462 * error code from which an error message can be printed. */
4464 imagex_error(T("Exiting with error code %d:\n"
4466 wimlib_get_error_string(ret));
4467 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4468 imagex_error_with_errno(T("errno"));
4470 /* Make wimlib free any resources it's holding (although this is not
4471 * strictly necessary because the process is ending anyway). */
4472 wimlib_global_cleanup();