4 * Use wimlib to create, modify, extract, mount, unmount, or display information
9 * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # include "config.h" /* Need for PACKAGE_VERSION, etc. */
30 #include "wimlib_tchar.h"
49 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
52 # include "imagex-win32.h"
53 # define print_security_descriptor win32_print_security_descriptor
56 # include <langinfo.h>
57 # define print_security_descriptor default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
63 /* Don't confuse the user by presenting the mounting commands on Windows when
64 * they will never work. However on UNIX-like systems we always present them,
65 * even if WITH_FUSE is not defined at this point, as to not tie the build of
66 * wimlib-imagex to a specific build of wimlib. */
68 # define WIM_MOUNTING_SUPPORTED 0
70 # define WIM_MOUNTING_SUPPORTED 1
73 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
76 is_any_path_separator(tchar c)
78 return c == T('/') || c == T('\\');
81 /* Like basename(), but handles both forward and backwards slashes. */
83 tbasename(tchar *path)
85 tchar *p = tstrchr(path, T('\0'));
90 if (!is_any_path_separator(*--p))
98 if (is_any_path_separator(*--p))
103 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
117 #if WIM_MOUNTING_SUPPORTED
123 #if WIM_MOUNTING_SUPPORTED
131 static void usage(int cmd, FILE *fp);
132 static void usage_all(FILE *fp);
133 static void recommend_man_page(int cmd, FILE *fp);
134 static const tchar *get_cmd_string(int cmd, bool nospace);
136 static bool imagex_be_quiet = false;
137 static FILE *imagex_info_file;
139 #define imagex_printf(format, ...) \
140 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
143 IMAGEX_ALLOW_OTHER_OPTION,
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_NO_SOLID_SORT_OPTION,
174 IMAGEX_NULLGLOB_OPTION,
175 IMAGEX_ONE_FILE_ONLY_OPTION,
177 IMAGEX_PIPABLE_OPTION,
178 IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
179 IMAGEX_REBUILD_OPTION,
180 IMAGEX_RECOMPRESS_OPTION,
181 IMAGEX_RECURSIVE_OPTION,
183 IMAGEX_RESUME_OPTION,
187 IMAGEX_SOLID_CHUNK_SIZE_OPTION,
188 IMAGEX_SOLID_COMPRESS_OPTION,
189 IMAGEX_SOURCE_LIST_OPTION,
190 IMAGEX_STAGING_DIR_OPTION,
191 IMAGEX_STREAMS_INTERFACE_OPTION,
192 IMAGEX_STRICT_ACLS_OPTION,
193 IMAGEX_THREADS_OPTION,
194 IMAGEX_TO_STDOUT_OPTION,
195 IMAGEX_UNIX_DATA_OPTION,
196 IMAGEX_UPDATE_OF_OPTION,
197 IMAGEX_VERBOSE_OPTION,
198 IMAGEX_WIMBOOT_OPTION,
199 IMAGEX_WIMBOOT_CONFIG_OPTION,
203 static const struct option apply_options[] = {
204 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
205 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
206 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
207 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
208 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
209 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
210 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
211 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
212 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
213 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
214 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
216 /* --resume is undocumented for now as it needs improvement. */
217 {T("resume"), no_argument, NULL, IMAGEX_RESUME_OPTION},
218 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
222 static const struct option capture_or_append_options[] = {
223 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
224 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
225 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
226 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
227 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
228 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
229 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
230 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
231 {T("pack-streams"), no_argument, NULL, IMAGEX_SOLID_OPTION},
232 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
233 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
234 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
235 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
236 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
237 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
238 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
239 {T("flags"), required_argument, NULL, IMAGEX_FLAGS_OPTION},
240 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
241 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
242 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
243 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
244 {T("source-list"), no_argument, NULL, IMAGEX_SOURCE_LIST_OPTION},
245 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
246 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
247 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
248 {T("rpfix"), no_argument, NULL, IMAGEX_RPFIX_OPTION},
249 {T("norpfix"), no_argument, NULL, IMAGEX_NORPFIX_OPTION},
250 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
251 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
252 {T("update-of"), required_argument, NULL, IMAGEX_UPDATE_OF_OPTION},
253 {T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
254 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
258 static const struct option delete_options[] = {
259 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
260 {T("soft"), no_argument, NULL, IMAGEX_SOFT_OPTION},
264 static const struct option dir_options[] = {
265 {T("path"), required_argument, NULL, IMAGEX_PATH_OPTION},
266 {T("detailed"), no_argument, NULL, IMAGEX_DETAILED_OPTION},
267 {T("one-file-only"), no_argument, NULL, IMAGEX_ONE_FILE_ONLY_OPTION},
271 static const struct option export_options[] = {
272 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
273 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
274 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
275 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
276 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
277 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
278 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
279 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
280 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
281 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
282 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
283 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
284 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
285 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
286 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
287 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
288 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
289 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
290 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
291 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
292 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
296 static const struct option extract_options[] = {
297 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
298 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
299 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
300 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
301 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
302 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
303 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
304 {T("no-attributes"), no_argument, NULL, IMAGEX_NO_ATTRIBUTES_OPTION},
305 {T("dest-dir"), required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
306 {T("to-stdout"), no_argument, NULL, IMAGEX_TO_STDOUT_OPTION},
307 {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
308 {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
309 {T("no-globs"), no_argument, NULL, IMAGEX_NO_GLOBS_OPTION},
310 {T("nullglob"), no_argument, NULL, IMAGEX_NULLGLOB_OPTION},
311 {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
312 {T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
316 static const struct option info_options[] = {
317 {T("boot"), no_argument, NULL, IMAGEX_BOOT_OPTION},
318 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
319 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
320 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
321 {T("extract-xml"), required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
322 {T("header"), no_argument, NULL, IMAGEX_HEADER_OPTION},
323 {T("lookup-table"), no_argument, NULL, IMAGEX_LOOKUP_TABLE_OPTION},
324 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
325 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
329 static const struct option join_options[] = {
330 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
334 static const struct option mount_options[] = {
335 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
336 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
337 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
338 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
339 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
340 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
341 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
345 static const struct option optimize_options[] = {
346 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
347 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
348 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
349 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
350 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
351 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
352 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
353 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
354 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
355 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
356 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
357 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
358 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
359 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
360 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
361 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
362 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
363 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
367 static const struct option split_options[] = {
368 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
372 static const struct option unmount_options[] = {
373 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
374 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
375 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
376 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
377 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
378 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
382 static const struct option update_options[] = {
383 /* Careful: some of the options here set the defaults for update
384 * commands, but the flags given to an actual update command (and not to
385 * `imagex update' itself are also handled in
386 * update_command_add_option(). */
387 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
388 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
389 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
390 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
391 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
393 /* Default delete options */
394 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
395 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
397 /* Global add option */
398 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
400 /* Default add options */
401 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
402 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
403 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
404 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
405 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
406 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
407 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
412 static const struct option verify_options[] = {
413 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
414 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
420 # define _format_attribute(type, format_str, args_start) \
421 __attribute__((format(type, format_str, args_start)))
423 # define _format_attribute(type, format_str, args_start)
426 /* Print formatted error message to stderr. */
427 static void _format_attribute(printf, 1, 2)
428 imagex_error(const tchar *format, ...)
431 va_start(va, format);
432 tfputs(T("ERROR: "), stderr);
433 tvfprintf(stderr, format, va);
434 tputc(T('\n'), stderr);
438 /* Print formatted error message to stderr. */
439 static void _format_attribute(printf, 1, 2)
440 imagex_error_with_errno(const tchar *format, ...)
442 int errno_save = errno;
444 va_start(va, format);
445 tfputs(T("ERROR: "), stderr);
446 tvfprintf(stderr, format, va);
447 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
452 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
454 if (image == WIMLIB_NO_IMAGE) {
455 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
456 " Please specify a 1-based image index or "
457 "image name. To list the images\n"
458 " contained in the WIM archive, run\n"
460 " %"TS" \"%"TS"\"\n"),
461 image_name, wim_name,
462 get_cmd_string(CMD_INFO, false), wim_name);
463 return WIMLIB_ERR_INVALID_IMAGE;
469 verify_image_is_single(int image)
471 if (image == WIMLIB_ALL_IMAGES) {
472 imagex_error(T("Cannot specify all images for this action!"));
473 return WIMLIB_ERR_INVALID_IMAGE;
479 verify_image_exists_and_is_single(int image, const tchar *image_name,
480 const tchar *wim_name)
483 ret = verify_image_exists(image, image_name, wim_name);
485 ret = verify_image_is_single(image);
490 print_available_compression_types(FILE *fp)
492 static const tchar *s =
494 "Available compression types:\n"
497 " xpress (alias: \"fast\")\n"
498 " lzx (alias: \"maximum\") (default for capture)\n"
499 " lzms (alias: \"recovery\")\n"
505 /* Parse the argument to --compress */
507 get_compression_type(tchar *optarg)
510 unsigned int compression_level = 0;
513 plevel = tstrchr(optarg, T(':'));
519 ultmp = tstrtoul(plevel, &ptmp, 10);
520 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
521 imagex_error(T("Compression level must be a positive integer! "
522 "e.g. --compress=lzx:80"));
523 return WIMLIB_COMPRESSION_TYPE_INVALID;
525 compression_level = ultmp;
528 if (!tstrcasecmp(optarg, T("maximum")) ||
529 !tstrcasecmp(optarg, T("lzx")) ||
530 !tstrcasecmp(optarg, T("max")))
531 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
532 else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress")))
533 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
534 else if (!tstrcasecmp(optarg, T("recovery")) || !tstrcasecmp(optarg, T("lzms")))
535 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
536 else if (!tstrcasecmp(optarg, T("none")))
537 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
539 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
540 print_available_compression_types(stderr);
541 return WIMLIB_COMPRESSION_TYPE_INVALID;
544 if (compression_level != 0)
545 wimlib_set_default_compression_level(ctype, compression_level);
550 set_compress_slow(void)
553 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
554 " Use the '--compress=TYPE:LEVEL' option instead.\n");
556 wimlib_set_default_compression_level(-1, 100);
560 const tchar **strings;
561 unsigned num_strings;
562 unsigned num_alloc_strings;
565 #define STRING_SET_INITIALIZER \
566 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
568 #define STRING_SET(_strings) \
569 struct string_set _strings = STRING_SET_INITIALIZER
572 string_set_append(struct string_set *set, const tchar *glob)
574 unsigned num_alloc_strings = set->num_alloc_strings;
576 if (set->num_strings == num_alloc_strings) {
577 const tchar **new_strings;
579 num_alloc_strings += 4;
580 new_strings = realloc(set->strings,
581 sizeof(set->strings[0]) * num_alloc_strings);
583 imagex_error(T("Out of memory!"));
586 set->strings = new_strings;
587 set->num_alloc_strings = num_alloc_strings;
589 set->strings[set->num_strings++] = glob;
594 string_set_destroy(struct string_set *set)
600 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
602 return wimlib_reference_resource_files(wim, set->strings,
604 WIMLIB_REF_FLAG_GLOB_ENABLE,
609 do_resource_not_found_warning(const tchar *wimfile,
610 const struct wimlib_wim_info *info,
611 const struct string_set *refglobs)
613 if (info->total_parts > 1) {
614 if (refglobs->num_strings == 0) {
615 imagex_error(T("\"%"TS"\" is part of a split WIM. "
616 "Use --ref to specify the other parts."),
619 imagex_error(T("Perhaps the '--ref' argument did not "
620 "specify all other parts of the split "
624 imagex_error(T("If this is a delta WIM, use the --ref argument "
625 "to specify the WIM(s) on which it is based."));
629 /* Returns the size of a file given its name, or -1 if the file does not exist
630 * or its size cannot be determined. */
632 file_get_size(const tchar *filename)
635 if (tstat(filename, &st) == 0)
642 PARSE_STRING_SUCCESS = 0,
643 PARSE_STRING_FAILURE = 1,
644 PARSE_STRING_NONE = 2,
648 * Parses a string token from an array of characters.
650 * Tokens are either whitespace-delimited, or double or single-quoted.
652 * @line_p: Pointer to the pointer to the line of data. Will be updated
653 * to point past the string token iff the return value is
654 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
657 * @len_p: @len_p initially stores the length of the line of data, which may
658 * be 0, and it will be updated to the number of bytes remaining in
659 * the line iff the return value is PARSE_STRING_SUCCESS.
661 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
662 * parsed string token will be returned here.
664 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
665 * PARSE_STRING_FAILURE if the data was invalid due to a missing
666 * closing quote; or PARSE_STRING_NONE if the line ended before the
667 * beginning of a string token was found.
670 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
673 tchar *line = *line_p;
677 /* Skip leading whitespace */
680 return PARSE_STRING_NONE;
681 if (!istspace(*line) && *line != T('\0'))
687 if (quote_char == T('"') || quote_char == T('\'')) {
692 line = tmemchr(line, quote_char, len);
694 imagex_error(T("Missing closing quote: %"TS), fn - 1);
695 return PARSE_STRING_FAILURE;
698 /* Unquoted string. Go until whitespace. Line is terminated
699 * by '\0', so no need to check 'len'. */
703 } while (!istspace(*line) && *line != T('\0'));
710 return PARSE_STRING_SUCCESS;
713 /* Parses a line of data (not an empty line or comment) in the source list file
714 * format. (See the man page for 'wimlib-imagex capture' for details on this
715 * format and the meaning.)
717 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
718 * len == 0. The data in @line will be modified by this function call.
720 * @len: Length of the line of data.
722 * @source: On success, the capture source and target described by the line is
723 * written into this destination. Note that it will contain pointers
724 * to data in the @line array.
726 * Returns true if the line was valid; false otherwise. */
728 parse_source_list_line(tchar *line, size_t len,
729 struct wimlib_capture_source *source)
733 ret = parse_string(&line, &len, &source->fs_source_path);
734 if (ret != PARSE_STRING_SUCCESS)
736 ret = parse_string(&line, &len, &source->wim_target_path);
737 if (ret == PARSE_STRING_NONE)
738 source->wim_target_path = source->fs_source_path;
739 return ret != PARSE_STRING_FAILURE;
742 /* Returns %true if the given line of length @len > 0 is a comment or empty line
743 * in the source list file format. */
745 is_comment_line(const tchar *line, size_t len)
748 if (*line == T('#') || *line == T(';'))
750 if (!istspace(*line) && *line != T('\0'))
760 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
763 tchar *contents = *contents_p;
764 size_t nchars = *nchars_p;
767 for (i = 0; i < nchars; i++)
768 if (contents[i] == T('\n'))
771 /* Handle last line not terminated by a newline */
772 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
773 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
775 imagex_error(T("Out of memory!"));
778 contents[nchars] = T('\n');
779 *contents_p = contents;
787 /* Parses a file in the source list format. (See the man page for
788 * 'wimlib-imagex capture' for details on this format and the meaning.)
790 * @source_list_contents: Contents of the source list file. Note that this
791 * buffer will be modified to save memory allocations,
792 * and cannot be freed until the returned array of
793 * wimlib_capture_source's has also been freed.
795 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
798 * @nsources_ret: On success, the length of the returned array is
801 * Returns: An array of `struct wimlib_capture_source's that can be passed to
802 * the wimlib_add_image_multisource() function to specify how a WIM image is to
804 static struct wimlib_capture_source *
805 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
806 size_t *nsources_ret)
810 struct wimlib_capture_source *sources;
813 nlines = text_file_count_lines(source_list_contents_p,
814 &source_list_nchars);
818 /* Always allocate at least 1 slot, just in case the implementation of
819 * calloc() returns NULL if 0 bytes are requested. */
820 sources = calloc(nlines ?: 1, sizeof(*sources));
822 imagex_error(T("out of memory"));
825 p = *source_list_contents_p;
827 for (i = 0; i < nlines; i++) {
828 /* XXX: Could use rawmemchr() here instead, but it may not be
829 * available on all platforms. */
830 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
831 size_t len = endp - p + 1;
833 if (!is_comment_line(p, len)) {
834 if (!parse_source_list_line(p, len, &sources[j++])) {
846 /* Reads the contents of a file into memory. */
848 file_get_contents(const tchar *filename, size_t *len_ret)
855 if (tstat(filename, &stbuf) != 0) {
856 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
861 fp = tfopen(filename, T("rb"));
863 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
867 buf = malloc(len ? len : 1);
869 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
870 "contents of file \"%"TS"\""), len, filename);
873 if (fread(buf, 1, len, fp) != len) {
874 imagex_error_with_errno(T("Failed to read %zu bytes from the "
875 "file \"%"TS"\""), len, filename);
889 /* Read standard input until EOF and return the full contents in a malloc()ed
890 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
893 stdin_get_contents(size_t *len_ret)
895 /* stdin can, of course, be a pipe or other non-seekable file, so the
896 * total length of the data cannot be pre-determined */
898 size_t newlen = 1024;
902 char *p = realloc(buf, newlen);
903 size_t bytes_read, bytes_to_read;
905 imagex_error(T("out of memory while reading stdin"));
909 bytes_to_read = newlen - pos;
910 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
912 if (bytes_read != bytes_to_read) {
917 imagex_error_with_errno(T("error reading stdin"));
931 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
934 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
936 *num_tchars_ret = num_bytes;
938 #else /* !__WIN32__ */
939 /* On Windows, translate the text to UTF-16LE */
943 if (num_bytes >= 2 &&
944 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
945 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
947 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
948 * with something that looks like an ASCII character encoded as
949 * a UTF-16LE code unit. Assume the file is encoded as
950 * UTF-16LE. This is not a 100% reliable check. */
951 num_wchars = num_bytes / 2;
952 text_wstr = (wchar_t*)text;
954 /* File does not look like UTF-16LE. Assume it is encoded in
955 * the current Windows code page. I think these are always
956 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
957 * should work as expected. */
958 text_wstr = win32_mbs_to_wcs(text,
963 *num_tchars_ret = num_wchars;
965 #endif /* __WIN32__ */
969 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
974 contents = file_get_contents(filename, &num_bytes);
977 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
981 stdin_get_text_contents(size_t *num_tchars_ret)
986 contents = stdin_get_contents(&num_bytes);
989 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
992 #define TO_PERCENT(numerator, denominator) \
993 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
995 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
996 #define MEBIBYTE_MIN_NBYTES 10000000ULL
997 #define KIBIBYTE_MIN_NBYTES 10000ULL
1000 get_unit(uint64_t total_bytes, const tchar **name_ret)
1002 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1003 *name_ret = T("GiB");
1005 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1006 *name_ret = T("MiB");
1008 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1009 *name_ret = T("KiB");
1012 *name_ret = T("bytes");
1017 static struct wimlib_progress_info_scan last_scan_progress;
1020 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1022 uint64_t prev_count, cur_count;
1024 prev_count = last_scan_progress.num_nondirs_scanned +
1025 last_scan_progress.num_dirs_scanned;
1026 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1028 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1029 cur_count % 128 == 0)
1031 unsigned unit_shift;
1032 const tchar *unit_name;
1034 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1035 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1036 "%"PRIu64" directories) "),
1037 scan->num_bytes_scanned >> unit_shift,
1039 scan->num_nondirs_scanned,
1040 scan->num_dirs_scanned);
1041 last_scan_progress = *scan;
1044 /* Progress callback function passed to various wimlib functions. */
1045 static enum wimlib_progress_status
1046 imagex_progress_func(enum wimlib_progress_msg msg,
1047 union wimlib_progress_info *info,
1048 void *_ignored_context)
1050 unsigned percent_done;
1051 unsigned unit_shift;
1052 const tchar *unit_name;
1054 if (imagex_be_quiet)
1055 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1057 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1059 static bool first = true;
1061 imagex_printf(T("Writing %"TS"-compressed data "
1062 "using %u thread%"TS"\n"),
1063 wimlib_get_compression_type_string(
1064 info->write_streams.compression_type),
1065 info->write_streams.num_threads,
1066 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1070 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1071 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1072 info->write_streams.total_bytes);
1074 imagex_printf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
1075 "written (%u%% done)"),
1076 info->write_streams.completed_bytes >> unit_shift,
1078 info->write_streams.total_bytes >> unit_shift,
1081 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1082 imagex_printf(T("\n"));
1084 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1085 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1086 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1087 imagex_printf(T("\n"));
1089 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1090 info->scan.wim_target_path);
1092 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1094 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1095 switch (info->scan.status) {
1096 case WIMLIB_SCAN_DENTRY_OK:
1097 report_scan_progress(&info->scan, false);
1099 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1100 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1102 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1103 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1104 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1106 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1107 /* Symlink fixups are enabled by default. This is
1108 * mainly intended for Windows, which for some reason
1109 * uses absolute junctions (with drive letters!) in the
1110 * default installation. On UNIX-like systems, warn the
1111 * user when fixing the target of an absolute symbolic
1112 * link, so they know to disable this if they want. */
1114 imagex_printf(T("\nWARNING: Adjusted target of "
1115 "absolute symbolic link \"%"TS"\"\n"
1116 " (Use --norpfix to capture "
1117 "absolute symbolic links as-is)\n"),
1118 info->scan.cur_path);
1125 case WIMLIB_PROGRESS_MSG_SCAN_END:
1126 report_scan_progress(&info->scan, true);
1127 imagex_printf(T("\n"));
1129 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1130 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1131 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1132 info->integrity.total_bytes);
1133 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1134 "of %"PRIu64" %"TS" (%u%%) done"),
1135 info->integrity.filename,
1136 info->integrity.completed_bytes >> unit_shift,
1138 info->integrity.total_bytes >> unit_shift,
1141 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1142 imagex_printf(T("\n"));
1144 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1145 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1146 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1147 info->integrity.total_bytes);
1148 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1149 "of %"PRIu64" %"TS" (%u%%) done"),
1150 info->integrity.completed_bytes >> unit_shift,
1152 info->integrity.total_bytes >> unit_shift,
1155 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1156 imagex_printf(T("\n"));
1158 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1159 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1160 "to %"TS" \"%"TS"\"\n"),
1161 info->extract.image,
1162 info->extract.image_name,
1163 info->extract.wimfile_name,
1164 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1165 T("NTFS volume") : T("directory")),
1166 info->extract.target);
1168 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1169 if (info->extract.end_file_count >= 2000) {
1170 percent_done = TO_PERCENT(info->extract.current_file_count,
1171 info->extract.end_file_count);
1172 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1173 info->extract.current_file_count,
1174 info->extract.end_file_count, percent_done);
1175 if (info->extract.current_file_count == info->extract.end_file_count)
1176 imagex_printf(T("\n"));
1179 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1180 percent_done = TO_PERCENT(info->extract.completed_bytes,
1181 info->extract.total_bytes);
1182 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1183 imagex_printf(T("\rExtracting files: "
1184 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1185 info->extract.completed_bytes >> unit_shift,
1187 info->extract.total_bytes >> unit_shift,
1190 if (info->extract.completed_bytes >= info->extract.total_bytes)
1191 imagex_printf(T("\n"));
1193 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1194 if (info->extract.end_file_count >= 2000) {
1195 percent_done = TO_PERCENT(info->extract.current_file_count,
1196 info->extract.end_file_count);
1197 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1198 info->extract.current_file_count,
1199 info->extract.end_file_count, percent_done);
1200 if (info->extract.current_file_count == info->extract.end_file_count)
1201 imagex_printf(T("\n"));
1204 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1205 if (info->extract.total_parts != 1) {
1206 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1207 info->extract.part_number,
1208 info->extract.total_parts);
1211 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1212 percent_done = TO_PERCENT(info->split.completed_bytes,
1213 info->split.total_bytes);
1214 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1215 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1216 "%"PRIu64" %"TS" (%u%%) written\n"),
1217 info->split.part_name,
1218 info->split.cur_part_number,
1219 info->split.total_parts,
1220 info->split.completed_bytes >> unit_shift,
1222 info->split.total_bytes >> unit_shift,
1226 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1227 if (info->split.completed_bytes == info->split.total_bytes) {
1228 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1229 info->split.cur_part_number,
1230 info->split.total_parts);
1233 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1234 switch (info->update.command->op) {
1235 case WIMLIB_UPDATE_OP_DELETE:
1236 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1237 info->update.command->delete_.wim_path);
1239 case WIMLIB_UPDATE_OP_RENAME:
1240 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1241 info->update.command->rename.wim_source_path,
1242 info->update.command->rename.wim_target_path);
1244 case WIMLIB_UPDATE_OP_ADD:
1249 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1250 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1251 info->replace.path_in_wim);
1253 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1254 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1255 info->wimboot_exclude.path_in_wim);
1257 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1258 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1259 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1260 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1261 info->unmount.mounted_wim,
1262 info->unmount.mounted_image);
1264 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1265 info->unmount.mounted_wim,
1266 info->unmount.mounted_image);
1267 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1271 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1272 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1273 info->verify_image.current_image,
1274 info->verify_image.total_images);
1276 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1277 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1278 info->verify_streams.total_bytes);
1279 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1280 imagex_printf(T("\rVerifying streams: "
1281 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1282 info->verify_streams.completed_bytes >> unit_shift,
1284 info->verify_streams.total_bytes >> unit_shift,
1287 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1288 imagex_printf(T("\n"));
1293 fflush(imagex_info_file);
1294 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1298 parse_num_threads(const tchar *optarg)
1301 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1302 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1303 imagex_error(T("Number of threads must be a non-negative integer!"));
1311 parse_chunk_size(const tchar *optarg)
1314 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1315 if (chunk_size == 0) {
1316 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1317 " with optional K, M, or G suffix"));
1321 if (*tmp == T('k') || *tmp == T('K')) {
1324 } else if (*tmp == T('m') || *tmp == T('M')) {
1327 } else if (*tmp == T('g') || *tmp == T('G')) {
1331 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1332 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1336 if (chunk_size >= UINT32_MAX) {
1337 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1345 * Parse an option passed to an update command.
1347 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1350 * @option: Text string for the option (beginning with --)
1352 * @cmd: `struct wimlib_update_command' that is being constructed for
1355 * Returns true if the option was recognized; false if not.
1358 update_command_add_option(int op, const tchar *option,
1359 struct wimlib_update_command *cmd)
1361 bool recognized = true;
1363 case WIMLIB_UPDATE_OP_ADD:
1364 if (!tstrcmp(option, T("--verbose")))
1365 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1366 else if (!tstrcmp(option, T("--unix-data")))
1367 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1368 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1369 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1370 else if (!tstrcmp(option, T("--strict-acls")))
1371 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1372 else if (!tstrcmp(option, T("--dereference")))
1373 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1374 else if (!tstrcmp(option, T("--no-replace")))
1375 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1379 case WIMLIB_UPDATE_OP_DELETE:
1380 if (!tstrcmp(option, T("--force")))
1381 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1382 else if (!tstrcmp(option, T("--recursive")))
1383 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1394 /* How many nonoption arguments each `imagex update' command expects */
1395 static const unsigned update_command_num_nonoptions[] = {
1396 [WIMLIB_UPDATE_OP_ADD] = 2,
1397 [WIMLIB_UPDATE_OP_DELETE] = 1,
1398 [WIMLIB_UPDATE_OP_RENAME] = 2,
1402 update_command_add_nonoption(int op, const tchar *nonoption,
1403 struct wimlib_update_command *cmd,
1404 unsigned num_nonoptions)
1407 case WIMLIB_UPDATE_OP_ADD:
1408 if (num_nonoptions == 0)
1409 cmd->add.fs_source_path = (tchar*)nonoption;
1411 cmd->add.wim_target_path = (tchar*)nonoption;
1413 case WIMLIB_UPDATE_OP_DELETE:
1414 cmd->delete_.wim_path = (tchar*)nonoption;
1416 case WIMLIB_UPDATE_OP_RENAME:
1417 if (num_nonoptions == 0)
1418 cmd->rename.wim_source_path = (tchar*)nonoption;
1420 cmd->rename.wim_target_path = (tchar*)nonoption;
1426 * Parse a command passed on stdin to `imagex update'.
1428 * @line: Text of the command.
1429 * @len: Length of the line, including a null terminator
1432 * @command: A `struct wimlib_update_command' to fill in from the parsed
1435 * @line_number: Line number of the command, for diagnostics.
1437 * Returns true on success; returns false on parse error.
1440 parse_update_command(tchar *line, size_t len,
1441 struct wimlib_update_command *command,
1445 tchar *command_name;
1447 size_t num_nonoptions;
1449 /* Get the command name ("add", "delete", "rename") */
1450 ret = parse_string(&line, &len, &command_name);
1451 if (ret != PARSE_STRING_SUCCESS)
1454 if (!tstrcasecmp(command_name, T("add"))) {
1455 op = WIMLIB_UPDATE_OP_ADD;
1456 } else if (!tstrcasecmp(command_name, T("delete"))) {
1457 op = WIMLIB_UPDATE_OP_DELETE;
1458 } else if (!tstrcasecmp(command_name, T("rename"))) {
1459 op = WIMLIB_UPDATE_OP_RENAME;
1461 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1462 command_name, line_number);
1467 /* Parse additional options and non-options as needed */
1472 ret = parse_string(&line, &len, &next_string);
1473 if (ret == PARSE_STRING_NONE) /* End of line */
1475 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1477 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1479 if (!update_command_add_option(op, next_string, command))
1481 imagex_error(T("Unrecognized option \"%"TS"\" to "
1482 "update command \"%"TS"\" on line %zu"),
1483 next_string, command_name, line_number);
1489 if (num_nonoptions == update_command_num_nonoptions[op])
1491 imagex_error(T("Unexpected argument \"%"TS"\" in "
1492 "update command on line %zu\n"
1493 " (The \"%"TS"\" command only "
1494 "takes %zu nonoption arguments!)\n"),
1495 next_string, line_number,
1496 command_name, num_nonoptions);
1499 update_command_add_nonoption(op, next_string,
1500 command, num_nonoptions);
1505 if (num_nonoptions != update_command_num_nonoptions[op]) {
1506 imagex_error(T("Not enough arguments to update command "
1507 "\"%"TS"\" on line %zu"), command_name, line_number);
1513 static struct wimlib_update_command *
1514 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1515 size_t *num_cmds_ret)
1519 struct wimlib_update_command *cmds;
1522 nlines = text_file_count_lines(cmd_file_contents_p,
1527 /* Always allocate at least 1 slot, just in case the implementation of
1528 * calloc() returns NULL if 0 bytes are requested. */
1529 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1531 imagex_error(T("out of memory"));
1534 p = *cmd_file_contents_p;
1536 for (i = 0; i < nlines; i++) {
1537 /* XXX: Could use rawmemchr() here instead, but it may not be
1538 * available on all platforms. */
1539 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1540 size_t len = endp - p + 1;
1542 if (!is_comment_line(p, len)) {
1543 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1554 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1555 * one image from a WIM file to an NTFS volume. */
1557 imagex_apply(int argc, tchar **argv, int cmd)
1561 int image = WIMLIB_NO_IMAGE;
1563 struct wimlib_wim_info info;
1565 const tchar *wimfile;
1566 const tchar *target;
1567 const tchar *image_num_or_name = NULL;
1568 int extract_flags = 0;
1570 STRING_SET(refglobs);
1572 for_opt(c, apply_options) {
1574 case IMAGEX_CHECK_OPTION:
1575 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1577 case IMAGEX_VERBOSE_OPTION:
1578 /* No longer does anything. */
1580 case IMAGEX_REF_OPTION:
1581 ret = string_set_append(&refglobs, optarg);
1583 goto out_free_refglobs;
1585 case IMAGEX_UNIX_DATA_OPTION:
1586 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1588 case IMAGEX_NO_ACLS_OPTION:
1589 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1591 case IMAGEX_STRICT_ACLS_OPTION:
1592 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1594 case IMAGEX_NO_ATTRIBUTES_OPTION:
1595 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1597 case IMAGEX_NORPFIX_OPTION:
1598 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1600 case IMAGEX_RPFIX_OPTION:
1601 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1603 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1604 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1605 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1607 case IMAGEX_RESUME_OPTION:
1608 extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME;
1610 case IMAGEX_WIMBOOT_OPTION:
1611 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1619 if (argc != 2 && argc != 3)
1624 if (!tstrcmp(wimfile, T("-"))) {
1625 /* Attempt to apply pipable WIM from standard input. */
1627 image_num_or_name = NULL;
1630 image_num_or_name = argv[1];
1635 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1636 imagex_progress_func, NULL);
1638 goto out_free_refglobs;
1640 wimlib_get_wim_info(wim, &info);
1643 /* Image explicitly specified. */
1644 image_num_or_name = argv[1];
1645 image = wimlib_resolve_image(wim, image_num_or_name);
1646 ret = verify_image_exists(image, image_num_or_name, wimfile);
1648 goto out_wimlib_free;
1651 /* No image specified; default to image 1, but only if the WIM
1652 * contains exactly one image. */
1654 if (info.image_count != 1) {
1655 imagex_error(T("\"%"TS"\" contains %d images; "
1656 "Please select one (or all)."),
1657 wimfile, info.image_count);
1666 if (refglobs.num_strings) {
1668 imagex_error(T("Can't specify --ref when applying from stdin!"));
1670 goto out_wimlib_free;
1672 ret = wim_reference_globs(wim, &refglobs, open_flags);
1674 goto out_wimlib_free;
1679 /* Interpret a regular file or block device target as an NTFS
1683 if (tstat(target, &stbuf)) {
1684 if (errno != ENOENT) {
1685 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1688 goto out_wimlib_free;
1691 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1692 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1698 ret = wimlib_extract_image(wim, image, target, extract_flags);
1700 set_fd_to_binary_mode(STDIN_FILENO);
1701 ret = wimlib_extract_image_from_pipe_with_progress(
1706 imagex_progress_func,
1710 imagex_printf(T("Done applying WIM image.\n"));
1711 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1713 do_resource_not_found_warning(wimfile, &info, &refglobs);
1715 imagex_error(T( "If you are applying an image "
1716 "from a split pipable WIM,\n"
1717 " make sure you have "
1718 "concatenated together all parts."));
1724 string_set_destroy(&refglobs);
1728 usage(CMD_APPLY, stderr);
1730 goto out_free_refglobs;
1733 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1734 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1735 * the desired image. 'wimlib-imagex append': add a new image to an existing
1738 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1742 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1743 WIMLIB_ADD_FLAG_WINCONFIG |
1744 WIMLIB_ADD_FLAG_VERBOSE;
1745 int write_flags = 0;
1746 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1747 uint32_t chunk_size = UINT32_MAX;
1748 uint32_t solid_chunk_size = UINT32_MAX;
1749 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1750 const tchar *wimfile;
1754 const tchar *flags_element = NULL;
1757 STRING_SET(base_wimfiles);
1758 WIMStruct **base_wims;
1760 WIMStruct *template_wim;
1761 const tchar *template_wimfile = NULL;
1762 const tchar *template_image_name_or_num = NULL;
1763 int template_image = WIMLIB_NO_IMAGE;
1766 unsigned num_threads = 0;
1771 tchar *config_file = NULL;
1773 bool source_list = false;
1774 size_t source_list_nchars = 0;
1775 tchar *source_list_contents;
1776 bool capture_sources_malloced;
1777 struct wimlib_capture_source *capture_sources;
1779 bool name_defaulted;
1781 for_opt(c, capture_or_append_options) {
1783 case IMAGEX_BOOT_OPTION:
1784 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1786 case IMAGEX_CHECK_OPTION:
1787 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1788 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1790 case IMAGEX_NOCHECK_OPTION:
1791 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1793 case IMAGEX_CONFIG_OPTION:
1794 config_file = optarg;
1795 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1797 case IMAGEX_COMPRESS_OPTION:
1798 compression_type = get_compression_type(optarg);
1799 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1802 case IMAGEX_COMPRESS_SLOW_OPTION:
1803 set_compress_slow();
1805 case IMAGEX_CHUNK_SIZE_OPTION:
1806 chunk_size = parse_chunk_size(optarg);
1807 if (chunk_size == UINT32_MAX)
1810 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1811 solid_chunk_size = parse_chunk_size(optarg);
1812 if (solid_chunk_size == UINT32_MAX)
1815 case IMAGEX_SOLID_COMPRESS_OPTION:
1816 solid_ctype = get_compression_type(optarg);
1817 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1820 case IMAGEX_SOLID_OPTION:
1821 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1823 case IMAGEX_NO_SOLID_SORT_OPTION:
1824 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1826 case IMAGEX_FLAGS_OPTION:
1827 flags_element = optarg;
1829 case IMAGEX_DEREFERENCE_OPTION:
1830 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1832 case IMAGEX_VERBOSE_OPTION:
1833 /* No longer does anything. */
1835 case IMAGEX_THREADS_OPTION:
1836 num_threads = parse_num_threads(optarg);
1837 if (num_threads == UINT_MAX)
1840 case IMAGEX_REBUILD_OPTION:
1841 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1843 case IMAGEX_UNIX_DATA_OPTION:
1844 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1846 case IMAGEX_SOURCE_LIST_OPTION:
1849 case IMAGEX_NO_ACLS_OPTION:
1850 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1852 case IMAGEX_STRICT_ACLS_OPTION:
1853 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1855 case IMAGEX_RPFIX_OPTION:
1856 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1858 case IMAGEX_NORPFIX_OPTION:
1859 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1861 case IMAGEX_PIPABLE_OPTION:
1862 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1864 case IMAGEX_NOT_PIPABLE_OPTION:
1865 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1867 case IMAGEX_UPDATE_OF_OPTION:
1868 if (template_image_name_or_num) {
1869 imagex_error(T("'--update-of' can only be "
1870 "specified one time!"));
1874 colon = tstrrchr(optarg, T(':'));
1877 template_wimfile = optarg;
1879 template_image_name_or_num = colon + 1;
1881 template_wimfile = NULL;
1882 template_image_name_or_num = optarg;
1886 case IMAGEX_DELTA_FROM_OPTION:
1887 if (cmd != CMD_CAPTURE) {
1888 imagex_error(T("'--delta-from' is only "
1889 "valid for capture!"));
1892 ret = string_set_append(&base_wimfiles, optarg);
1894 goto out_free_base_wimfiles;
1895 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
1897 case IMAGEX_WIMBOOT_OPTION:
1898 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
1907 if (argc < 2 || argc > 4)
1913 /* Set default compression type and parameters. */
1916 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
1917 /* No compression type specified. Use the default. */
1919 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
1920 /* With --wimboot, default to XPRESS compression. */
1921 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
1922 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
1923 /* With --solid, default to LZMS compression. (However,
1924 * this will not affect solid resources!) */
1925 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
1927 /* Otherwise, default to LZX compression. */
1928 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
1932 if (!tstrcmp(wimfile, T("-"))) {
1933 /* Writing captured WIM to standard output. */
1935 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
1936 imagex_error("Can't write a non-pipable WIM to "
1937 "standard output! Specify --pipable\n"
1938 " if you want to create a pipable WIM "
1939 "(but read the docs first).");
1943 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1945 if (cmd == CMD_APPEND) {
1946 imagex_error(T("Using standard output for append does "
1947 "not make sense."));
1950 wim_fd = STDOUT_FILENO;
1952 imagex_info_file = stderr;
1953 set_fd_to_binary_mode(wim_fd);
1956 /* If template image was specified using --update-of=IMAGE rather
1957 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
1958 if (template_image_name_or_num && !template_wimfile) {
1959 if (base_wimfiles.num_strings == 1) {
1960 /* Capturing delta WIM based on single WIM: default to
1962 template_wimfile = base_wimfiles.strings[0];
1963 } else if (cmd == CMD_APPEND) {
1964 /* Appending to WIM: default to WIM being appended to.
1966 template_wimfile = wimfile;
1968 /* Capturing a normal (non-delta) WIM, so the WIM file
1969 * *must* be explicitly specified. */
1970 if (base_wimfiles.num_strings > 1) {
1971 imagex_error(T("For capture of delta WIM "
1972 "based on multiple existing "
1974 " '--update-of' must "
1975 "specify WIMFILE:IMAGE!"));
1977 imagex_error(T("For capture of non-delta WIM, "
1978 "'--update-of' must specify "
1987 name_defaulted = false;
1989 /* Set default name to SOURCE argument, omitting any directory
1990 * prefixes and trailing slashes. This requires making a copy
1991 * of @source. Leave some free characters at the end in case we
1992 * append a number to keep the name unique. */
1993 size_t source_name_len;
1995 source_name_len = tstrlen(source);
1996 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
1997 name = tbasename(tstrcpy(source_copy, source));
1998 name_defaulted = true;
2000 /* Image description defaults to NULL if not given. */
2007 /* Set up capture sources in source list mode */
2008 if (source[0] == T('-') && source[1] == T('\0')) {
2009 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2011 source_list_contents = file_get_text_contents(source,
2012 &source_list_nchars);
2014 if (!source_list_contents)
2017 capture_sources = parse_source_list(&source_list_contents,
2020 if (!capture_sources) {
2022 goto out_free_source_list_contents;
2024 capture_sources_malloced = true;
2026 /* Set up capture source in non-source-list mode. */
2027 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2028 capture_sources[0].fs_source_path = source;
2029 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2030 capture_sources[0].reserved = 0;
2032 capture_sources_malloced = false;
2033 source_list_contents = NULL;
2036 /* Open the existing WIM, or create a new one. */
2037 if (cmd == CMD_APPEND) {
2038 ret = wimlib_open_wim_with_progress(wimfile,
2039 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2041 imagex_progress_func,
2044 goto out_free_capture_sources;
2046 ret = wimlib_create_new_wim(compression_type, &wim);
2048 goto out_free_capture_sources;
2049 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2052 /* Set chunk size if non-default. */
2053 if (chunk_size != UINT32_MAX) {
2054 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2057 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT) &&
2058 compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2059 ret = wimlib_set_output_chunk_size(wim, 4096);
2063 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2064 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2068 if (solid_chunk_size != UINT32_MAX) {
2069 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2075 /* Detect if source is regular file or block device and set NTFS volume
2080 if (tstat(source, &stbuf) == 0) {
2081 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2082 imagex_printf(T("Capturing WIM image from NTFS "
2083 "filesystem on \"%"TS"\"\n"), source);
2084 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2087 if (errno != ENOENT) {
2088 imagex_error_with_errno(T("Failed to stat "
2089 "\"%"TS"\""), source);
2097 /* If the user did not specify an image name, and the basename of the
2098 * source already exists as an image name in the WIM file, append a
2099 * suffix to make it unique. */
2100 if (cmd == CMD_APPEND && name_defaulted) {
2101 unsigned long conflict_idx;
2102 tchar *name_end = tstrchr(name, T('\0'));
2103 for (conflict_idx = 1;
2104 wimlib_image_name_in_use(wim, name);
2107 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2111 /* If capturing a delta WIM, reference resources from the base WIMs
2112 * before adding the new image. */
2113 if (base_wimfiles.num_strings) {
2114 base_wims = calloc(base_wimfiles.num_strings,
2115 sizeof(base_wims[0]));
2116 if (base_wims == NULL) {
2117 imagex_error(T("Out of memory!"));
2122 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2123 ret = wimlib_open_wim_with_progress(
2124 base_wimfiles.strings[i], open_flags,
2125 &base_wims[i], imagex_progress_func, NULL);
2127 goto out_free_base_wims;
2131 ret = wimlib_reference_resources(wim, base_wims,
2132 base_wimfiles.num_strings, 0);
2134 goto out_free_base_wims;
2136 if (base_wimfiles.num_strings == 1) {
2137 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2138 base_wimfiles.strings[0]);
2140 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2141 base_wimfiles.num_strings);
2148 /* If capturing or appending as an update of an existing (template) image,
2149 * open the WIM if needed and parse the image index. */
2150 if (template_image_name_or_num) {
2153 if (base_wimfiles.num_strings == 1 &&
2154 template_wimfile == base_wimfiles.strings[0]) {
2155 template_wim = base_wims[0];
2156 } else if (template_wimfile == wimfile) {
2159 ret = wimlib_open_wim_with_progress(template_wimfile,
2162 imagex_progress_func,
2165 goto out_free_base_wims;
2168 template_image = wimlib_resolve_image(template_wim,
2169 template_image_name_or_num);
2171 if (template_image_name_or_num[0] == T('-')) {
2174 struct wimlib_wim_info info;
2176 wimlib_get_wim_info(template_wim, &info);
2177 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2178 if (n >= 1 && n <= info.image_count &&
2180 tmp != template_image_name_or_num + 1)
2182 template_image = info.image_count - (n - 1);
2185 ret = verify_image_exists_and_is_single(template_image,
2186 template_image_name_or_num,
2189 goto out_free_template_wim;
2191 template_wim = NULL;
2194 ret = wimlib_add_image_multisource(wim,
2201 goto out_free_template_wim;
2203 if (desc || flags_element || template_image_name_or_num) {
2204 /* User provided <DESCRIPTION> or <FLAGS> element, or an image
2205 * on which the added one is to be based has been specified with
2206 * --update-of. Get the index of the image we just
2207 * added, then use it to call the appropriate functions. */
2208 struct wimlib_wim_info info;
2210 wimlib_get_wim_info(wim, &info);
2213 ret = wimlib_set_image_descripton(wim,
2217 goto out_free_template_wim;
2220 if (flags_element) {
2221 ret = wimlib_set_image_flags(wim, info.image_count,
2224 goto out_free_template_wim;
2227 /* Reference template image if the user provided one. */
2228 if (template_image_name_or_num) {
2229 imagex_printf(T("Using image %d "
2230 "from \"%"TS"\" as template\n"),
2231 template_image, template_wimfile);
2232 ret = wimlib_reference_template_image(wim,
2238 goto out_free_template_wim;
2242 /* Write the new WIM or overwrite the existing WIM with the new image
2244 if (cmd == CMD_APPEND) {
2245 ret = wimlib_overwrite(wim, write_flags, num_threads);
2246 } else if (wimfile) {
2247 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2248 write_flags, num_threads);
2250 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2251 write_flags, num_threads);
2253 out_free_template_wim:
2254 /* template_wim may alias base_wims[0] or wim. */
2255 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2256 template_wim != wim)
2257 wimlib_free(template_wim);
2259 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2260 wimlib_free(base_wims[i]);
2264 out_free_capture_sources:
2265 if (capture_sources_malloced)
2266 free(capture_sources);
2267 out_free_source_list_contents:
2268 free(source_list_contents);
2269 out_free_base_wimfiles:
2270 string_set_destroy(&base_wimfiles);
2277 goto out_free_base_wimfiles;
2280 /* Remove image(s) from a WIM. */
2282 imagex_delete(int argc, tchar **argv, int cmd)
2285 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2286 int write_flags = 0;
2287 const tchar *wimfile;
2288 const tchar *image_num_or_name;
2293 for_opt(c, delete_options) {
2295 case IMAGEX_CHECK_OPTION:
2296 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2297 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2299 case IMAGEX_SOFT_OPTION:
2300 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2311 imagex_error(T("Must specify a WIM file"));
2313 imagex_error(T("Must specify an image"));
2317 image_num_or_name = argv[1];
2319 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2320 imagex_progress_func, NULL);
2324 image = wimlib_resolve_image(wim, image_num_or_name);
2326 ret = verify_image_exists(image, image_num_or_name, wimfile);
2328 goto out_wimlib_free;
2330 ret = wimlib_delete_image(wim, image);
2332 imagex_error(T("Failed to delete image from \"%"TS"\""),
2334 goto out_wimlib_free;
2337 ret = wimlib_overwrite(wim, write_flags, 0);
2339 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2340 "deleted"), wimfile);
2348 usage(CMD_DELETE, stderr);
2353 struct print_dentry_options {
2358 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2360 tprintf(T("%"TS"\n"), dentry->full_path);
2363 static const struct {
2366 } file_attr_flags[] = {
2367 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2368 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2369 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2370 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2371 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2372 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2373 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2374 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2375 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2376 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2377 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2378 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2379 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2380 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2381 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2384 #define TIMESTR_MAX 100
2387 timespec_to_string(const struct timespec *spec, tchar *buf)
2389 time_t t = spec->tv_sec;
2392 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2393 buf[TIMESTR_MAX - 1] = '\0';
2397 print_time(const tchar *type, const struct timespec *spec)
2399 tchar timestr[TIMESTR_MAX];
2401 timespec_to_string(spec, timestr);
2403 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2406 static void print_byte_field(const uint8_t field[], size_t len)
2409 tprintf(T("%02hhx"), *field++);
2413 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2415 tputs(T("WIM Information:"));
2416 tputs(T("----------------"));
2417 tprintf(T("Path: %"TS"\n"), wimfile);
2418 tprintf(T("GUID: 0x"));
2419 print_byte_field(info->guid, sizeof(info->guid));
2421 tprintf(T("Version: %u\n"), info->wim_version);
2422 tprintf(T("Image Count: %d\n"), info->image_count);
2423 tprintf(T("Compression: %"TS"\n"),
2424 wimlib_get_compression_type_string(info->compression_type));
2425 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2427 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2428 tprintf(T("Boot Index: %d\n"), info->boot_index);
2429 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2430 tprintf(T("Integrity Info: %"TS"\n"),
2431 info->has_integrity_table ? T("yes") : T("no"));
2432 tprintf(T("Relative path junction: %"TS"\n"),
2433 info->has_rpfix ? T("yes") : T("no"));
2434 tprintf(T("Pipable: %"TS"\n"),
2435 info->pipable ? T("yes") : T("no"));
2440 print_resource(const struct wimlib_resource_entry *resource,
2443 tprintf(T("Hash = 0x"));
2444 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2447 if (!resource->is_missing) {
2448 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2449 resource->uncompressed_size);
2450 if (resource->packed) {
2451 tprintf(T("Raw compressed size = %"PRIu64" bytes\n"),
2452 resource->raw_resource_compressed_size);
2454 tprintf(T("Raw offset in WIM = %"PRIu64" bytes\n"),
2455 resource->raw_resource_offset_in_wim);
2457 tprintf(T("Offset in raw = %"PRIu64" bytes\n"),
2460 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2461 resource->compressed_size);
2463 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2467 tprintf(T("Part Number = %u\n"), resource->part_number);
2468 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2470 tprintf(T("Flags = "));
2471 if (resource->is_compressed)
2472 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2473 if (resource->is_metadata)
2474 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2475 if (resource->is_free)
2476 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2477 if (resource->is_spanned)
2478 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2479 if (resource->packed)
2480 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2488 print_lookup_table(WIMStruct *wim)
2490 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2494 default_print_security_descriptor(const uint8_t *sd, size_t size)
2496 tprintf(T("Security Descriptor = "));
2497 print_byte_field(sd, size);
2502 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2506 "----------------------------------------------------------------------------\n"));
2507 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2508 if (dentry->dos_name)
2509 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2510 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2511 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2512 if (file_attr_flags[i].flag & dentry->attributes)
2513 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2514 file_attr_flags[i].name);
2516 if (dentry->security_descriptor) {
2517 print_security_descriptor(dentry->security_descriptor,
2518 dentry->security_descriptor_size);
2521 print_time(T("Creation Time"), &dentry->creation_time);
2522 print_time(T("Last Write Time"), &dentry->last_write_time);
2523 print_time(T("Last Access Time"), &dentry->last_access_time);
2526 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2527 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2529 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2530 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2532 if (dentry->unix_mode != 0) {
2533 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2534 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2535 dentry->unix_uid, dentry->unix_gid,
2536 dentry->unix_mode, dentry->unix_rdev);
2539 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2540 if (dentry->streams[i].stream_name) {
2541 tprintf(T("\tData stream \"%"TS"\":\n"),
2542 dentry->streams[i].stream_name);
2544 tprintf(T("\tUnnamed data stream:\n"));
2546 print_resource(&dentry->streams[i].resource, NULL);
2551 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2553 const struct print_dentry_options *options = _options;
2554 if (!options->detailed)
2555 print_dentry_full_path(dentry);
2557 print_dentry_detailed(dentry);
2561 /* Print the files contained in an image(s) in a WIM file. */
2563 imagex_dir(int argc, tchar **argv, int cmd)
2565 const tchar *wimfile;
2566 WIMStruct *wim = NULL;
2569 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2571 struct print_dentry_options options = {
2574 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2576 for_opt(c, dir_options) {
2578 case IMAGEX_PATH_OPTION:
2581 case IMAGEX_DETAILED_OPTION:
2582 options.detailed = true;
2584 case IMAGEX_ONE_FILE_ONLY_OPTION:
2585 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2595 imagex_error(T("Must specify a WIM file"));
2599 imagex_error(T("Too many arguments"));
2604 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2605 imagex_progress_func, NULL);
2610 image = wimlib_resolve_image(wim, argv[1]);
2611 ret = verify_image_exists(image, argv[1], wimfile);
2613 goto out_wimlib_free;
2615 /* No image specified; default to image 1, but only if the WIM
2616 * contains exactly one image. */
2618 struct wimlib_wim_info info;
2620 wimlib_get_wim_info(wim, &info);
2621 if (info.image_count != 1) {
2622 imagex_error(T("\"%"TS"\" contains %d images; Please "
2623 "select one (or all)."),
2624 wimfile, info.image_count);
2631 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2632 print_dentry, &options);
2639 usage(CMD_DIR, stderr);
2644 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2647 imagex_export(int argc, tchar **argv, int cmd)
2651 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2652 int write_flags = 0;
2653 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2654 const tchar *src_wimfile;
2655 const tchar *src_image_num_or_name;
2656 const tchar *dest_wimfile;
2658 const tchar *dest_name;
2659 const tchar *dest_desc;
2661 struct wimlib_wim_info src_info;
2662 WIMStruct *dest_wim;
2667 STRING_SET(refglobs);
2668 unsigned num_threads = 0;
2669 uint32_t chunk_size = UINT32_MAX;
2670 uint32_t solid_chunk_size = UINT32_MAX;
2671 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2673 for_opt(c, export_options) {
2675 case IMAGEX_BOOT_OPTION:
2676 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2678 case IMAGEX_CHECK_OPTION:
2679 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2680 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2682 case IMAGEX_NOCHECK_OPTION:
2683 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2685 case IMAGEX_COMPRESS_OPTION:
2686 compression_type = get_compression_type(optarg);
2687 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2690 case IMAGEX_COMPRESS_SLOW_OPTION:
2691 set_compress_slow();
2692 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2694 case IMAGEX_RECOMPRESS_OPTION:
2695 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2697 case IMAGEX_SOLID_OPTION:
2698 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2700 case IMAGEX_NO_SOLID_SORT_OPTION:
2701 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2703 case IMAGEX_CHUNK_SIZE_OPTION:
2704 chunk_size = parse_chunk_size(optarg);
2705 if (chunk_size == UINT32_MAX)
2708 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2709 solid_chunk_size = parse_chunk_size(optarg);
2710 if (solid_chunk_size == UINT32_MAX)
2713 case IMAGEX_SOLID_COMPRESS_OPTION:
2714 solid_ctype = get_compression_type(optarg);
2715 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2718 case IMAGEX_REF_OPTION:
2719 ret = string_set_append(&refglobs, optarg);
2721 goto out_free_refglobs;
2723 case IMAGEX_THREADS_OPTION:
2724 num_threads = parse_num_threads(optarg);
2725 if (num_threads == UINT_MAX)
2728 case IMAGEX_REBUILD_OPTION:
2729 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2731 case IMAGEX_PIPABLE_OPTION:
2732 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2734 case IMAGEX_NOT_PIPABLE_OPTION:
2735 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2737 case IMAGEX_WIMBOOT_OPTION:
2738 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2746 if (argc < 3 || argc > 5)
2748 src_wimfile = argv[0];
2749 src_image_num_or_name = argv[1];
2750 dest_wimfile = argv[2];
2751 dest_name = (argc >= 4) ? argv[3] : NULL;
2752 dest_desc = (argc >= 5) ? argv[4] : NULL;
2753 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2754 imagex_progress_func, NULL);
2756 goto out_free_refglobs;
2758 wimlib_get_wim_info(src_wim, &src_info);
2760 /* Determine if the destination is an existing file or not. If so, we
2761 * try to append the exported image(s) to it; otherwise, we create a new
2762 * WIM containing the exported image(s). Furthermore, determine if we
2763 * need to write a pipable WIM directly to standard output. */
2765 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2767 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2768 imagex_error("Can't write a non-pipable WIM to "
2769 "standard output! Specify --pipable\n"
2770 " if you want to create a pipable WIM "
2771 "(but read the docs first).");
2773 goto out_free_src_wim;
2776 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2778 dest_wimfile = NULL;
2779 dest_wim_fd = STDOUT_FILENO;
2780 imagex_info_file = stderr;
2781 set_fd_to_binary_mode(dest_wim_fd);
2784 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2786 /* Destination file exists. */
2788 if (!S_ISREG(stbuf.st_mode)) {
2789 imagex_error(T("\"%"TS"\" is not a regular file"),
2792 goto out_free_src_wim;
2794 ret = wimlib_open_wim_with_progress(dest_wimfile,
2796 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2798 imagex_progress_func,
2801 goto out_free_src_wim;
2803 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2804 /* The user specified a compression type, but we're
2805 * exporting to an existing WIM. Make sure the
2806 * specified compression type is the same as the
2807 * compression type of the existing destination WIM. */
2808 struct wimlib_wim_info dest_info;
2810 wimlib_get_wim_info(dest_wim, &dest_info);
2811 if (compression_type != dest_info.compression_type) {
2812 imagex_error(T("Cannot specify a compression type that is "
2813 "not the same as that used in the "
2814 "destination WIM"));
2816 goto out_free_dest_wim;
2822 if (errno != ENOENT) {
2823 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
2826 goto out_free_src_wim;
2829 /* dest_wimfile is not an existing file, so create a new WIM. */
2831 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2832 /* The user did not specify a compression type; default
2833 * to that of the source WIM, unless --solid or
2834 * --wimboot was specified. */
2836 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
2837 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2838 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2839 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2841 compression_type = src_info.compression_type;
2843 ret = wimlib_create_new_wim(compression_type, &dest_wim);
2845 goto out_free_src_wim;
2847 wimlib_register_progress_function(dest_wim,
2848 imagex_progress_func, NULL);
2850 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2851 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
2853 /* For --wimboot export, use small XPRESS chunks. */
2854 wimlib_set_output_chunk_size(dest_wim, 4096);
2855 } else if (compression_type == src_info.compression_type &&
2856 chunk_size == UINT32_MAX)
2858 /* Use same chunk size if compression type is the same. */
2859 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
2863 if (chunk_size != UINT32_MAX) {
2864 /* Set destination chunk size. */
2865 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
2867 goto out_free_dest_wim;
2869 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2870 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
2872 goto out_free_dest_wim;
2874 if (solid_chunk_size != UINT32_MAX) {
2875 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
2877 goto out_free_dest_wim;
2880 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
2881 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
2883 goto out_free_dest_wim;
2885 if (refglobs.num_strings) {
2886 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
2888 goto out_free_dest_wim;
2891 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
2892 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
2894 imagex_error(T("--boot specified for all-images export, but source WIM "
2895 "has no bootable image."));
2897 goto out_free_dest_wim;
2900 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
2901 dest_desc, export_flags);
2903 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
2904 do_resource_not_found_warning(src_wimfile,
2905 &src_info, &refglobs);
2907 goto out_free_dest_wim;
2911 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
2912 else if (dest_wimfile)
2913 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
2914 write_flags, num_threads);
2916 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
2917 WIMLIB_ALL_IMAGES, write_flags,
2920 wimlib_free(dest_wim);
2922 wimlib_free(src_wim);
2924 string_set_destroy(&refglobs);
2928 usage(CMD_EXPORT, stderr);
2931 goto out_free_refglobs;
2934 /* Extract files or directories from a WIM image */
2936 imagex_extract(int argc, tchar **argv, int cmd)
2943 const tchar *wimfile;
2944 const tchar *image_num_or_name;
2945 tchar *dest_dir = T(".");
2946 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
2947 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
2948 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
2949 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
2951 STRING_SET(refglobs);
2953 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
2955 for_opt(c, extract_options) {
2957 case IMAGEX_CHECK_OPTION:
2958 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2960 case IMAGEX_VERBOSE_OPTION:
2961 /* No longer does anything. */
2963 case IMAGEX_REF_OPTION:
2964 ret = string_set_append(&refglobs, optarg);
2966 goto out_free_refglobs;
2968 case IMAGEX_UNIX_DATA_OPTION:
2969 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
2971 case IMAGEX_NO_ACLS_OPTION:
2972 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
2974 case IMAGEX_STRICT_ACLS_OPTION:
2975 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
2977 case IMAGEX_NO_ATTRIBUTES_OPTION:
2978 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
2980 case IMAGEX_DEST_DIR_OPTION:
2983 case IMAGEX_TO_STDOUT_OPTION:
2984 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
2985 imagex_info_file = stderr;
2986 imagex_be_quiet = true;
2987 set_fd_to_binary_mode(STDOUT_FILENO);
2989 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
2990 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
2991 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
2993 case IMAGEX_NO_GLOBS_OPTION:
2994 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
2996 case IMAGEX_NULLGLOB_OPTION:
2997 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
2999 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3000 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3002 case IMAGEX_WIMBOOT_OPTION:
3003 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3015 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3016 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3018 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3023 image_num_or_name = argv[1];
3028 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3029 imagex_progress_func, NULL);
3031 goto out_free_refglobs;
3033 image = wimlib_resolve_image(wim, image_num_or_name);
3034 ret = verify_image_exists_and_is_single(image,
3038 goto out_wimlib_free;
3040 if (refglobs.num_strings) {
3041 ret = wim_reference_globs(wim, &refglobs, open_flags);
3043 goto out_wimlib_free;
3049 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3052 while (argc != 0 && ret == 0) {
3056 num_paths < argc && argv[num_paths][0] != T('@');
3061 ret = wimlib_extract_paths(wim, image, dest_dir,
3062 (const tchar **)argv,
3064 extract_flags | notlist_extract_flags);
3068 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3077 if (!imagex_be_quiet)
3078 imagex_printf(T("Done extracting files.\n"));
3079 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3080 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3081 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3082 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3083 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3086 T("Note: You can use the '--nullglob' "
3087 "option to ignore missing files.\n"));
3089 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3090 "files and directories\n"
3091 " are in the WIM image.\n"),
3092 get_cmd_string(CMD_DIR, false));
3093 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3094 struct wimlib_wim_info info;
3096 wimlib_get_wim_info(wim, &info);
3097 do_resource_not_found_warning(wimfile, &info, &refglobs);
3102 string_set_destroy(&refglobs);
3106 usage(CMD_EXTRACT, stderr);
3109 goto out_free_refglobs;
3112 /* Prints information about a WIM file; also can mark an image as bootable,
3113 * change the name of an image, or change the description of an image. */
3115 imagex_info(int argc, tchar **argv, int cmd)
3120 bool nocheck = false;
3121 bool header = false;
3122 bool lookup_table = false;
3124 bool short_header = true;
3125 const tchar *xml_out_file = NULL;
3126 const tchar *wimfile;
3127 const tchar *image_num_or_name;
3128 const tchar *new_name;
3129 const tchar *new_desc;
3134 struct wimlib_wim_info info;
3136 for_opt(c, info_options) {
3138 case IMAGEX_BOOT_OPTION:
3141 case IMAGEX_CHECK_OPTION:
3144 case IMAGEX_NOCHECK_OPTION:
3147 case IMAGEX_HEADER_OPTION:
3149 short_header = false;
3151 case IMAGEX_LOOKUP_TABLE_OPTION:
3152 lookup_table = true;
3153 short_header = false;
3155 case IMAGEX_XML_OPTION:
3157 short_header = false;
3159 case IMAGEX_EXTRACT_XML_OPTION:
3160 xml_out_file = optarg;
3161 short_header = false;
3163 case IMAGEX_METADATA_OPTION:
3164 imagex_error(T("The --metadata option has been removed. "
3165 "Use 'wimdir --detail' instead."));
3174 if (argc < 1 || argc > 4)
3178 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3179 new_name = (argc >= 3) ? argv[2] : NULL;
3180 new_desc = (argc >= 4) ? argv[3] : NULL;
3182 if (check && nocheck) {
3183 imagex_error(T("Can't specify both --check and --nocheck"));
3188 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3190 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3191 imagex_progress_func, NULL);
3195 wimlib_get_wim_info(wim, &info);
3197 image = wimlib_resolve_image(wim, image_num_or_name);
3198 ret = WIMLIB_ERR_INVALID_IMAGE;
3199 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3200 verify_image_exists(image, image_num_or_name, wimfile);
3202 imagex_error(T("If you would like to set the boot "
3203 "index to 0, specify image \"0\" with "
3204 "the --boot flag."));
3206 goto out_wimlib_free;
3209 if (boot && info.image_count == 0) {
3210 imagex_error(T("--boot is meaningless on a WIM with no images"));
3211 goto out_wimlib_free;
3214 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3216 imagex_error(T("Cannot specify the --boot flag "
3217 "without specifying a specific "
3218 "image in a multi-image WIM"));
3219 goto out_wimlib_free;
3222 imagex_error(T("Cannot specify the NEW_NAME "
3223 "without specifying a specific "
3224 "image in a multi-image WIM"));
3225 goto out_wimlib_free;
3229 /* Operations that print information are separated from operations that
3230 * recreate the WIM file. */
3231 if (!new_name && !boot) {
3233 /* Read-only operations */
3235 if (image == WIMLIB_NO_IMAGE) {
3236 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3237 image_num_or_name, wimfile);
3238 goto out_wimlib_free;
3241 if (image == WIMLIB_ALL_IMAGES && short_header)
3242 print_wim_information(wimfile, &info);
3245 wimlib_print_header(wim);
3248 if (info.total_parts != 1) {
3249 tfprintf(stderr, T("Warning: Only showing the lookup table "
3250 "for part %d of a %d-part WIM.\n"),
3251 info.part_number, info.total_parts);
3253 print_lookup_table(wim);
3257 ret = wimlib_extract_xml_data(wim, stdout);
3259 goto out_wimlib_free;
3265 fp = tfopen(xml_out_file, T("wb"));
3267 imagex_error_with_errno(T("Failed to open the "
3268 "file \"%"TS"\" for "
3272 goto out_wimlib_free;
3274 ret = wimlib_extract_xml_data(wim, fp);
3276 imagex_error(T("Failed to close the file "
3282 goto out_wimlib_free;
3286 wimlib_print_available_images(wim, image);
3291 /* Modification operations */
3293 if (image == WIMLIB_ALL_IMAGES)
3296 if (image == WIMLIB_NO_IMAGE && new_name) {
3297 imagex_error(T("Cannot specify new_name (\"%"TS"\") "
3298 "when using image 0"), new_name);
3300 goto out_wimlib_free;
3304 if (image == info.boot_index) {
3305 imagex_printf(T("Image %d is already marked as "
3306 "bootable.\n"), image);
3309 imagex_printf(T("Marking image %d as bootable.\n"),
3311 info.boot_index = image;
3312 ret = wimlib_set_wim_info(wim, &info,
3313 WIMLIB_CHANGE_BOOT_INDEX);
3315 goto out_wimlib_free;
3319 if (!tstrcmp(wimlib_get_image_name(wim, image), new_name))
3321 imagex_printf(T("Image %d is already named \"%"TS"\".\n"),
3325 imagex_printf(T("Changing the name of image %d to "
3326 "\"%"TS"\".\n"), image, new_name);
3327 ret = wimlib_set_image_name(wim, image, new_name);
3329 goto out_wimlib_free;
3333 const tchar *old_desc;
3334 old_desc = wimlib_get_image_description(wim, image);
3335 if (old_desc && !tstrcmp(old_desc, new_desc)) {
3336 imagex_printf(T("The description of image %d is already "
3337 "\"%"TS"\".\n"), image, new_desc);
3340 imagex_printf(T("Changing the description of image %d "
3341 "to \"%"TS"\".\n"), image, new_desc);
3342 ret = wimlib_set_image_descripton(wim, image,
3345 goto out_wimlib_free;
3349 /* Only call wimlib_overwrite() if something actually needs to
3351 if (boot || new_name || new_desc ||
3352 (check && !info.has_integrity_table) ||
3353 (nocheck && info.has_integrity_table))
3355 int write_flags = 0;
3358 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3360 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3361 ret = wimlib_overwrite(wim, write_flags, 1);
3363 imagex_printf(T("The file \"%"TS"\" was not modified "
3364 "because nothing needed to be done.\n"),
3375 usage(CMD_INFO, stderr);
3381 /* Join split WIMs into one part WIM */
3383 imagex_join(int argc, tchar **argv, int cmd)
3386 int swm_open_flags = 0;
3387 int wim_write_flags = 0;
3388 const tchar *output_path;
3391 for_opt(c, join_options) {
3393 case IMAGEX_CHECK_OPTION:
3394 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3395 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3405 imagex_error(T("Must specify one or more split WIM (.swm) "
3409 output_path = argv[0];
3410 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3415 imagex_progress_func,
3421 usage(CMD_JOIN, stderr);
3426 #if WIM_MOUNTING_SUPPORTED
3428 /* Mounts a WIM image. */
3430 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3433 int mount_flags = 0;
3435 const tchar *staging_dir = NULL;
3436 const tchar *wimfile;
3439 struct wimlib_wim_info info;
3443 STRING_SET(refglobs);
3445 if (cmd == CMD_MOUNTRW) {
3446 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3447 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3450 for_opt(c, mount_options) {
3452 case IMAGEX_ALLOW_OTHER_OPTION:
3453 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3455 case IMAGEX_CHECK_OPTION:
3456 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3458 case IMAGEX_DEBUG_OPTION:
3459 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3461 case IMAGEX_STREAMS_INTERFACE_OPTION:
3462 if (!tstrcasecmp(optarg, T("none")))
3463 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3464 else if (!tstrcasecmp(optarg, T("xattr")))
3465 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3466 else if (!tstrcasecmp(optarg, T("windows")))
3467 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3469 imagex_error(T("Unknown stream interface \"%"TS"\""),
3474 case IMAGEX_REF_OPTION:
3475 ret = string_set_append(&refglobs, optarg);
3477 goto out_free_refglobs;
3479 case IMAGEX_STAGING_DIR_OPTION:
3480 staging_dir = optarg;
3482 case IMAGEX_UNIX_DATA_OPTION:
3483 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3491 if (argc != 2 && argc != 3)
3496 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3497 imagex_progress_func, NULL);
3499 goto out_free_refglobs;
3501 wimlib_get_wim_info(wim, &info);
3504 /* Image explicitly specified. */
3505 image = wimlib_resolve_image(wim, argv[1]);
3507 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3511 /* No image specified; default to image 1, but only if the WIM
3512 * contains exactly one image. */
3514 if (info.image_count != 1) {
3515 imagex_error(T("\"%"TS"\" contains %d images; Please "
3516 "select one."), wimfile, info.image_count);
3524 if (refglobs.num_strings) {
3525 ret = wim_reference_globs(wim, &refglobs, open_flags);
3530 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3532 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3534 image, wimfile, dir);
3539 string_set_destroy(&refglobs);
3545 goto out_free_refglobs;
3547 #endif /* WIM_MOUNTING_SUPPORTED */
3549 /* Rebuild a WIM file */
3551 imagex_optimize(int argc, tchar **argv, int cmd)
3554 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3555 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3556 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3557 uint32_t chunk_size = UINT32_MAX;
3558 uint32_t solid_chunk_size = UINT32_MAX;
3559 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3562 const tchar *wimfile;
3565 unsigned num_threads = 0;
3567 for_opt(c, optimize_options) {
3569 case IMAGEX_CHECK_OPTION:
3570 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3571 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3573 case IMAGEX_NOCHECK_OPTION:
3574 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3576 case IMAGEX_COMPRESS_OPTION:
3577 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3578 compression_type = get_compression_type(optarg);
3579 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3582 case IMAGEX_COMPRESS_SLOW_OPTION:
3583 set_compress_slow();
3584 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3586 case IMAGEX_RECOMPRESS_OPTION:
3587 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3589 case IMAGEX_CHUNK_SIZE_OPTION:
3590 chunk_size = parse_chunk_size(optarg);
3591 if (chunk_size == UINT32_MAX)
3594 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3595 solid_chunk_size = parse_chunk_size(optarg);
3596 if (solid_chunk_size == UINT32_MAX)
3599 case IMAGEX_SOLID_COMPRESS_OPTION:
3600 solid_ctype = get_compression_type(optarg);
3601 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3604 case IMAGEX_SOLID_OPTION:
3605 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3606 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3608 case IMAGEX_NO_SOLID_SORT_OPTION:
3609 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3611 case IMAGEX_THREADS_OPTION:
3612 num_threads = parse_num_threads(optarg);
3613 if (num_threads == UINT_MAX)
3616 case IMAGEX_PIPABLE_OPTION:
3617 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3619 case IMAGEX_NOT_PIPABLE_OPTION:
3620 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3634 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3635 imagex_progress_func, NULL);
3639 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3640 /* Change compression type. */
3641 ret = wimlib_set_output_compression_type(wim, compression_type);
3643 goto out_wimlib_free;
3646 if (chunk_size != UINT32_MAX) {
3647 /* Change chunk size. */
3648 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3650 goto out_wimlib_free;
3652 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3653 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3655 goto out_wimlib_free;
3657 if (solid_chunk_size != UINT32_MAX) {
3658 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3660 goto out_wimlib_free;
3663 old_size = file_get_size(wimfile);
3664 tprintf(T("\"%"TS"\" original size: "), wimfile);
3666 tputs(T("Unknown"));
3668 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3670 ret = wimlib_overwrite(wim, write_flags, num_threads);
3672 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3673 goto out_wimlib_free;
3676 new_size = file_get_size(wimfile);
3677 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3679 tputs(T("Unknown"));
3681 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3683 tfputs(T("Space saved: "), stdout);
3684 if (new_size != -1 && old_size != -1) {
3685 tprintf(T("%lld KiB\n"),
3686 ((long long)old_size - (long long)new_size) >> 10);
3688 tputs(T("Unknown"));
3697 usage(CMD_OPTIMIZE, stderr);
3703 /* Split a WIM into a spanned set */
3705 imagex_split(int argc, tchar **argv, int cmd)
3709 int write_flags = 0;
3710 unsigned long part_size;
3715 for_opt(c, split_options) {
3717 case IMAGEX_CHECK_OPTION:
3718 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3719 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3731 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3732 if (tmp == argv[2] || *tmp) {
3733 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3734 imagex_error(T("The part size must be an integer or "
3735 "floating-point number of megabytes."));
3738 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3739 imagex_progress_func, NULL);
3743 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3749 usage(CMD_SPLIT, stderr);
3755 #if WIM_MOUNTING_SUPPORTED
3756 /* Unmounts a mounted WIM image. */
3758 imagex_unmount(int argc, tchar **argv, int cmd)
3761 int unmount_flags = 0;
3764 for_opt(c, unmount_options) {
3766 case IMAGEX_COMMIT_OPTION:
3767 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3769 case IMAGEX_CHECK_OPTION:
3770 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3772 case IMAGEX_REBUILD_OPTION:
3773 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3775 case IMAGEX_LAZY_OPTION:
3776 case IMAGEX_FORCE_OPTION:
3777 /* Now, unmount is lazy by default. However, committing
3778 * the image will fail with
3779 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3780 * file descriptors on the WIM image. The
3781 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3782 * descriptors to be closed. */
3783 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3785 case IMAGEX_NEW_IMAGE_OPTION:
3786 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
3797 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
3798 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
3799 imagex_error(T("--new-image is meaningless "
3800 "without --commit also specified!"));
3805 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
3806 imagex_progress_func, NULL);
3808 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
3809 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
3811 "\tNote: Use --commit --force to force changes "
3812 "to be committed, regardless\n"
3813 "\t of open files.\n"));
3820 usage(CMD_UNMOUNT, stderr);
3825 #endif /* WIM_MOUNTING_SUPPORTED */
3828 * Add, delete, or rename files in a WIM image.
3831 imagex_update(int argc, tchar **argv, int cmd)
3833 const tchar *wimfile;
3837 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3838 int write_flags = 0;
3839 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
3840 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
3841 WIMLIB_ADD_FLAG_VERBOSE |
3842 WIMLIB_ADD_FLAG_WINCONFIG;
3843 int default_delete_flags = 0;
3844 unsigned num_threads = 0;
3846 tchar *cmd_file_contents;
3847 size_t cmd_file_nchars;
3848 struct wimlib_update_command *cmds;
3850 tchar *command_str = NULL;
3851 tchar *config_file = NULL;
3852 tchar *wimboot_config = NULL;
3854 for_opt(c, update_options) {
3856 /* Generic or write options */
3857 case IMAGEX_THREADS_OPTION:
3858 num_threads = parse_num_threads(optarg);
3859 if (num_threads == UINT_MAX)
3862 case IMAGEX_CHECK_OPTION:
3863 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3864 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3866 case IMAGEX_REBUILD_OPTION:
3867 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
3869 case IMAGEX_COMMAND_OPTION:
3871 imagex_error(T("--command may only be specified "
3872 "one time. Please provide\n"
3873 " the update commands "
3874 "on standard input instead."));
3877 command_str = tstrdup(optarg);
3879 imagex_error(T("Out of memory!"));
3883 case IMAGEX_WIMBOOT_CONFIG_OPTION:
3884 wimboot_config = optarg;
3886 /* Default delete options */
3887 case IMAGEX_FORCE_OPTION:
3888 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
3890 case IMAGEX_RECURSIVE_OPTION:
3891 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
3894 /* Global add option */
3895 case IMAGEX_CONFIG_OPTION:
3896 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
3897 config_file = optarg;
3900 /* Default add options */
3901 case IMAGEX_VERBOSE_OPTION:
3902 /* No longer does anything. */
3904 case IMAGEX_DEREFERENCE_OPTION:
3905 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
3907 case IMAGEX_UNIX_DATA_OPTION:
3908 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
3910 case IMAGEX_NO_ACLS_OPTION:
3911 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
3913 case IMAGEX_STRICT_ACLS_OPTION:
3914 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
3916 case IMAGEX_NO_REPLACE_OPTION:
3917 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
3926 if (argc != 1 && argc != 2)
3930 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3931 imagex_progress_func, NULL);
3933 goto out_free_command_str;
3936 /* Image explicitly specified. */
3937 image = wimlib_resolve_image(wim, argv[1]);
3938 ret = verify_image_exists_and_is_single(image, argv[1],
3941 goto out_wimlib_free;
3943 /* No image specified; default to image 1, but only if the WIM
3944 * contains exactly one image. */
3945 struct wimlib_wim_info info;
3947 wimlib_get_wim_info(wim, &info);
3948 if (info.image_count != 1) {
3949 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
3950 wimfile, info.image_count);
3957 /* Read update commands from standard input, or the command string if
3960 cmd_file_contents = NULL;
3961 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
3965 goto out_free_cmd_file_contents;
3967 } else if (!wimboot_config) {
3968 if (isatty(STDIN_FILENO)) {
3969 tputs(T("Reading update commands from standard input..."));
3970 recommend_man_page(CMD_UPDATE, stdout);
3972 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
3973 if (!cmd_file_contents) {
3975 goto out_wimlib_free;
3978 /* Parse the update commands */
3979 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
3983 goto out_free_cmd_file_contents;
3986 cmd_file_contents = NULL;
3991 /* Set default flags and capture config on the update commands */
3992 for (size_t i = 0; i < num_cmds; i++) {
3993 switch (cmds[i].op) {
3994 case WIMLIB_UPDATE_OP_ADD:
3995 cmds[i].add.add_flags |= default_add_flags;
3996 cmds[i].add.config_file = config_file;
3998 case WIMLIB_UPDATE_OP_DELETE:
3999 cmds[i].delete_.delete_flags |= default_delete_flags;
4006 /* Execute the update commands */
4007 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4011 if (wimboot_config) {
4012 /* --wimboot-config=FILE is short for an
4013 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4015 struct wimlib_update_command cmd;
4017 cmd.op = WIMLIB_UPDATE_OP_ADD;
4018 cmd.add.fs_source_path = wimboot_config;
4019 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4020 cmd.add.config_file = NULL;
4021 cmd.add.add_flags = 0;
4023 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4028 /* Overwrite the updated WIM */
4029 ret = wimlib_overwrite(wim, write_flags, num_threads);
4032 out_free_cmd_file_contents:
4033 free(cmd_file_contents);
4036 out_free_command_str:
4041 usage(CMD_UPDATE, stderr);
4044 goto out_free_command_str;
4047 /* Verify a WIM file. */
4049 imagex_verify(int argc, tchar **argv, int cmd)
4052 const tchar *wimfile;
4054 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4055 int verify_flags = 0;
4056 STRING_SET(refglobs);
4059 for_opt(c, verify_options) {
4061 case IMAGEX_REF_OPTION:
4062 ret = string_set_append(&refglobs, optarg);
4064 goto out_free_refglobs;
4066 case IMAGEX_NOCHECK_OPTION:
4067 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4079 imagex_error(T("Must specify a WIM file!"));
4081 imagex_error(T("At most one WIM file can be specified!"));
4087 ret = wimlib_open_wim_with_progress(wimfile,
4090 imagex_progress_func,
4093 goto out_free_refglobs;
4095 ret = wim_reference_globs(wim, &refglobs, open_flags);
4097 goto out_wimlib_free;
4099 ret = wimlib_verify_wim(wim, verify_flags);
4101 tputc(T('\n'), stderr);
4102 imagex_error(T("\"%"TS"\" failed verification!"),
4104 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4105 refglobs.num_strings == 0)
4107 imagex_printf(T("Note: if this WIM file is not standalone, "
4108 "use the --ref option to specify the other parts.\n"));
4111 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4118 string_set_destroy(&refglobs);
4122 usage(CMD_VERIFY, stderr);
4124 goto out_free_refglobs;
4127 struct imagex_command {
4129 int (*func)(int argc, tchar **argv, int cmd);
4132 static const struct imagex_command imagex_commands[] = {
4133 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4134 [CMD_APPLY] = {T("apply"), imagex_apply},
4135 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4136 [CMD_DELETE] = {T("delete"), imagex_delete},
4137 [CMD_DIR ] = {T("dir"), imagex_dir},
4138 [CMD_EXPORT] = {T("export"), imagex_export},
4139 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4140 [CMD_INFO] = {T("info"), imagex_info},
4141 [CMD_JOIN] = {T("join"), imagex_join},
4142 #if WIM_MOUNTING_SUPPORTED
4143 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4144 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4146 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4147 [CMD_SPLIT] = {T("split"), imagex_split},
4148 #if WIM_MOUNTING_SUPPORTED
4149 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4151 [CMD_UPDATE] = {T("update"), imagex_update},
4152 [CMD_VERIFY] = {T("verify"), imagex_verify},
4157 /* Can be a directory or source list file. But source list file is probably
4158 * a rare use case, so just say directory. */
4159 # define SOURCE_STR T("DIRECTORY")
4161 /* Can only be a directory */
4162 # define TARGET_STR T("DIRECTORY")
4165 /* Can be a directory, NTFS volume, or source list file. */
4166 # define SOURCE_STR T("SOURCE")
4168 /* Can be a directory or NTFS volume. */
4169 # define TARGET_STR T("TARGET")
4173 static const tchar *usage_strings[] = {
4176 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4177 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4178 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4179 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4180 " [--wimboot] [--unix-data] [--dereference]\n"
4184 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4185 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4186 " [--no-attributes] [--rpfix] [--norpfix]\n"
4187 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4191 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4192 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4193 " [--config=FILE] [--threads=NUM_THREADS]\n"
4194 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4195 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4196 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4200 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4204 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4208 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4209 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4210 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4211 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4212 " [--wimboot] [--solid]\n"
4216 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4217 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4218 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4219 " [--no-attributes] [--include-invalid-names]\n"
4220 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4224 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4225 " [--boot] [--check] [--nocheck] [--xml]\n"
4226 " [--extract-xml FILE] [--header] [--lookup-table]\n"
4230 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4232 #if WIM_MOUNTING_SUPPORTED
4235 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4236 " [--check] [--streams-interface=INTERFACE]\n"
4237 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4241 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4242 " [--check] [--streams-interface=INTERFACE]\n"
4243 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4249 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4250 " [--check] [--nocheck] [--solid]\n"
4255 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4257 #if WIM_MOUNTING_SUPPORTED
4260 " %"TS" DIRECTORY\n"
4261 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4266 " %"TS" WIMFILE [IMAGE]\n"
4267 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4268 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4269 " [--command=STRING] [--wimboot-config=FILE]\n"
4274 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4278 static const tchar *invocation_name;
4279 static int invocation_cmd = CMD_NONE;
4281 static const tchar *get_cmd_string(int cmd, bool nospace)
4283 static tchar buf[50];
4284 if (cmd == CMD_NONE) {
4285 return T("wimlib-imagex");
4286 } else if (invocation_cmd != CMD_NONE) {
4287 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4289 const tchar *format;
4292 format = T("%"TS"-%"TS"");
4294 format = T("%"TS" %"TS"");
4295 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4303 static const tchar *s =
4305 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4306 "Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers\n"
4307 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4308 "This is free software: you are free to change and redistribute it.\n"
4309 "There is NO WARRANTY, to the extent permitted by law.\n"
4311 "Report bugs to "PACKAGE_BUGREPORT".\n"
4318 help_or_version(int argc, tchar **argv, int cmd)
4323 for (i = 1; i < argc; i++) {
4325 if (p[0] == T('-') && p[1] == T('-')) {
4327 if (!tstrcmp(p, T("help"))) {
4328 if (cmd == CMD_NONE)
4333 } else if (!tstrcmp(p, T("version"))) {
4342 print_usage_string(int cmd, FILE *fp)
4344 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4348 recommend_man_page(int cmd, FILE *fp)
4350 const tchar *format_str;
4352 format_str = T("Some uncommon options are not listed;\n"
4353 "See %"TS".pdf in the doc directory for more details.\n");
4355 format_str = T("Some uncommon options are not listed;\n"
4356 "Try `man %"TS"' for more details.\n");
4358 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4362 usage(int cmd, FILE *fp)
4364 tfprintf(fp, T("Usage:\n"));
4365 print_usage_string(cmd, fp);
4366 tfprintf(fp, T("\n"));
4367 recommend_man_page(cmd, fp);
4373 tfprintf(fp, T("Usage:\n"));
4374 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4375 print_usage_string(cmd, fp);
4376 tfprintf(fp, T("\n"));
4378 static const tchar *extra =
4381 " %"TS" --version\n"
4384 tfprintf(fp, extra, invocation_name, invocation_name);
4386 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4387 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4388 "For some commands IMAGE may be \"all\".\n"
4390 recommend_man_page(CMD_NONE, fp);
4393 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4394 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4395 * something else), while on Windows the command arguments will be UTF-16LE
4396 * encoded 'wchar_t' strings. */
4399 wmain(int argc, wchar_t **argv, wchar_t **envp)
4401 main(int argc, char **argv)
4408 imagex_info_file = stdout;
4409 invocation_name = tbasename(argv[0]);
4412 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4413 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4417 setlocale(LC_ALL, "");
4418 codeset = nl_langinfo(CODESET);
4419 if (!strstr(codeset, "UTF-8") &&
4420 !strstr(codeset, "UTF8") &&
4421 !strstr(codeset, "utf-8") &&
4422 !strstr(codeset, "utf8"))
4425 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4426 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4427 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4428 " to any value to force wimlib to use UTF-8.\n",
4434 #endif /* !__WIN32__ */
4437 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4438 if (igcase != NULL) {
4439 if (!tstrcmp(igcase, T("no")) ||
4440 !tstrcmp(igcase, T("0")))
4441 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4442 else if (!tstrcmp(igcase, T("yes")) ||
4443 !tstrcmp(igcase, T("1")))
4444 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4447 "WARNING: Ignoring unknown setting of "
4448 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4453 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4455 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4456 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4457 for (int i = 0; i < CMD_MAX; i++) {
4458 if (!tstrcmp(invocation_name + 3,
4459 imagex_commands[i].name))
4468 /* Unless already known from the invocation name, determine which
4469 * command was specified. */
4470 if (cmd == CMD_NONE) {
4472 imagex_error(T("No command specified!\n"));
4476 for (int i = 0; i < CMD_MAX; i++) {
4477 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4482 if (cmd != CMD_NONE) {
4488 /* Handle --help and --version. --help can be either for the program as
4489 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4490 * CMD_NONE). Note: help_or_version() will not return if a --help or
4491 * --version argument was found. */
4492 help_or_version(argc, argv, cmd);
4494 /* Bail if a valid command was not specified. */
4495 if (cmd == CMD_NONE) {
4496 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4501 /* Enable warning and error messages in wimlib to be more user-friendly.
4503 wimlib_set_print_errors(true);
4505 /* Initialize wimlib. */
4506 ret = wimlib_global_init(init_flags);
4508 goto out_check_status;
4510 /* Call the command handler function. */
4511 ret = imagex_commands[cmd].func(argc, argv, cmd);
4513 /* Check for error writing to standard output, especially since for some
4514 * commands, writing to standard output is part of the program's actual
4515 * behavior and not just for informational purposes. */
4516 if (ferror(stdout) || fclose(stdout)) {
4517 imagex_error_with_errno(T("error writing to standard output"));
4522 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4523 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4524 * error code from which an error message can be printed. */
4526 imagex_error(T("Exiting with error code %d:\n"
4528 wimlib_get_error_string(ret));
4529 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4530 imagex_error_with_errno(T("errno"));
4532 /* Make wimlib free any resources it's holding (although this is not
4533 * strictly necessary because the process is ending anyway). */
4534 wimlib_global_cleanup();