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,
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_BLOBS_OPTION},
324 {T("blobs"), no_argument, NULL, IMAGEX_BLOBS_OPTION},
325 {T("metadata"), no_argument, NULL, IMAGEX_METADATA_OPTION},
326 {T("xml"), no_argument, NULL, IMAGEX_XML_OPTION},
330 static const struct option join_options[] = {
331 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
335 static const struct option mount_options[] = {
336 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
337 {T("debug"), no_argument, NULL, IMAGEX_DEBUG_OPTION},
338 {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
339 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
340 {T("staging-dir"), required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
341 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
342 {T("allow-other"), no_argument, NULL, IMAGEX_ALLOW_OTHER_OPTION},
346 static const struct option optimize_options[] = {
347 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
348 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
349 {T("no-check"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
350 {T("compress"), required_argument, NULL, IMAGEX_COMPRESS_OPTION},
351 {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
352 {T("compress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
353 {T("recompress-slow"), no_argument, NULL, IMAGEX_COMPRESS_SLOW_OPTION},
354 {T("chunk-size"), required_argument, NULL, IMAGEX_CHUNK_SIZE_OPTION},
355 {T("solid"), no_argument, NULL, IMAGEX_SOLID_OPTION},
356 {T("pack-streams"),no_argument, NULL, IMAGEX_SOLID_OPTION},
357 {T("solid-compress"),required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
358 {T("pack-compress"), required_argument, NULL, IMAGEX_SOLID_COMPRESS_OPTION},
359 {T("solid-chunk-size"),required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
360 {T("pack-chunk-size"), required_argument, NULL, IMAGEX_SOLID_CHUNK_SIZE_OPTION},
361 {T("no-solid-sort"), no_argument, NULL, IMAGEX_NO_SOLID_SORT_OPTION},
362 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
363 {T("pipable"), no_argument, NULL, IMAGEX_PIPABLE_OPTION},
364 {T("not-pipable"), no_argument, NULL, IMAGEX_NOT_PIPABLE_OPTION},
368 static const struct option split_options[] = {
369 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
373 static const struct option unmount_options[] = {
374 {T("commit"), no_argument, NULL, IMAGEX_COMMIT_OPTION},
375 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
376 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
377 {T("lazy"), no_argument, NULL, IMAGEX_LAZY_OPTION},
378 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
379 {T("new-image"), no_argument, NULL, IMAGEX_NEW_IMAGE_OPTION},
383 static const struct option update_options[] = {
384 /* Careful: some of the options here set the defaults for update
385 * commands, but the flags given to an actual update command (and not to
386 * `imagex update' itself are also handled in
387 * update_command_add_option(). */
388 {T("threads"), required_argument, NULL, IMAGEX_THREADS_OPTION},
389 {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
390 {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
391 {T("command"), required_argument, NULL, IMAGEX_COMMAND_OPTION},
392 {T("wimboot-config"), required_argument, NULL, IMAGEX_WIMBOOT_CONFIG_OPTION},
394 /* Default delete options */
395 {T("force"), no_argument, NULL, IMAGEX_FORCE_OPTION},
396 {T("recursive"), no_argument, NULL, IMAGEX_RECURSIVE_OPTION},
398 /* Global add option */
399 {T("config"), required_argument, NULL, IMAGEX_CONFIG_OPTION},
401 /* Default add options */
402 {T("verbose"), no_argument, NULL, IMAGEX_VERBOSE_OPTION},
403 {T("dereference"), no_argument, NULL, IMAGEX_DEREFERENCE_OPTION},
404 {T("unix-data"), no_argument, NULL, IMAGEX_UNIX_DATA_OPTION},
405 {T("noacls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
406 {T("no-acls"), no_argument, NULL, IMAGEX_NO_ACLS_OPTION},
407 {T("strict-acls"), no_argument, NULL, IMAGEX_STRICT_ACLS_OPTION},
408 {T("no-replace"), no_argument, NULL, IMAGEX_NO_REPLACE_OPTION},
413 static const struct option verify_options[] = {
414 {T("ref"), required_argument, NULL, IMAGEX_REF_OPTION},
415 {T("nocheck"), no_argument, NULL, IMAGEX_NOCHECK_OPTION},
421 # define _format_attribute(type, format_str, args_start) \
422 __attribute__((format(type, format_str, args_start)))
424 # define _format_attribute(type, format_str, args_start)
427 /* Print formatted error message to stderr. */
428 static void _format_attribute(printf, 1, 2)
429 imagex_error(const tchar *format, ...)
432 va_start(va, format);
433 tfputs(T("ERROR: "), stderr);
434 tvfprintf(stderr, format, va);
435 tputc(T('\n'), stderr);
439 /* Print formatted error message to stderr. */
440 static void _format_attribute(printf, 1, 2)
441 imagex_error_with_errno(const tchar *format, ...)
443 int errno_save = errno;
445 va_start(va, format);
446 tfputs(T("ERROR: "), stderr);
447 tvfprintf(stderr, format, va);
448 tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
453 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
455 if (image == WIMLIB_NO_IMAGE) {
456 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
457 " Please specify a 1-based image index or "
458 "image name. To list the images\n"
459 " contained in the WIM archive, run\n"
461 " %"TS" \"%"TS"\"\n"),
462 image_name, wim_name,
463 get_cmd_string(CMD_INFO, false), wim_name);
464 return WIMLIB_ERR_INVALID_IMAGE;
470 verify_image_is_single(int image)
472 if (image == WIMLIB_ALL_IMAGES) {
473 imagex_error(T("Cannot specify all images for this action!"));
474 return WIMLIB_ERR_INVALID_IMAGE;
480 verify_image_exists_and_is_single(int image, const tchar *image_name,
481 const tchar *wim_name)
484 ret = verify_image_exists(image, image_name, wim_name);
486 ret = verify_image_is_single(image);
491 print_available_compression_types(FILE *fp)
493 static const tchar *s =
495 "Available compression types:\n"
498 " xpress (alias: \"fast\")\n"
499 " lzx (alias: \"maximum\") (default for capture)\n"
500 " lzms (alias: \"recovery\")\n"
506 /* Parse the argument to --compress */
508 get_compression_type(tchar *optarg)
511 unsigned int compression_level = 0;
514 plevel = tstrchr(optarg, T(':'));
520 ultmp = tstrtoul(plevel, &ptmp, 10);
521 if (ultmp >= UINT_MAX || ultmp == 0 || *ptmp || ptmp == plevel) {
522 imagex_error(T("Compression level must be a positive integer! "
523 "e.g. --compress=lzx:80"));
524 return WIMLIB_COMPRESSION_TYPE_INVALID;
526 compression_level = ultmp;
529 if (!tstrcasecmp(optarg, T("maximum")) ||
530 !tstrcasecmp(optarg, T("lzx")) ||
531 !tstrcasecmp(optarg, T("max")))
532 ctype = WIMLIB_COMPRESSION_TYPE_LZX;
533 else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress")))
534 ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
535 else if (!tstrcasecmp(optarg, T("recovery")) || !tstrcasecmp(optarg, T("lzms")))
536 ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
537 else if (!tstrcasecmp(optarg, T("none")))
538 ctype = WIMLIB_COMPRESSION_TYPE_NONE;
540 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
541 print_available_compression_types(stderr);
542 return WIMLIB_COMPRESSION_TYPE_INVALID;
545 if (compression_level != 0)
546 wimlib_set_default_compression_level(ctype, compression_level);
551 set_compress_slow(void)
554 fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
555 " Use the '--compress=TYPE:LEVEL' option instead.\n");
557 wimlib_set_default_compression_level(-1, 100);
561 const tchar **strings;
562 unsigned num_strings;
563 unsigned num_alloc_strings;
566 #define STRING_SET_INITIALIZER \
567 { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
569 #define STRING_SET(_strings) \
570 struct string_set _strings = STRING_SET_INITIALIZER
573 string_set_append(struct string_set *set, const tchar *glob)
575 unsigned num_alloc_strings = set->num_alloc_strings;
577 if (set->num_strings == num_alloc_strings) {
578 const tchar **new_strings;
580 num_alloc_strings += 4;
581 new_strings = realloc(set->strings,
582 sizeof(set->strings[0]) * num_alloc_strings);
584 imagex_error(T("Out of memory!"));
587 set->strings = new_strings;
588 set->num_alloc_strings = num_alloc_strings;
590 set->strings[set->num_strings++] = glob;
595 string_set_destroy(struct string_set *set)
601 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
603 return wimlib_reference_resource_files(wim, set->strings,
605 WIMLIB_REF_FLAG_GLOB_ENABLE,
610 do_resource_not_found_warning(const tchar *wimfile,
611 const struct wimlib_wim_info *info,
612 const struct string_set *refglobs)
614 if (info->total_parts > 1) {
615 if (refglobs->num_strings == 0) {
616 imagex_error(T("\"%"TS"\" is part of a split WIM. "
617 "Use --ref to specify the other parts."),
620 imagex_error(T("Perhaps the '--ref' argument did not "
621 "specify all other parts of the split "
625 imagex_error(T("If this is a delta WIM, use the --ref argument "
626 "to specify the WIM(s) on which it is based."));
630 /* Returns the size of a file given its name, or -1 if the file does not exist
631 * or its size cannot be determined. */
633 file_get_size(const tchar *filename)
636 if (tstat(filename, &st) == 0)
643 PARSE_STRING_SUCCESS = 0,
644 PARSE_STRING_FAILURE = 1,
645 PARSE_STRING_NONE = 2,
649 * Parses a string token from an array of characters.
651 * Tokens are either whitespace-delimited, or double or single-quoted.
653 * @line_p: Pointer to the pointer to the line of data. Will be updated
654 * to point past the string token iff the return value is
655 * PARSE_STRING_SUCCESS. If *len_p > 0, (*line_p)[*len_p - 1] must
658 * @len_p: @len_p initially stores the length of the line of data, which may
659 * be 0, and it will be updated to the number of bytes remaining in
660 * the line iff the return value is PARSE_STRING_SUCCESS.
662 * @fn_ret: Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
663 * parsed string token will be returned here.
665 * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
666 * PARSE_STRING_FAILURE if the data was invalid due to a missing
667 * closing quote; or PARSE_STRING_NONE if the line ended before the
668 * beginning of a string token was found.
671 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
674 tchar *line = *line_p;
678 /* Skip leading whitespace */
681 return PARSE_STRING_NONE;
682 if (!istspace(*line) && *line != T('\0'))
688 if (quote_char == T('"') || quote_char == T('\'')) {
693 line = tmemchr(line, quote_char, len);
695 imagex_error(T("Missing closing quote: %"TS), fn - 1);
696 return PARSE_STRING_FAILURE;
699 /* Unquoted string. Go until whitespace. Line is terminated
700 * by '\0', so no need to check 'len'. */
704 } while (!istspace(*line) && *line != T('\0'));
711 return PARSE_STRING_SUCCESS;
714 /* Parses a line of data (not an empty line or comment) in the source list file
715 * format. (See the man page for 'wimlib-imagex capture' for details on this
716 * format and the meaning.)
718 * @line: Line of data to be parsed. line[len - 1] must be '\0', unless
719 * len == 0. The data in @line will be modified by this function call.
721 * @len: Length of the line of data.
723 * @source: On success, the capture source and target described by the line is
724 * written into this destination. Note that it will contain pointers
725 * to data in the @line array.
727 * Returns true if the line was valid; false otherwise. */
729 parse_source_list_line(tchar *line, size_t len,
730 struct wimlib_capture_source *source)
734 ret = parse_string(&line, &len, &source->fs_source_path);
735 if (ret != PARSE_STRING_SUCCESS)
737 ret = parse_string(&line, &len, &source->wim_target_path);
738 if (ret == PARSE_STRING_NONE)
739 source->wim_target_path = source->fs_source_path;
740 return ret != PARSE_STRING_FAILURE;
743 /* Returns %true if the given line of length @len > 0 is a comment or empty line
744 * in the source list file format. */
746 is_comment_line(const tchar *line, size_t len)
749 if (*line == T('#') || *line == T(';'))
751 if (!istspace(*line) && *line != T('\0'))
761 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
764 tchar *contents = *contents_p;
765 size_t nchars = *nchars_p;
768 for (i = 0; i < nchars; i++)
769 if (contents[i] == T('\n'))
772 /* Handle last line not terminated by a newline */
773 if (nchars != 0 && contents[nchars - 1] != T('\n')) {
774 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
776 imagex_error(T("Out of memory!"));
779 contents[nchars] = T('\n');
780 *contents_p = contents;
788 /* Parses a file in the source list format. (See the man page for
789 * 'wimlib-imagex capture' for details on this format and the meaning.)
791 * @source_list_contents: Contents of the source list file. Note that this
792 * buffer will be modified to save memory allocations,
793 * and cannot be freed until the returned array of
794 * wimlib_capture_source's has also been freed.
796 * @source_list_nbytes: Number of bytes of data in the @source_list_contents
799 * @nsources_ret: On success, the length of the returned array is
802 * Returns: An array of `struct wimlib_capture_source's that can be passed to
803 * the wimlib_add_image_multisource() function to specify how a WIM image is to
805 static struct wimlib_capture_source *
806 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
807 size_t *nsources_ret)
811 struct wimlib_capture_source *sources;
814 nlines = text_file_count_lines(source_list_contents_p,
815 &source_list_nchars);
819 /* Always allocate at least 1 slot, just in case the implementation of
820 * calloc() returns NULL if 0 bytes are requested. */
821 sources = calloc(nlines ?: 1, sizeof(*sources));
823 imagex_error(T("out of memory"));
826 p = *source_list_contents_p;
828 for (i = 0; i < nlines; i++) {
829 /* XXX: Could use rawmemchr() here instead, but it may not be
830 * available on all platforms. */
831 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
832 size_t len = endp - p + 1;
834 if (!is_comment_line(p, len)) {
835 if (!parse_source_list_line(p, len, &sources[j++])) {
847 /* Reads the contents of a file into memory. */
849 file_get_contents(const tchar *filename, size_t *len_ret)
856 if (tstat(filename, &stbuf) != 0) {
857 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
862 fp = tfopen(filename, T("rb"));
864 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
868 buf = malloc(len ? len : 1);
870 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
871 "contents of file \"%"TS"\""), len, filename);
874 if (fread(buf, 1, len, fp) != len) {
875 imagex_error_with_errno(T("Failed to read %zu bytes from the "
876 "file \"%"TS"\""), len, filename);
890 /* Read standard input until EOF and return the full contents in a malloc()ed
891 * buffer and the number of bytes of data in @len_ret. Returns NULL on read
894 stdin_get_contents(size_t *len_ret)
896 /* stdin can, of course, be a pipe or other non-seekable file, so the
897 * total length of the data cannot be pre-determined */
899 size_t newlen = 1024;
903 char *p = realloc(buf, newlen);
904 size_t bytes_read, bytes_to_read;
906 imagex_error(T("out of memory while reading stdin"));
910 bytes_to_read = newlen - pos;
911 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
913 if (bytes_read != bytes_to_read) {
918 imagex_error_with_errno(T("error reading stdin"));
932 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
935 /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
937 *num_tchars_ret = num_bytes;
939 #else /* !__WIN32__ */
940 /* On Windows, translate the text to UTF-16LE */
944 if (num_bytes >= 2 &&
945 (((unsigned char)text[0] == 0xff && (unsigned char)text[1] == 0xfe) ||
946 ((unsigned char)text[0] <= 0x7f && (unsigned char)text[1] == 0x00)))
948 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
949 * with something that looks like an ASCII character encoded as
950 * a UTF-16LE code unit. Assume the file is encoded as
951 * UTF-16LE. This is not a 100% reliable check. */
952 num_wchars = num_bytes / 2;
953 text_wstr = (wchar_t*)text;
955 /* File does not look like UTF-16LE. Assume it is encoded in
956 * the current Windows code page. I think these are always
957 * ASCII-compatible, so any so-called "plain-text" (ASCII) files
958 * should work as expected. */
959 text_wstr = win32_mbs_to_wcs(text,
964 *num_tchars_ret = num_wchars;
966 #endif /* __WIN32__ */
970 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
975 contents = file_get_contents(filename, &num_bytes);
978 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
982 stdin_get_text_contents(size_t *num_tchars_ret)
987 contents = stdin_get_contents(&num_bytes);
990 return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
993 #define TO_PERCENT(numerator, denominator) \
994 (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
996 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
997 #define MEBIBYTE_MIN_NBYTES 10000000ULL
998 #define KIBIBYTE_MIN_NBYTES 10000ULL
1001 get_unit(uint64_t total_bytes, const tchar **name_ret)
1003 if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1004 *name_ret = T("GiB");
1006 } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1007 *name_ret = T("MiB");
1009 } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1010 *name_ret = T("KiB");
1013 *name_ret = T("bytes");
1018 static struct wimlib_progress_info_scan last_scan_progress;
1021 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1023 uint64_t prev_count, cur_count;
1025 prev_count = last_scan_progress.num_nondirs_scanned +
1026 last_scan_progress.num_dirs_scanned;
1027 cur_count = scan->num_nondirs_scanned + scan->num_dirs_scanned;
1029 if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1030 cur_count % 128 == 0)
1032 unsigned unit_shift;
1033 const tchar *unit_name;
1035 unit_shift = get_unit(scan->num_bytes_scanned, &unit_name);
1036 imagex_printf(T("\r%"PRIu64" %"TS" scanned (%"PRIu64" files, "
1037 "%"PRIu64" directories) "),
1038 scan->num_bytes_scanned >> unit_shift,
1040 scan->num_nondirs_scanned,
1041 scan->num_dirs_scanned);
1042 last_scan_progress = *scan;
1045 /* Progress callback function passed to various wimlib functions. */
1046 static enum wimlib_progress_status
1047 imagex_progress_func(enum wimlib_progress_msg msg,
1048 union wimlib_progress_info *info,
1049 void *_ignored_context)
1051 unsigned percent_done;
1052 unsigned unit_shift;
1053 const tchar *unit_name;
1055 if (imagex_be_quiet)
1056 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1058 case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1060 static bool first = true;
1062 imagex_printf(T("Writing %"TS"-compressed data "
1063 "using %u thread%"TS"\n"),
1064 wimlib_get_compression_type_string(
1065 info->write_streams.compression_type),
1066 info->write_streams.num_threads,
1067 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1071 unit_shift = get_unit(info->write_streams.total_bytes, &unit_name);
1072 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1073 info->write_streams.total_bytes);
1075 imagex_printf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
1076 "written (%u%% done)"),
1077 info->write_streams.completed_bytes >> unit_shift,
1079 info->write_streams.total_bytes >> unit_shift,
1082 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1083 imagex_printf(T("\n"));
1085 case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1086 imagex_printf(T("Scanning \"%"TS"\""), info->scan.source);
1087 if (WIMLIB_IS_WIM_ROOT_PATH(info->scan.wim_target_path)) {
1088 imagex_printf(T("\n"));
1090 imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1091 info->scan.wim_target_path);
1093 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1095 case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1096 switch (info->scan.status) {
1097 case WIMLIB_SCAN_DENTRY_OK:
1098 report_scan_progress(&info->scan, false);
1100 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1101 imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1103 case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
1104 imagex_printf(T("\nWARNING: Excluding unsupported file or directory\n"
1105 " \"%"TS"\" from capture\n"), info->scan.cur_path);
1107 case WIMLIB_SCAN_DENTRY_FIXED_SYMLINK:
1108 /* Symlink fixups are enabled by default. This is
1109 * mainly intended for Windows, which for some reason
1110 * uses absolute junctions (with drive letters!) in the
1111 * default installation. On UNIX-like systems, warn the
1112 * user when fixing the target of an absolute symbolic
1113 * link, so they know to disable this if they want. */
1115 imagex_printf(T("\nWARNING: Adjusted target of "
1116 "absolute symbolic link \"%"TS"\"\n"
1117 " (Use --norpfix to capture "
1118 "absolute symbolic links as-is)\n"),
1119 info->scan.cur_path);
1126 case WIMLIB_PROGRESS_MSG_SCAN_END:
1127 report_scan_progress(&info->scan, true);
1128 imagex_printf(T("\n"));
1130 case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1131 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1132 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1133 info->integrity.total_bytes);
1134 imagex_printf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" %"TS" "
1135 "of %"PRIu64" %"TS" (%u%%) done"),
1136 info->integrity.filename,
1137 info->integrity.completed_bytes >> unit_shift,
1139 info->integrity.total_bytes >> unit_shift,
1142 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1143 imagex_printf(T("\n"));
1145 case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1146 unit_shift = get_unit(info->integrity.total_bytes, &unit_name);
1147 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1148 info->integrity.total_bytes);
1149 imagex_printf(T("\rCalculating integrity table for WIM: %"PRIu64" %"TS" "
1150 "of %"PRIu64" %"TS" (%u%%) done"),
1151 info->integrity.completed_bytes >> unit_shift,
1153 info->integrity.total_bytes >> unit_shift,
1156 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1157 imagex_printf(T("\n"));
1159 case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1160 imagex_printf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1161 "to %"TS" \"%"TS"\"\n"),
1162 info->extract.image,
1163 info->extract.image_name,
1164 info->extract.wimfile_name,
1165 ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1166 T("NTFS volume") : T("directory")),
1167 info->extract.target);
1169 case WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE:
1170 if (info->extract.end_file_count >= 2000) {
1171 percent_done = TO_PERCENT(info->extract.current_file_count,
1172 info->extract.end_file_count);
1173 imagex_printf(T("\rCreating files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1174 info->extract.current_file_count,
1175 info->extract.end_file_count, percent_done);
1176 if (info->extract.current_file_count == info->extract.end_file_count)
1177 imagex_printf(T("\n"));
1180 case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1181 percent_done = TO_PERCENT(info->extract.completed_bytes,
1182 info->extract.total_bytes);
1183 unit_shift = get_unit(info->extract.total_bytes, &unit_name);
1184 imagex_printf(T("\rExtracting files: "
1185 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1186 info->extract.completed_bytes >> unit_shift,
1188 info->extract.total_bytes >> unit_shift,
1191 if (info->extract.completed_bytes >= info->extract.total_bytes)
1192 imagex_printf(T("\n"));
1194 case WIMLIB_PROGRESS_MSG_EXTRACT_METADATA:
1195 if (info->extract.end_file_count >= 2000) {
1196 percent_done = TO_PERCENT(info->extract.current_file_count,
1197 info->extract.end_file_count);
1198 imagex_printf(T("\rApplying metadata to files: %"PRIu64" of %"PRIu64" (%u%%) done"),
1199 info->extract.current_file_count,
1200 info->extract.end_file_count, percent_done);
1201 if (info->extract.current_file_count == info->extract.end_file_count)
1202 imagex_printf(T("\n"));
1205 case WIMLIB_PROGRESS_MSG_EXTRACT_SPWM_PART_BEGIN:
1206 if (info->extract.total_parts != 1) {
1207 imagex_printf(T("\nReading split pipable WIM part %u of %u\n"),
1208 info->extract.part_number,
1209 info->extract.total_parts);
1212 case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1213 percent_done = TO_PERCENT(info->split.completed_bytes,
1214 info->split.total_bytes);
1215 unit_shift = get_unit(info->split.total_bytes, &unit_name);
1216 imagex_printf(T("Writing \"%"TS"\" (part %u of %u): %"PRIu64" %"TS" of "
1217 "%"PRIu64" %"TS" (%u%%) written\n"),
1218 info->split.part_name,
1219 info->split.cur_part_number,
1220 info->split.total_parts,
1221 info->split.completed_bytes >> unit_shift,
1223 info->split.total_bytes >> unit_shift,
1227 case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1228 if (info->split.completed_bytes == info->split.total_bytes) {
1229 imagex_printf(T("Finished writing split WIM part %u of %u\n"),
1230 info->split.cur_part_number,
1231 info->split.total_parts);
1234 case WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND:
1235 switch (info->update.command->op) {
1236 case WIMLIB_UPDATE_OP_DELETE:
1237 imagex_printf(T("Deleted WIM path \"%"TS"\"\n"),
1238 info->update.command->delete_.wim_path);
1240 case WIMLIB_UPDATE_OP_RENAME:
1241 imagex_printf(T("Renamed WIM path \"%"TS"\" => \"%"TS"\"\n"),
1242 info->update.command->rename.wim_source_path,
1243 info->update.command->rename.wim_target_path);
1245 case WIMLIB_UPDATE_OP_ADD:
1250 case WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM:
1251 imagex_printf(T("Updating \"%"TS"\" in WIM image\n"),
1252 info->replace.path_in_wim);
1254 case WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE:
1255 imagex_printf(T("\nExtracting \"%"TS"\" as normal file (not WIMBoot pointer)\n"),
1256 info->wimboot_exclude.path_in_wim);
1258 case WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN:
1259 if (info->unmount.mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1260 if (info->unmount.unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT) {
1261 imagex_printf(T("Committing changes to %"TS" (image %d)\n"),
1262 info->unmount.mounted_wim,
1263 info->unmount.mounted_image);
1265 imagex_printf(T("Discarding changes to %"TS" (image %d)\n"),
1266 info->unmount.mounted_wim,
1267 info->unmount.mounted_image);
1268 imagex_printf(T("\t(Use --commit to keep changes.)\n"));
1272 case WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE:
1273 imagex_printf(T("Verifying metadata for image %"PRIu32" of %"PRIu32"\n"),
1274 info->verify_image.current_image,
1275 info->verify_image.total_images);
1277 case WIMLIB_PROGRESS_MSG_VERIFY_STREAMS:
1278 percent_done = TO_PERCENT(info->verify_streams.completed_bytes,
1279 info->verify_streams.total_bytes);
1280 unit_shift = get_unit(info->verify_streams.total_bytes, &unit_name);
1281 imagex_printf(T("\rVerifying streams: "
1282 "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1283 info->verify_streams.completed_bytes >> unit_shift,
1285 info->verify_streams.total_bytes >> unit_shift,
1288 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1289 imagex_printf(T("\n"));
1294 fflush(imagex_info_file);
1295 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1299 parse_num_threads(const tchar *optarg)
1302 unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1303 if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1304 imagex_error(T("Number of threads must be a non-negative integer!"));
1312 parse_chunk_size(const tchar *optarg)
1315 uint64_t chunk_size = tstrtoul(optarg, &tmp, 10);
1316 if (chunk_size == 0) {
1317 imagex_error(T("Invalid chunk size specification; must be a positive integer\n"
1318 " with optional K, M, or G suffix"));
1322 if (*tmp == T('k') || *tmp == T('K')) {
1325 } else if (*tmp == T('m') || *tmp == T('M')) {
1328 } else if (*tmp == T('g') || *tmp == T('G')) {
1332 if (*tmp && !(*tmp == T('i') && *(tmp + 1) == T('B'))) {
1333 imagex_error(T("Invalid chunk size specification; suffix must be K, M, or G"));
1337 if (chunk_size >= UINT32_MAX) {
1338 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1346 * Parse an option passed to an update command.
1348 * @op: One of WIMLIB_UPDATE_OP_* that indicates the command being
1351 * @option: Text string for the option (beginning with --)
1353 * @cmd: `struct wimlib_update_command' that is being constructed for
1356 * Returns true if the option was recognized; false if not.
1359 update_command_add_option(int op, const tchar *option,
1360 struct wimlib_update_command *cmd)
1362 bool recognized = true;
1364 case WIMLIB_UPDATE_OP_ADD:
1365 if (!tstrcmp(option, T("--verbose")))
1366 cmd->add.add_flags |= WIMLIB_ADD_FLAG_VERBOSE;
1367 else if (!tstrcmp(option, T("--unix-data")))
1368 cmd->add.add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1369 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1370 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1371 else if (!tstrcmp(option, T("--strict-acls")))
1372 cmd->add.add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1373 else if (!tstrcmp(option, T("--dereference")))
1374 cmd->add.add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1375 else if (!tstrcmp(option, T("--no-replace")))
1376 cmd->add.add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
1380 case WIMLIB_UPDATE_OP_DELETE:
1381 if (!tstrcmp(option, T("--force")))
1382 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1383 else if (!tstrcmp(option, T("--recursive")))
1384 cmd->delete_.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1395 /* How many nonoption arguments each `imagex update' command expects */
1396 static const unsigned update_command_num_nonoptions[] = {
1397 [WIMLIB_UPDATE_OP_ADD] = 2,
1398 [WIMLIB_UPDATE_OP_DELETE] = 1,
1399 [WIMLIB_UPDATE_OP_RENAME] = 2,
1403 update_command_add_nonoption(int op, const tchar *nonoption,
1404 struct wimlib_update_command *cmd,
1405 unsigned num_nonoptions)
1408 case WIMLIB_UPDATE_OP_ADD:
1409 if (num_nonoptions == 0)
1410 cmd->add.fs_source_path = (tchar*)nonoption;
1412 cmd->add.wim_target_path = (tchar*)nonoption;
1414 case WIMLIB_UPDATE_OP_DELETE:
1415 cmd->delete_.wim_path = (tchar*)nonoption;
1417 case WIMLIB_UPDATE_OP_RENAME:
1418 if (num_nonoptions == 0)
1419 cmd->rename.wim_source_path = (tchar*)nonoption;
1421 cmd->rename.wim_target_path = (tchar*)nonoption;
1427 * Parse a command passed on stdin to `imagex update'.
1429 * @line: Text of the command.
1430 * @len: Length of the line, including a null terminator
1433 * @command: A `struct wimlib_update_command' to fill in from the parsed
1436 * @line_number: Line number of the command, for diagnostics.
1438 * Returns true on success; returns false on parse error.
1441 parse_update_command(tchar *line, size_t len,
1442 struct wimlib_update_command *command,
1446 tchar *command_name;
1448 size_t num_nonoptions;
1450 /* Get the command name ("add", "delete", "rename") */
1451 ret = parse_string(&line, &len, &command_name);
1452 if (ret != PARSE_STRING_SUCCESS)
1455 if (!tstrcasecmp(command_name, T("add"))) {
1456 op = WIMLIB_UPDATE_OP_ADD;
1457 } else if (!tstrcasecmp(command_name, T("delete"))) {
1458 op = WIMLIB_UPDATE_OP_DELETE;
1459 } else if (!tstrcasecmp(command_name, T("rename"))) {
1460 op = WIMLIB_UPDATE_OP_RENAME;
1462 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1463 command_name, line_number);
1468 /* Parse additional options and non-options as needed */
1473 ret = parse_string(&line, &len, &next_string);
1474 if (ret == PARSE_STRING_NONE) /* End of line */
1476 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1478 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1480 if (!update_command_add_option(op, next_string, command))
1482 imagex_error(T("Unrecognized option \"%"TS"\" to "
1483 "update command \"%"TS"\" on line %zu"),
1484 next_string, command_name, line_number);
1490 if (num_nonoptions == update_command_num_nonoptions[op])
1492 imagex_error(T("Unexpected argument \"%"TS"\" in "
1493 "update command on line %zu\n"
1494 " (The \"%"TS"\" command only "
1495 "takes %zu nonoption arguments!)\n"),
1496 next_string, line_number,
1497 command_name, num_nonoptions);
1500 update_command_add_nonoption(op, next_string,
1501 command, num_nonoptions);
1506 if (num_nonoptions != update_command_num_nonoptions[op]) {
1507 imagex_error(T("Not enough arguments to update command "
1508 "\"%"TS"\" on line %zu"), command_name, line_number);
1514 static struct wimlib_update_command *
1515 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1516 size_t *num_cmds_ret)
1520 struct wimlib_update_command *cmds;
1523 nlines = text_file_count_lines(cmd_file_contents_p,
1528 /* Always allocate at least 1 slot, just in case the implementation of
1529 * calloc() returns NULL if 0 bytes are requested. */
1530 cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1532 imagex_error(T("out of memory"));
1535 p = *cmd_file_contents_p;
1537 for (i = 0; i < nlines; i++) {
1538 /* XXX: Could use rawmemchr() here instead, but it may not be
1539 * available on all platforms. */
1540 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1541 size_t len = endp - p + 1;
1543 if (!is_comment_line(p, len)) {
1544 if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1555 /* Apply one image, or all images, from a WIM file to a directory, OR apply
1556 * one image from a WIM file to an NTFS volume. */
1558 imagex_apply(int argc, tchar **argv, int cmd)
1562 int image = WIMLIB_NO_IMAGE;
1564 struct wimlib_wim_info info;
1566 const tchar *wimfile;
1567 const tchar *target;
1568 const tchar *image_num_or_name = NULL;
1569 int extract_flags = 0;
1571 STRING_SET(refglobs);
1573 for_opt(c, apply_options) {
1575 case IMAGEX_CHECK_OPTION:
1576 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1578 case IMAGEX_VERBOSE_OPTION:
1579 /* No longer does anything. */
1581 case IMAGEX_REF_OPTION:
1582 ret = string_set_append(&refglobs, optarg);
1584 goto out_free_refglobs;
1586 case IMAGEX_UNIX_DATA_OPTION:
1587 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1589 case IMAGEX_NO_ACLS_OPTION:
1590 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1592 case IMAGEX_STRICT_ACLS_OPTION:
1593 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1595 case IMAGEX_NO_ATTRIBUTES_OPTION:
1596 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1598 case IMAGEX_NORPFIX_OPTION:
1599 extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1601 case IMAGEX_RPFIX_OPTION:
1602 extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1604 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
1605 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
1606 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
1608 case IMAGEX_RESUME_OPTION:
1609 extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME;
1611 case IMAGEX_WIMBOOT_OPTION:
1612 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1620 if (argc != 2 && argc != 3)
1625 if (!tstrcmp(wimfile, T("-"))) {
1626 /* Attempt to apply pipable WIM from standard input. */
1628 image_num_or_name = NULL;
1631 image_num_or_name = argv[1];
1636 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1637 imagex_progress_func, NULL);
1639 goto out_free_refglobs;
1641 wimlib_get_wim_info(wim, &info);
1644 /* Image explicitly specified. */
1645 image_num_or_name = argv[1];
1646 image = wimlib_resolve_image(wim, image_num_or_name);
1647 ret = verify_image_exists(image, image_num_or_name, wimfile);
1649 goto out_wimlib_free;
1652 /* No image specified; default to image 1, but only if the WIM
1653 * contains exactly one image. */
1655 if (info.image_count != 1) {
1656 imagex_error(T("\"%"TS"\" contains %d images; "
1657 "Please select one (or all)."),
1658 wimfile, info.image_count);
1667 if (refglobs.num_strings) {
1669 imagex_error(T("Can't specify --ref when applying from stdin!"));
1671 goto out_wimlib_free;
1673 ret = wim_reference_globs(wim, &refglobs, open_flags);
1675 goto out_wimlib_free;
1680 /* Interpret a regular file or block device target as an NTFS
1684 if (tstat(target, &stbuf)) {
1685 if (errno != ENOENT) {
1686 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1689 goto out_wimlib_free;
1692 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1693 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1699 ret = wimlib_extract_image(wim, image, target, extract_flags);
1701 set_fd_to_binary_mode(STDIN_FILENO);
1702 ret = wimlib_extract_image_from_pipe_with_progress(
1707 imagex_progress_func,
1711 imagex_printf(T("Done applying WIM image.\n"));
1712 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1714 do_resource_not_found_warning(wimfile, &info, &refglobs);
1716 imagex_error(T( "If you are applying an image "
1717 "from a split pipable WIM,\n"
1718 " make sure you have "
1719 "concatenated together all parts."));
1725 string_set_destroy(&refglobs);
1729 usage(CMD_APPLY, stderr);
1731 goto out_free_refglobs;
1734 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1735 * directory trees. 'wimlib-imagex capture': create a new WIM file containing
1736 * the desired image. 'wimlib-imagex append': add a new image to an existing
1739 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1743 int add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
1744 WIMLIB_ADD_FLAG_WINCONFIG |
1745 WIMLIB_ADD_FLAG_VERBOSE;
1746 int write_flags = 0;
1747 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
1748 uint32_t chunk_size = UINT32_MAX;
1749 uint32_t solid_chunk_size = UINT32_MAX;
1750 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
1751 const tchar *wimfile;
1755 const tchar *flags_element = NULL;
1758 STRING_SET(base_wimfiles);
1759 WIMStruct **base_wims;
1761 WIMStruct *template_wim;
1762 const tchar *template_wimfile = NULL;
1763 const tchar *template_image_name_or_num = NULL;
1764 int template_image = WIMLIB_NO_IMAGE;
1767 unsigned num_threads = 0;
1772 tchar *config_file = NULL;
1774 bool source_list = false;
1775 size_t source_list_nchars = 0;
1776 tchar *source_list_contents;
1777 bool capture_sources_malloced;
1778 struct wimlib_capture_source *capture_sources;
1780 bool name_defaulted;
1782 for_opt(c, capture_or_append_options) {
1784 case IMAGEX_BOOT_OPTION:
1785 add_flags |= WIMLIB_ADD_FLAG_BOOT;
1787 case IMAGEX_CHECK_OPTION:
1788 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1789 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1791 case IMAGEX_NOCHECK_OPTION:
1792 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1794 case IMAGEX_CONFIG_OPTION:
1795 config_file = optarg;
1796 add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1798 case IMAGEX_COMPRESS_OPTION:
1799 compression_type = get_compression_type(optarg);
1800 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1803 case IMAGEX_COMPRESS_SLOW_OPTION:
1804 set_compress_slow();
1806 case IMAGEX_CHUNK_SIZE_OPTION:
1807 chunk_size = parse_chunk_size(optarg);
1808 if (chunk_size == UINT32_MAX)
1811 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1812 solid_chunk_size = parse_chunk_size(optarg);
1813 if (solid_chunk_size == UINT32_MAX)
1816 case IMAGEX_SOLID_COMPRESS_OPTION:
1817 solid_ctype = get_compression_type(optarg);
1818 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1821 case IMAGEX_SOLID_OPTION:
1822 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1824 case IMAGEX_NO_SOLID_SORT_OPTION:
1825 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1827 case IMAGEX_FLAGS_OPTION:
1828 flags_element = optarg;
1830 case IMAGEX_DEREFERENCE_OPTION:
1831 add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1833 case IMAGEX_VERBOSE_OPTION:
1834 /* No longer does anything. */
1836 case IMAGEX_THREADS_OPTION:
1837 num_threads = parse_num_threads(optarg);
1838 if (num_threads == UINT_MAX)
1841 case IMAGEX_REBUILD_OPTION:
1842 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1844 case IMAGEX_UNIX_DATA_OPTION:
1845 add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1847 case IMAGEX_SOURCE_LIST_OPTION:
1850 case IMAGEX_NO_ACLS_OPTION:
1851 add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1853 case IMAGEX_STRICT_ACLS_OPTION:
1854 add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1856 case IMAGEX_RPFIX_OPTION:
1857 add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1859 case IMAGEX_NORPFIX_OPTION:
1860 add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1862 case IMAGEX_PIPABLE_OPTION:
1863 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1865 case IMAGEX_NOT_PIPABLE_OPTION:
1866 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1868 case IMAGEX_UPDATE_OF_OPTION:
1869 if (template_image_name_or_num) {
1870 imagex_error(T("'--update-of' can only be "
1871 "specified one time!"));
1875 colon = tstrrchr(optarg, T(':'));
1878 template_wimfile = optarg;
1880 template_image_name_or_num = colon + 1;
1882 template_wimfile = NULL;
1883 template_image_name_or_num = optarg;
1887 case IMAGEX_DELTA_FROM_OPTION:
1888 if (cmd != CMD_CAPTURE) {
1889 imagex_error(T("'--delta-from' is only "
1890 "valid for capture!"));
1893 ret = string_set_append(&base_wimfiles, optarg);
1895 goto out_free_base_wimfiles;
1896 write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
1898 case IMAGEX_WIMBOOT_OPTION:
1899 add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
1908 if (argc < 2 || argc > 4)
1914 /* Set default compression type and parameters. */
1917 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
1918 /* No compression type specified. Use the default. */
1920 if (add_flags & WIMLIB_ADD_FLAG_WIMBOOT) {
1921 /* With --wimboot, default to XPRESS compression. */
1922 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
1923 } else if (write_flags & WIMLIB_WRITE_FLAG_SOLID) {
1924 /* With --solid, default to LZMS compression. (However,
1925 * this will not affect solid resources!) */
1926 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
1928 /* Otherwise, default to LZX compression. */
1929 compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
1933 if (!tstrcmp(wimfile, T("-"))) {
1934 /* Writing captured WIM to standard output. */
1936 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
1937 imagex_error("Can't write a non-pipable WIM to "
1938 "standard output! Specify --pipable\n"
1939 " if you want to create a pipable WIM "
1940 "(but read the docs first).");
1944 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1946 if (cmd == CMD_APPEND) {
1947 imagex_error(T("Using standard output for append does "
1948 "not make sense."));
1951 wim_fd = STDOUT_FILENO;
1953 imagex_info_file = stderr;
1954 set_fd_to_binary_mode(wim_fd);
1957 /* If template image was specified using --update-of=IMAGE rather
1958 * than --update-of=WIMFILE:IMAGE, set the default WIMFILE. */
1959 if (template_image_name_or_num && !template_wimfile) {
1960 if (base_wimfiles.num_strings == 1) {
1961 /* Capturing delta WIM based on single WIM: default to
1963 template_wimfile = base_wimfiles.strings[0];
1964 } else if (cmd == CMD_APPEND) {
1965 /* Appending to WIM: default to WIM being appended to.
1967 template_wimfile = wimfile;
1969 /* Capturing a normal (non-delta) WIM, so the WIM file
1970 * *must* be explicitly specified. */
1971 if (base_wimfiles.num_strings > 1) {
1972 imagex_error(T("For capture of delta WIM "
1973 "based on multiple existing "
1975 " '--update-of' must "
1976 "specify WIMFILE:IMAGE!"));
1978 imagex_error(T("For capture of non-delta WIM, "
1979 "'--update-of' must specify "
1988 name_defaulted = false;
1990 /* Set default name to SOURCE argument, omitting any directory
1991 * prefixes and trailing slashes. This requires making a copy
1992 * of @source. Leave some free characters at the end in case we
1993 * append a number to keep the name unique. */
1994 size_t source_name_len;
1996 source_name_len = tstrlen(source);
1997 source_copy = alloca((source_name_len + 1 + 25) * sizeof(tchar));
1998 name = tbasename(tstrcpy(source_copy, source));
1999 name_defaulted = true;
2001 /* Image description defaults to NULL if not given. */
2008 /* Set up capture sources in source list mode */
2009 if (source[0] == T('-') && source[1] == T('\0')) {
2010 source_list_contents = stdin_get_text_contents(&source_list_nchars);
2012 source_list_contents = file_get_text_contents(source,
2013 &source_list_nchars);
2015 if (!source_list_contents)
2018 capture_sources = parse_source_list(&source_list_contents,
2021 if (!capture_sources) {
2023 goto out_free_source_list_contents;
2025 capture_sources_malloced = true;
2027 /* Set up capture source in non-source-list mode. */
2028 capture_sources = alloca(sizeof(struct wimlib_capture_source));
2029 capture_sources[0].fs_source_path = source;
2030 capture_sources[0].wim_target_path = WIMLIB_WIM_ROOT_PATH;
2031 capture_sources[0].reserved = 0;
2033 capture_sources_malloced = false;
2034 source_list_contents = NULL;
2037 /* Open the existing WIM, or create a new one. */
2038 if (cmd == CMD_APPEND) {
2039 ret = wimlib_open_wim_with_progress(wimfile,
2040 open_flags | WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2042 imagex_progress_func,
2045 goto out_free_capture_sources;
2047 ret = wimlib_create_new_wim(compression_type, &wim);
2049 goto out_free_capture_sources;
2050 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2053 /* Set chunk size if non-default. */
2054 if (chunk_size != UINT32_MAX) {
2055 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2058 } else if ((add_flags & WIMLIB_ADD_FLAG_WIMBOOT) &&
2059 compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS) {
2060 ret = wimlib_set_output_chunk_size(wim, 4096);
2064 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2065 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2069 if (solid_chunk_size != UINT32_MAX) {
2070 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2076 /* Detect if source is regular file or block device and set NTFS volume
2081 if (tstat(source, &stbuf) == 0) {
2082 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
2083 imagex_printf(T("Capturing WIM image from NTFS "
2084 "filesystem on \"%"TS"\"\n"), source);
2085 add_flags |= WIMLIB_ADD_FLAG_NTFS;
2088 if (errno != ENOENT) {
2089 imagex_error_with_errno(T("Failed to stat "
2090 "\"%"TS"\""), source);
2098 /* If the user did not specify an image name, and the basename of the
2099 * source already exists as an image name in the WIM file, append a
2100 * suffix to make it unique. */
2101 if (cmd == CMD_APPEND && name_defaulted) {
2102 unsigned long conflict_idx;
2103 tchar *name_end = tstrchr(name, T('\0'));
2104 for (conflict_idx = 1;
2105 wimlib_image_name_in_use(wim, name);
2108 tsprintf(name_end, T(" (%lu)"), conflict_idx);
2112 /* If capturing a delta WIM, reference resources from the base WIMs
2113 * before adding the new image. */
2114 if (base_wimfiles.num_strings) {
2115 base_wims = calloc(base_wimfiles.num_strings,
2116 sizeof(base_wims[0]));
2117 if (base_wims == NULL) {
2118 imagex_error(T("Out of memory!"));
2123 for (size_t i = 0; i < base_wimfiles.num_strings; i++) {
2124 ret = wimlib_open_wim_with_progress(
2125 base_wimfiles.strings[i], open_flags,
2126 &base_wims[i], imagex_progress_func, NULL);
2128 goto out_free_base_wims;
2132 ret = wimlib_reference_resources(wim, base_wims,
2133 base_wimfiles.num_strings, 0);
2135 goto out_free_base_wims;
2137 if (base_wimfiles.num_strings == 1) {
2138 imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2139 base_wimfiles.strings[0]);
2141 imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2142 base_wimfiles.num_strings);
2149 /* If capturing or appending as an update of an existing (template) image,
2150 * open the WIM if needed and parse the image index. */
2151 if (template_image_name_or_num) {
2154 if (base_wimfiles.num_strings == 1 &&
2155 template_wimfile == base_wimfiles.strings[0]) {
2156 template_wim = base_wims[0];
2157 } else if (template_wimfile == wimfile) {
2160 ret = wimlib_open_wim_with_progress(template_wimfile,
2163 imagex_progress_func,
2166 goto out_free_base_wims;
2169 template_image = wimlib_resolve_image(template_wim,
2170 template_image_name_or_num);
2172 if (template_image_name_or_num[0] == T('-')) {
2175 struct wimlib_wim_info info;
2177 wimlib_get_wim_info(template_wim, &info);
2178 n = tstrtoul(template_image_name_or_num + 1, &tmp, 10);
2179 if (n >= 1 && n <= info.image_count &&
2181 tmp != template_image_name_or_num + 1)
2183 template_image = info.image_count - (n - 1);
2186 ret = verify_image_exists_and_is_single(template_image,
2187 template_image_name_or_num,
2190 goto out_free_template_wim;
2192 template_wim = NULL;
2195 ret = wimlib_add_image_multisource(wim,
2202 goto out_free_template_wim;
2204 if (desc || flags_element || template_image_name_or_num) {
2205 /* User provided <DESCRIPTION> or <FLAGS> element, or an image
2206 * on which the added one is to be based has been specified with
2207 * --update-of. Get the index of the image we just
2208 * added, then use it to call the appropriate functions. */
2209 struct wimlib_wim_info info;
2211 wimlib_get_wim_info(wim, &info);
2214 ret = wimlib_set_image_descripton(wim,
2218 goto out_free_template_wim;
2221 if (flags_element) {
2222 ret = wimlib_set_image_flags(wim, info.image_count,
2225 goto out_free_template_wim;
2228 /* Reference template image if the user provided one. */
2229 if (template_image_name_or_num) {
2230 imagex_printf(T("Using image %d "
2231 "from \"%"TS"\" as template\n"),
2232 template_image, template_wimfile);
2233 ret = wimlib_reference_template_image(wim,
2239 goto out_free_template_wim;
2243 /* Write the new WIM or overwrite the existing WIM with the new image
2245 if (cmd == CMD_APPEND) {
2246 ret = wimlib_overwrite(wim, write_flags, num_threads);
2247 } else if (wimfile) {
2248 ret = wimlib_write(wim, wimfile, WIMLIB_ALL_IMAGES,
2249 write_flags, num_threads);
2251 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2252 write_flags, num_threads);
2254 out_free_template_wim:
2255 /* template_wim may alias base_wims[0] or wim. */
2256 if ((base_wimfiles.num_strings != 1 || template_wim != base_wims[0]) &&
2257 template_wim != wim)
2258 wimlib_free(template_wim);
2260 for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2261 wimlib_free(base_wims[i]);
2265 out_free_capture_sources:
2266 if (capture_sources_malloced)
2267 free(capture_sources);
2268 out_free_source_list_contents:
2269 free(source_list_contents);
2270 out_free_base_wimfiles:
2271 string_set_destroy(&base_wimfiles);
2278 goto out_free_base_wimfiles;
2281 /* Remove image(s) from a WIM. */
2283 imagex_delete(int argc, tchar **argv, int cmd)
2286 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
2287 int write_flags = 0;
2288 const tchar *wimfile;
2289 const tchar *image_num_or_name;
2294 for_opt(c, delete_options) {
2296 case IMAGEX_CHECK_OPTION:
2297 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2298 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2300 case IMAGEX_SOFT_OPTION:
2301 write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2312 imagex_error(T("Must specify a WIM file"));
2314 imagex_error(T("Must specify an image"));
2318 image_num_or_name = argv[1];
2320 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2321 imagex_progress_func, NULL);
2325 image = wimlib_resolve_image(wim, image_num_or_name);
2327 ret = verify_image_exists(image, image_num_or_name, wimfile);
2329 goto out_wimlib_free;
2331 ret = wimlib_delete_image(wim, image);
2333 imagex_error(T("Failed to delete image from \"%"TS"\""),
2335 goto out_wimlib_free;
2338 ret = wimlib_overwrite(wim, write_flags, 0);
2340 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2341 "deleted"), wimfile);
2349 usage(CMD_DELETE, stderr);
2354 struct print_dentry_options {
2359 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2361 tprintf(T("%"TS"\n"), dentry->full_path);
2364 static const struct {
2367 } file_attr_flags[] = {
2368 {WIMLIB_FILE_ATTRIBUTE_READONLY, T("READONLY")},
2369 {WIMLIB_FILE_ATTRIBUTE_HIDDEN, T("HIDDEN")},
2370 {WIMLIB_FILE_ATTRIBUTE_SYSTEM, T("SYSTEM")},
2371 {WIMLIB_FILE_ATTRIBUTE_DIRECTORY, T("DIRECTORY")},
2372 {WIMLIB_FILE_ATTRIBUTE_ARCHIVE, T("ARCHIVE")},
2373 {WIMLIB_FILE_ATTRIBUTE_DEVICE, T("DEVICE")},
2374 {WIMLIB_FILE_ATTRIBUTE_NORMAL, T("NORMAL")},
2375 {WIMLIB_FILE_ATTRIBUTE_TEMPORARY, T("TEMPORARY")},
2376 {WIMLIB_FILE_ATTRIBUTE_SPARSE_FILE, T("SPARSE_FILE")},
2377 {WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT, T("REPARSE_POINT")},
2378 {WIMLIB_FILE_ATTRIBUTE_COMPRESSED, T("COMPRESSED")},
2379 {WIMLIB_FILE_ATTRIBUTE_OFFLINE, T("OFFLINE")},
2380 {WIMLIB_FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, T("NOT_CONTENT_INDEXED")},
2381 {WIMLIB_FILE_ATTRIBUTE_ENCRYPTED, T("ENCRYPTED")},
2382 {WIMLIB_FILE_ATTRIBUTE_VIRTUAL, T("VIRTUAL")},
2385 #define TIMESTR_MAX 100
2388 timespec_to_string(const struct timespec *spec, tchar *buf)
2390 time_t t = spec->tv_sec;
2393 tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2394 buf[TIMESTR_MAX - 1] = '\0';
2398 print_time(const tchar *type, const struct timespec *spec)
2400 tchar timestr[TIMESTR_MAX];
2402 timespec_to_string(spec, timestr);
2404 tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2407 static void print_byte_field(const uint8_t field[], size_t len)
2410 tprintf(T("%02hhx"), *field++);
2414 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2416 tputs(T("WIM Information:"));
2417 tputs(T("----------------"));
2418 tprintf(T("Path: %"TS"\n"), wimfile);
2419 tprintf(T("GUID: 0x"));
2420 print_byte_field(info->guid, sizeof(info->guid));
2422 tprintf(T("Version: %u\n"), info->wim_version);
2423 tprintf(T("Image Count: %d\n"), info->image_count);
2424 tprintf(T("Compression: %"TS"\n"),
2425 wimlib_get_compression_type_string(info->compression_type));
2426 tprintf(T("Chunk Size: %"PRIu32" bytes\n"),
2428 tprintf(T("Part Number: %d/%d\n"), info->part_number, info->total_parts);
2429 tprintf(T("Boot Index: %d\n"), info->boot_index);
2430 tprintf(T("Size: %"PRIu64" bytes\n"), info->total_bytes);
2431 tprintf(T("Integrity Info: %"TS"\n"),
2432 info->has_integrity_table ? T("yes") : T("no"));
2433 tprintf(T("Relative path junction: %"TS"\n"),
2434 info->has_rpfix ? T("yes") : T("no"));
2435 tprintf(T("Pipable: %"TS"\n"),
2436 info->pipable ? T("yes") : T("no"));
2441 print_resource(const struct wimlib_resource_entry *resource,
2444 tprintf(T("Hash = 0x"));
2445 print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2448 if (!resource->is_missing) {
2449 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2450 resource->uncompressed_size);
2451 if (resource->packed) {
2452 tprintf(T("Solid resource = %"PRIu64" => %"PRIu64" "
2453 "bytes @ offset %"PRIu64"\n"),
2454 resource->raw_resource_uncompressed_size,
2455 resource->raw_resource_compressed_size,
2456 resource->raw_resource_offset_in_wim);
2458 tprintf(T("Solid offset = %"PRIu64" bytes\n"),
2461 tprintf(T("Compressed size = %"PRIu64" bytes\n"),
2462 resource->compressed_size);
2464 tprintf(T("Offset in WIM = %"PRIu64" bytes\n"),
2468 tprintf(T("Part Number = %u\n"), resource->part_number);
2469 tprintf(T("Reference Count = %u\n"), resource->reference_count);
2471 tprintf(T("Flags = "));
2472 if (resource->is_compressed)
2473 tprintf(T("WIM_RESHDR_FLAG_COMPRESSED "));
2474 if (resource->is_metadata)
2475 tprintf(T("WIM_RESHDR_FLAG_METADATA "));
2476 if (resource->is_free)
2477 tprintf(T("WIM_RESHDR_FLAG_FREE "));
2478 if (resource->is_spanned)
2479 tprintf(T("WIM_RESHDR_FLAG_SPANNED "));
2480 if (resource->packed)
2481 tprintf(T("WIM_RESHDR_FLAG_SOLID "));
2489 print_blobs(WIMStruct *wim)
2491 wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2495 default_print_security_descriptor(const uint8_t *sd, size_t size)
2497 tprintf(T("Security Descriptor = "));
2498 print_byte_field(sd, size);
2503 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2507 "----------------------------------------------------------------------------\n"));
2508 tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path);
2509 if (dentry->dos_name)
2510 tprintf(T("Short Name = \"%"TS"\"\n"), dentry->dos_name);
2511 tprintf(T("Attributes = 0x%08x\n"), dentry->attributes);
2512 for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2513 if (file_attr_flags[i].flag & dentry->attributes)
2514 tprintf(T(" FILE_ATTRIBUTE_%"TS" is set\n"),
2515 file_attr_flags[i].name);
2517 if (dentry->security_descriptor) {
2518 print_security_descriptor(dentry->security_descriptor,
2519 dentry->security_descriptor_size);
2522 print_time(T("Creation Time"), &dentry->creation_time);
2523 print_time(T("Last Write Time"), &dentry->last_write_time);
2524 print_time(T("Last Access Time"), &dentry->last_access_time);
2527 if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2528 tprintf(T("Reparse Tag = 0x%"PRIx32"\n"), dentry->reparse_tag);
2530 tprintf(T("Link Group ID = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2531 tprintf(T("Link Count = %"PRIu32"\n"), dentry->num_links);
2533 if (dentry->unix_mode != 0) {
2534 tprintf(T("UNIX Data = uid:%"PRIu32" gid:%"PRIu32" "
2535 "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2536 dentry->unix_uid, dentry->unix_gid,
2537 dentry->unix_mode, dentry->unix_rdev);
2540 for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2541 if (dentry->streams[i].stream_name) {
2542 tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2543 dentry->streams[i].stream_name);
2544 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2545 tprintf(T("\tRaw encrypted data stream:\n"));
2546 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2547 tprintf(T("\tReparse point stream:\n"));
2549 tprintf(T("\tUnnamed data stream:\n"));
2551 print_resource(&dentry->streams[i].resource, NULL);
2556 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2558 const struct print_dentry_options *options = _options;
2559 if (!options->detailed)
2560 print_dentry_full_path(dentry);
2562 print_dentry_detailed(dentry);
2566 /* Print the files contained in an image(s) in a WIM file. */
2568 imagex_dir(int argc, tchar **argv, int cmd)
2570 const tchar *wimfile;
2571 WIMStruct *wim = NULL;
2574 const tchar *path = WIMLIB_WIM_ROOT_PATH;
2576 struct print_dentry_options options = {
2579 int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2581 for_opt(c, dir_options) {
2583 case IMAGEX_PATH_OPTION:
2586 case IMAGEX_DETAILED_OPTION:
2587 options.detailed = true;
2589 case IMAGEX_ONE_FILE_ONLY_OPTION:
2590 iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2600 imagex_error(T("Must specify a WIM file"));
2604 imagex_error(T("Too many arguments"));
2609 ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2610 imagex_progress_func, NULL);
2615 image = wimlib_resolve_image(wim, argv[1]);
2616 ret = verify_image_exists(image, argv[1], wimfile);
2618 goto out_wimlib_free;
2620 /* No image specified; default to image 1, but only if the WIM
2621 * contains exactly one image. */
2623 struct wimlib_wim_info info;
2625 wimlib_get_wim_info(wim, &info);
2626 if (info.image_count != 1) {
2627 imagex_error(T("\"%"TS"\" contains %d images; Please "
2628 "select one (or all)."),
2629 wimfile, info.image_count);
2636 ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2637 print_dentry, &options);
2644 usage(CMD_DIR, stderr);
2649 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2652 imagex_export(int argc, tchar **argv, int cmd)
2656 int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2657 int write_flags = 0;
2658 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2659 const tchar *src_wimfile;
2660 const tchar *src_image_num_or_name;
2661 const tchar *dest_wimfile;
2663 const tchar *dest_name;
2664 const tchar *dest_desc;
2666 struct wimlib_wim_info src_info;
2667 WIMStruct *dest_wim;
2672 STRING_SET(refglobs);
2673 unsigned num_threads = 0;
2674 uint32_t chunk_size = UINT32_MAX;
2675 uint32_t solid_chunk_size = UINT32_MAX;
2676 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2678 for_opt(c, export_options) {
2680 case IMAGEX_BOOT_OPTION:
2681 export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2683 case IMAGEX_CHECK_OPTION:
2684 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2685 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2687 case IMAGEX_NOCHECK_OPTION:
2688 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2690 case IMAGEX_COMPRESS_OPTION:
2691 compression_type = get_compression_type(optarg);
2692 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2695 case IMAGEX_COMPRESS_SLOW_OPTION:
2696 set_compress_slow();
2697 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2699 case IMAGEX_RECOMPRESS_OPTION:
2700 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2702 case IMAGEX_SOLID_OPTION:
2703 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2705 case IMAGEX_NO_SOLID_SORT_OPTION:
2706 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2708 case IMAGEX_CHUNK_SIZE_OPTION:
2709 chunk_size = parse_chunk_size(optarg);
2710 if (chunk_size == UINT32_MAX)
2713 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2714 solid_chunk_size = parse_chunk_size(optarg);
2715 if (solid_chunk_size == UINT32_MAX)
2718 case IMAGEX_SOLID_COMPRESS_OPTION:
2719 solid_ctype = get_compression_type(optarg);
2720 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2723 case IMAGEX_REF_OPTION:
2724 ret = string_set_append(&refglobs, optarg);
2726 goto out_free_refglobs;
2728 case IMAGEX_THREADS_OPTION:
2729 num_threads = parse_num_threads(optarg);
2730 if (num_threads == UINT_MAX)
2733 case IMAGEX_REBUILD_OPTION:
2734 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2736 case IMAGEX_PIPABLE_OPTION:
2737 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2739 case IMAGEX_NOT_PIPABLE_OPTION:
2740 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2742 case IMAGEX_WIMBOOT_OPTION:
2743 export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2751 if (argc < 3 || argc > 5)
2753 src_wimfile = argv[0];
2754 src_image_num_or_name = argv[1];
2755 dest_wimfile = argv[2];
2756 dest_name = (argc >= 4) ? argv[3] : NULL;
2757 dest_desc = (argc >= 5) ? argv[4] : NULL;
2758 ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2759 imagex_progress_func, NULL);
2761 goto out_free_refglobs;
2763 wimlib_get_wim_info(src_wim, &src_info);
2765 /* Determine if the destination is an existing file or not. If so, we
2766 * try to append the exported image(s) to it; otherwise, we create a new
2767 * WIM containing the exported image(s). Furthermore, determine if we
2768 * need to write a pipable WIM directly to standard output. */
2770 if (tstrcmp(dest_wimfile, T("-")) == 0) {
2772 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2773 imagex_error("Can't write a non-pipable WIM to "
2774 "standard output! Specify --pipable\n"
2775 " if you want to create a pipable WIM "
2776 "(but read the docs first).");
2778 goto out_free_src_wim;
2781 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2783 dest_wimfile = NULL;
2784 dest_wim_fd = STDOUT_FILENO;
2785 imagex_info_file = stderr;
2786 set_fd_to_binary_mode(dest_wim_fd);
2789 if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2791 /* Destination file exists. */
2793 if (!S_ISREG(stbuf.st_mode)) {
2794 imagex_error(T("\"%"TS"\" is not a regular file"),
2797 goto out_free_src_wim;
2799 ret = wimlib_open_wim_with_progress(dest_wimfile,
2801 WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2803 imagex_progress_func,
2806 goto out_free_src_wim;
2808 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2809 /* The user specified a compression type, but we're
2810 * exporting to an existing WIM. Make sure the
2811 * specified compression type is the same as the
2812 * compression type of the existing destination WIM. */
2813 struct wimlib_wim_info dest_info;
2815 wimlib_get_wim_info(dest_wim, &dest_info);
2816 if (compression_type != dest_info.compression_type) {
2817 imagex_error(T("Cannot specify a compression type that is "
2818 "not the same as that used in the "
2819 "destination WIM"));
2821 goto out_free_dest_wim;
2827 if (errno != ENOENT) {
2828 imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
2831 goto out_free_src_wim;
2834 /* dest_wimfile is not an existing file, so create a new WIM. */
2836 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2837 /* The user did not specify a compression type; default
2838 * to that of the source WIM, unless --solid or
2839 * --wimboot was specified. */
2841 if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
2842 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2843 else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2844 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2846 compression_type = src_info.compression_type;
2848 ret = wimlib_create_new_wim(compression_type, &dest_wim);
2850 goto out_free_src_wim;
2852 wimlib_register_progress_function(dest_wim,
2853 imagex_progress_func, NULL);
2855 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2856 && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
2858 /* For --wimboot export, use small XPRESS chunks. */
2859 wimlib_set_output_chunk_size(dest_wim, 4096);
2860 } else if (compression_type == src_info.compression_type &&
2861 chunk_size == UINT32_MAX)
2863 /* Use same chunk size if compression type is the same. */
2864 wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
2868 if (chunk_size != UINT32_MAX) {
2869 /* Set destination chunk size. */
2870 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
2872 goto out_free_dest_wim;
2874 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2875 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
2877 goto out_free_dest_wim;
2879 if (solid_chunk_size != UINT32_MAX) {
2880 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
2882 goto out_free_dest_wim;
2885 image = wimlib_resolve_image(src_wim, src_image_num_or_name);
2886 ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
2888 goto out_free_dest_wim;
2890 if (refglobs.num_strings) {
2891 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
2893 goto out_free_dest_wim;
2896 if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
2897 image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
2899 imagex_error(T("--boot specified for all-images export, but source WIM "
2900 "has no bootable image."));
2902 goto out_free_dest_wim;
2905 ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
2906 dest_desc, export_flags);
2908 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
2909 do_resource_not_found_warning(src_wimfile,
2910 &src_info, &refglobs);
2912 goto out_free_dest_wim;
2916 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
2917 else if (dest_wimfile)
2918 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
2919 write_flags, num_threads);
2921 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
2922 WIMLIB_ALL_IMAGES, write_flags,
2925 wimlib_free(dest_wim);
2927 wimlib_free(src_wim);
2929 string_set_destroy(&refglobs);
2933 usage(CMD_EXPORT, stderr);
2936 goto out_free_refglobs;
2939 /* Extract files or directories from a WIM image */
2941 imagex_extract(int argc, tchar **argv, int cmd)
2948 const tchar *wimfile;
2949 const tchar *image_num_or_name;
2950 tchar *dest_dir = T(".");
2951 int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
2952 WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
2953 WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
2954 int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
2956 STRING_SET(refglobs);
2958 tchar *root_path = WIMLIB_WIM_ROOT_PATH;
2960 for_opt(c, extract_options) {
2962 case IMAGEX_CHECK_OPTION:
2963 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2965 case IMAGEX_VERBOSE_OPTION:
2966 /* No longer does anything. */
2968 case IMAGEX_REF_OPTION:
2969 ret = string_set_append(&refglobs, optarg);
2971 goto out_free_refglobs;
2973 case IMAGEX_UNIX_DATA_OPTION:
2974 extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
2976 case IMAGEX_NO_ACLS_OPTION:
2977 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
2979 case IMAGEX_STRICT_ACLS_OPTION:
2980 extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
2982 case IMAGEX_NO_ATTRIBUTES_OPTION:
2983 extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
2985 case IMAGEX_DEST_DIR_OPTION:
2988 case IMAGEX_TO_STDOUT_OPTION:
2989 extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
2990 imagex_info_file = stderr;
2991 imagex_be_quiet = true;
2992 set_fd_to_binary_mode(STDOUT_FILENO);
2994 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
2995 extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
2996 extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
2998 case IMAGEX_NO_GLOBS_OPTION:
2999 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3001 case IMAGEX_NULLGLOB_OPTION:
3002 extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3004 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3005 notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3007 case IMAGEX_WIMBOOT_OPTION:
3008 extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3020 if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3021 WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3023 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3028 image_num_or_name = argv[1];
3033 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3034 imagex_progress_func, NULL);
3036 goto out_free_refglobs;
3038 image = wimlib_resolve_image(wim, image_num_or_name);
3039 ret = verify_image_exists_and_is_single(image,
3043 goto out_wimlib_free;
3045 if (refglobs.num_strings) {
3046 ret = wim_reference_globs(wim, &refglobs, open_flags);
3048 goto out_wimlib_free;
3054 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3057 while (argc != 0 && ret == 0) {
3061 num_paths < argc && argv[num_paths][0] != T('@');
3066 ret = wimlib_extract_paths(wim, image, dest_dir,
3067 (const tchar **)argv,
3069 extract_flags | notlist_extract_flags);
3073 ret = wimlib_extract_pathlist(wim, image, dest_dir,
3082 if (!imagex_be_quiet)
3083 imagex_printf(T("Done extracting files.\n"));
3084 } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3085 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3086 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3087 == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3088 WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3091 T("Note: You can use the '--nullglob' "
3092 "option to ignore missing files.\n"));
3094 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3095 "files and directories\n"
3096 " are in the WIM image.\n"),
3097 get_cmd_string(CMD_DIR, false));
3098 } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3099 struct wimlib_wim_info info;
3101 wimlib_get_wim_info(wim, &info);
3102 do_resource_not_found_warning(wimfile, &info, &refglobs);
3107 string_set_destroy(&refglobs);
3111 usage(CMD_EXTRACT, stderr);
3114 goto out_free_refglobs;
3117 /* Prints information about a WIM file; also can mark an image as bootable,
3118 * change the name of an image, or change the description of an image. */
3120 imagex_info(int argc, tchar **argv, int cmd)
3125 bool nocheck = false;
3126 bool header = false;
3129 bool short_header = true;
3130 const tchar *xml_out_file = NULL;
3131 const tchar *wimfile;
3132 const tchar *image_num_or_name;
3133 const tchar *new_name;
3134 const tchar *new_desc;
3139 struct wimlib_wim_info info;
3141 for_opt(c, info_options) {
3143 case IMAGEX_BOOT_OPTION:
3146 case IMAGEX_CHECK_OPTION:
3149 case IMAGEX_NOCHECK_OPTION:
3152 case IMAGEX_HEADER_OPTION:
3154 short_header = false;
3156 case IMAGEX_BLOBS_OPTION:
3158 short_header = false;
3160 case IMAGEX_XML_OPTION:
3162 short_header = false;
3164 case IMAGEX_EXTRACT_XML_OPTION:
3165 xml_out_file = optarg;
3166 short_header = false;
3168 case IMAGEX_METADATA_OPTION:
3169 imagex_error(T("The --metadata option has been removed. "
3170 "Use 'wimdir --detail' instead."));
3179 if (argc < 1 || argc > 4)
3183 image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3184 new_name = (argc >= 3) ? argv[2] : NULL;
3185 new_desc = (argc >= 4) ? argv[3] : NULL;
3187 if (check && nocheck) {
3188 imagex_error(T("Can't specify both --check and --nocheck"));
3193 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3195 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3196 imagex_progress_func, NULL);
3200 wimlib_get_wim_info(wim, &info);
3202 image = wimlib_resolve_image(wim, image_num_or_name);
3203 ret = WIMLIB_ERR_INVALID_IMAGE;
3204 if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3205 verify_image_exists(image, image_num_or_name, wimfile);
3207 imagex_error(T("If you would like to set the boot "
3208 "index to 0, specify image \"0\" with "
3209 "the --boot flag."));
3211 goto out_wimlib_free;
3214 if (boot && info.image_count == 0) {
3215 imagex_error(T("--boot is meaningless on a WIM with no images"));
3216 goto out_wimlib_free;
3219 if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3221 imagex_error(T("Cannot specify the --boot flag "
3222 "without specifying a specific "
3223 "image in a multi-image WIM"));
3224 goto out_wimlib_free;
3227 imagex_error(T("Cannot specify the NEW_NAME "
3228 "without specifying a specific "
3229 "image in a multi-image WIM"));
3230 goto out_wimlib_free;
3234 /* Operations that print information are separated from operations that
3235 * recreate the WIM file. */
3236 if (!new_name && !boot) {
3238 /* Read-only operations */
3240 if (image == WIMLIB_NO_IMAGE) {
3241 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3242 image_num_or_name, wimfile);
3243 goto out_wimlib_free;
3246 if (image == WIMLIB_ALL_IMAGES && short_header)
3247 print_wim_information(wimfile, &info);
3250 wimlib_print_header(wim);
3253 if (info.total_parts != 1) {
3254 tfprintf(stderr, T("Warning: Only showing the blobs "
3255 "for part %d of a %d-part WIM.\n"),
3256 info.part_number, info.total_parts);
3262 ret = wimlib_extract_xml_data(wim, stdout);
3264 goto out_wimlib_free;
3270 fp = tfopen(xml_out_file, T("wb"));
3272 imagex_error_with_errno(T("Failed to open the "
3273 "file \"%"TS"\" for "
3277 goto out_wimlib_free;
3279 ret = wimlib_extract_xml_data(wim, fp);
3281 imagex_error(T("Failed to close the file "
3287 goto out_wimlib_free;
3291 wimlib_print_available_images(wim, image);
3296 /* Modification operations */
3298 if (image == WIMLIB_ALL_IMAGES)
3301 if (image == WIMLIB_NO_IMAGE && new_name) {
3302 imagex_error(T("Cannot specify new_name (\"%"TS"\") "
3303 "when using image 0"), new_name);
3305 goto out_wimlib_free;
3309 if (image == info.boot_index) {
3310 imagex_printf(T("Image %d is already marked as "
3311 "bootable.\n"), image);
3314 imagex_printf(T("Marking image %d as bootable.\n"),
3316 info.boot_index = image;
3317 ret = wimlib_set_wim_info(wim, &info,
3318 WIMLIB_CHANGE_BOOT_INDEX);
3320 goto out_wimlib_free;
3324 if (!tstrcmp(wimlib_get_image_name(wim, image), new_name))
3326 imagex_printf(T("Image %d is already named \"%"TS"\".\n"),
3330 imagex_printf(T("Changing the name of image %d to "
3331 "\"%"TS"\".\n"), image, new_name);
3332 ret = wimlib_set_image_name(wim, image, new_name);
3334 goto out_wimlib_free;
3338 const tchar *old_desc;
3339 old_desc = wimlib_get_image_description(wim, image);
3340 if (old_desc && !tstrcmp(old_desc, new_desc)) {
3341 imagex_printf(T("The description of image %d is already "
3342 "\"%"TS"\".\n"), image, new_desc);
3345 imagex_printf(T("Changing the description of image %d "
3346 "to \"%"TS"\".\n"), image, new_desc);
3347 ret = wimlib_set_image_descripton(wim, image,
3350 goto out_wimlib_free;
3354 /* Only call wimlib_overwrite() if something actually needs to
3356 if (boot || new_name || new_desc ||
3357 (check && !info.has_integrity_table) ||
3358 (nocheck && info.has_integrity_table))
3360 int write_flags = 0;
3363 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3365 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3366 ret = wimlib_overwrite(wim, write_flags, 1);
3368 imagex_printf(T("The file \"%"TS"\" was not modified "
3369 "because nothing needed to be done.\n"),
3380 usage(CMD_INFO, stderr);
3386 /* Join split WIMs into one part WIM */
3388 imagex_join(int argc, tchar **argv, int cmd)
3391 int swm_open_flags = 0;
3392 int wim_write_flags = 0;
3393 const tchar *output_path;
3396 for_opt(c, join_options) {
3398 case IMAGEX_CHECK_OPTION:
3399 swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3400 wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3410 imagex_error(T("Must specify one or more split WIM (.swm) "
3414 output_path = argv[0];
3415 ret = wimlib_join_with_progress((const tchar * const *)++argv,
3420 imagex_progress_func,
3426 usage(CMD_JOIN, stderr);
3431 #if WIM_MOUNTING_SUPPORTED
3433 /* Mounts a WIM image. */
3435 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3438 int mount_flags = 0;
3440 const tchar *staging_dir = NULL;
3441 const tchar *wimfile;
3444 struct wimlib_wim_info info;
3448 STRING_SET(refglobs);
3450 if (cmd == CMD_MOUNTRW) {
3451 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3452 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3455 for_opt(c, mount_options) {
3457 case IMAGEX_ALLOW_OTHER_OPTION:
3458 mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3460 case IMAGEX_CHECK_OPTION:
3461 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3463 case IMAGEX_DEBUG_OPTION:
3464 mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3466 case IMAGEX_STREAMS_INTERFACE_OPTION:
3467 if (!tstrcasecmp(optarg, T("none")))
3468 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3469 else if (!tstrcasecmp(optarg, T("xattr")))
3470 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3471 else if (!tstrcasecmp(optarg, T("windows")))
3472 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3474 imagex_error(T("Unknown stream interface \"%"TS"\""),
3479 case IMAGEX_REF_OPTION:
3480 ret = string_set_append(&refglobs, optarg);
3482 goto out_free_refglobs;
3484 case IMAGEX_STAGING_DIR_OPTION:
3485 staging_dir = optarg;
3487 case IMAGEX_UNIX_DATA_OPTION:
3488 mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3496 if (argc != 2 && argc != 3)
3501 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3502 imagex_progress_func, NULL);
3504 goto out_free_refglobs;
3506 wimlib_get_wim_info(wim, &info);
3509 /* Image explicitly specified. */
3510 image = wimlib_resolve_image(wim, argv[1]);
3512 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3516 /* No image specified; default to image 1, but only if the WIM
3517 * contains exactly one image. */
3519 if (info.image_count != 1) {
3520 imagex_error(T("\"%"TS"\" contains %d images; Please "
3521 "select one."), wimfile, info.image_count);
3529 if (refglobs.num_strings) {
3530 ret = wim_reference_globs(wim, &refglobs, open_flags);
3535 ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3537 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3539 image, wimfile, dir);
3544 string_set_destroy(&refglobs);
3550 goto out_free_refglobs;
3552 #endif /* WIM_MOUNTING_SUPPORTED */
3554 /* Rebuild a WIM file */
3556 imagex_optimize(int argc, tchar **argv, int cmd)
3559 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3560 int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3561 int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3562 uint32_t chunk_size = UINT32_MAX;
3563 uint32_t solid_chunk_size = UINT32_MAX;
3564 int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3567 const tchar *wimfile;
3570 unsigned num_threads = 0;
3572 for_opt(c, optimize_options) {
3574 case IMAGEX_CHECK_OPTION:
3575 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3576 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3578 case IMAGEX_NOCHECK_OPTION:
3579 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3581 case IMAGEX_COMPRESS_OPTION:
3582 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3583 compression_type = get_compression_type(optarg);
3584 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3587 case IMAGEX_COMPRESS_SLOW_OPTION:
3588 set_compress_slow();
3589 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3591 case IMAGEX_RECOMPRESS_OPTION:
3592 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3594 case IMAGEX_CHUNK_SIZE_OPTION:
3595 chunk_size = parse_chunk_size(optarg);
3596 if (chunk_size == UINT32_MAX)
3599 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3600 solid_chunk_size = parse_chunk_size(optarg);
3601 if (solid_chunk_size == UINT32_MAX)
3604 case IMAGEX_SOLID_COMPRESS_OPTION:
3605 solid_ctype = get_compression_type(optarg);
3606 if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3609 case IMAGEX_SOLID_OPTION:
3610 write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3611 write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3613 case IMAGEX_NO_SOLID_SORT_OPTION:
3614 write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3616 case IMAGEX_THREADS_OPTION:
3617 num_threads = parse_num_threads(optarg);
3618 if (num_threads == UINT_MAX)
3621 case IMAGEX_PIPABLE_OPTION:
3622 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3624 case IMAGEX_NOT_PIPABLE_OPTION:
3625 write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3639 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3640 imagex_progress_func, NULL);
3644 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3645 /* Change compression type. */
3646 ret = wimlib_set_output_compression_type(wim, compression_type);
3648 goto out_wimlib_free;
3651 if (chunk_size != UINT32_MAX) {
3652 /* Change chunk size. */
3653 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3655 goto out_wimlib_free;
3657 if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3658 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3660 goto out_wimlib_free;
3662 if (solid_chunk_size != UINT32_MAX) {
3663 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3665 goto out_wimlib_free;
3668 old_size = file_get_size(wimfile);
3669 tprintf(T("\"%"TS"\" original size: "), wimfile);
3671 tputs(T("Unknown"));
3673 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3675 ret = wimlib_overwrite(wim, write_flags, num_threads);
3677 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3678 goto out_wimlib_free;
3681 new_size = file_get_size(wimfile);
3682 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3684 tputs(T("Unknown"));
3686 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3688 tfputs(T("Space saved: "), stdout);
3689 if (new_size != -1 && old_size != -1) {
3690 tprintf(T("%lld KiB\n"),
3691 ((long long)old_size - (long long)new_size) >> 10);
3693 tputs(T("Unknown"));
3702 usage(CMD_OPTIMIZE, stderr);
3708 /* Split a WIM into a spanned set */
3710 imagex_split(int argc, tchar **argv, int cmd)
3714 int write_flags = 0;
3715 unsigned long part_size;
3720 for_opt(c, split_options) {
3722 case IMAGEX_CHECK_OPTION:
3723 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3724 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3736 part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3737 if (tmp == argv[2] || *tmp) {
3738 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3739 imagex_error(T("The part size must be an integer or "
3740 "floating-point number of megabytes."));
3743 ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3744 imagex_progress_func, NULL);
3748 ret = wimlib_split(wim, argv[1], part_size, write_flags);
3754 usage(CMD_SPLIT, stderr);
3760 #if WIM_MOUNTING_SUPPORTED
3761 /* Unmounts a mounted WIM image. */
3763 imagex_unmount(int argc, tchar **argv, int cmd)
3766 int unmount_flags = 0;
3769 for_opt(c, unmount_options) {
3771 case IMAGEX_COMMIT_OPTION:
3772 unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3774 case IMAGEX_CHECK_OPTION:
3775 unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3777 case IMAGEX_REBUILD_OPTION:
3778 unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3780 case IMAGEX_LAZY_OPTION:
3781 case IMAGEX_FORCE_OPTION:
3782 /* Now, unmount is lazy by default. However, committing
3783 * the image will fail with
3784 * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3785 * file descriptors on the WIM image. The
3786 * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3787 * descriptors to be closed. */
3788 unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3790 case IMAGEX_NEW_IMAGE_OPTION:
3791 unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
3802 if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
3803 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
3804 imagex_error(T("--new-image is meaningless "
3805 "without --commit also specified!"));
3810 ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
3811 imagex_progress_func, NULL);
3813 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
3814 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
3816 "\tNote: Use --commit --force to force changes "
3817 "to be committed, regardless\n"
3818 "\t of open files.\n"));
3825 usage(CMD_UNMOUNT, stderr);
3830 #endif /* WIM_MOUNTING_SUPPORTED */
3833 * Add, delete, or rename files in a WIM image.
3836 imagex_update(int argc, tchar **argv, int cmd)
3838 const tchar *wimfile;
3842 int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3843 int write_flags = 0;
3844 int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
3845 int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
3846 WIMLIB_ADD_FLAG_VERBOSE |
3847 WIMLIB_ADD_FLAG_WINCONFIG;
3848 int default_delete_flags = 0;
3849 unsigned num_threads = 0;
3851 tchar *cmd_file_contents;
3852 size_t cmd_file_nchars;
3853 struct wimlib_update_command *cmds;
3855 tchar *command_str = NULL;
3856 tchar *config_file = NULL;
3857 tchar *wimboot_config = NULL;
3859 for_opt(c, update_options) {
3861 /* Generic or write options */
3862 case IMAGEX_THREADS_OPTION:
3863 num_threads = parse_num_threads(optarg);
3864 if (num_threads == UINT_MAX)
3867 case IMAGEX_CHECK_OPTION:
3868 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3869 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3871 case IMAGEX_REBUILD_OPTION:
3872 write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
3874 case IMAGEX_COMMAND_OPTION:
3876 imagex_error(T("--command may only be specified "
3877 "one time. Please provide\n"
3878 " the update commands "
3879 "on standard input instead."));
3882 command_str = tstrdup(optarg);
3884 imagex_error(T("Out of memory!"));
3888 case IMAGEX_WIMBOOT_CONFIG_OPTION:
3889 wimboot_config = optarg;
3891 /* Default delete options */
3892 case IMAGEX_FORCE_OPTION:
3893 default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
3895 case IMAGEX_RECURSIVE_OPTION:
3896 default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
3899 /* Global add option */
3900 case IMAGEX_CONFIG_OPTION:
3901 default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
3902 config_file = optarg;
3905 /* Default add options */
3906 case IMAGEX_VERBOSE_OPTION:
3907 /* No longer does anything. */
3909 case IMAGEX_DEREFERENCE_OPTION:
3910 default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
3912 case IMAGEX_UNIX_DATA_OPTION:
3913 default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
3915 case IMAGEX_NO_ACLS_OPTION:
3916 default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
3918 case IMAGEX_STRICT_ACLS_OPTION:
3919 default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
3921 case IMAGEX_NO_REPLACE_OPTION:
3922 default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
3931 if (argc != 1 && argc != 2)
3935 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3936 imagex_progress_func, NULL);
3938 goto out_free_command_str;
3941 /* Image explicitly specified. */
3942 image = wimlib_resolve_image(wim, argv[1]);
3943 ret = verify_image_exists_and_is_single(image, argv[1],
3946 goto out_wimlib_free;
3948 /* No image specified; default to image 1, but only if the WIM
3949 * contains exactly one image. */
3950 struct wimlib_wim_info info;
3952 wimlib_get_wim_info(wim, &info);
3953 if (info.image_count != 1) {
3954 imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
3955 wimfile, info.image_count);
3962 /* Read update commands from standard input, or the command string if
3965 cmd_file_contents = NULL;
3966 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
3970 goto out_free_cmd_file_contents;
3972 } else if (!wimboot_config) {
3973 if (isatty(STDIN_FILENO)) {
3974 tputs(T("Reading update commands from standard input..."));
3975 recommend_man_page(CMD_UPDATE, stdout);
3977 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
3978 if (!cmd_file_contents) {
3980 goto out_wimlib_free;
3983 /* Parse the update commands */
3984 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
3988 goto out_free_cmd_file_contents;
3991 cmd_file_contents = NULL;
3996 /* Set default flags and capture config on the update commands */
3997 for (size_t i = 0; i < num_cmds; i++) {
3998 switch (cmds[i].op) {
3999 case WIMLIB_UPDATE_OP_ADD:
4000 cmds[i].add.add_flags |= default_add_flags;
4001 cmds[i].add.config_file = config_file;
4003 case WIMLIB_UPDATE_OP_DELETE:
4004 cmds[i].delete_.delete_flags |= default_delete_flags;
4011 /* Execute the update commands */
4012 ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4016 if (wimboot_config) {
4017 /* --wimboot-config=FILE is short for an
4018 * "add FILE /Windows/System32/WimBootCompress.ini" command.
4020 struct wimlib_update_command cmd;
4022 cmd.op = WIMLIB_UPDATE_OP_ADD;
4023 cmd.add.fs_source_path = wimboot_config;
4024 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4025 cmd.add.config_file = NULL;
4026 cmd.add.add_flags = 0;
4028 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4033 /* Overwrite the updated WIM */
4034 ret = wimlib_overwrite(wim, write_flags, num_threads);
4037 out_free_cmd_file_contents:
4038 free(cmd_file_contents);
4041 out_free_command_str:
4046 usage(CMD_UPDATE, stderr);
4049 goto out_free_command_str;
4052 /* Verify a WIM file. */
4054 imagex_verify(int argc, tchar **argv, int cmd)
4057 const tchar *wimfile;
4059 int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4060 int verify_flags = 0;
4061 STRING_SET(refglobs);
4064 for_opt(c, verify_options) {
4066 case IMAGEX_REF_OPTION:
4067 ret = string_set_append(&refglobs, optarg);
4069 goto out_free_refglobs;
4071 case IMAGEX_NOCHECK_OPTION:
4072 open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4084 imagex_error(T("Must specify a WIM file!"));
4086 imagex_error(T("At most one WIM file can be specified!"));
4092 ret = wimlib_open_wim_with_progress(wimfile,
4095 imagex_progress_func,
4098 goto out_free_refglobs;
4100 ret = wim_reference_globs(wim, &refglobs, open_flags);
4102 goto out_wimlib_free;
4104 ret = wimlib_verify_wim(wim, verify_flags);
4106 tputc(T('\n'), stderr);
4107 imagex_error(T("\"%"TS"\" failed verification!"),
4109 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4110 refglobs.num_strings == 0)
4112 imagex_printf(T("Note: if this WIM file is not standalone, "
4113 "use the --ref option to specify the other parts.\n"));
4116 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4123 string_set_destroy(&refglobs);
4127 usage(CMD_VERIFY, stderr);
4129 goto out_free_refglobs;
4132 struct imagex_command {
4134 int (*func)(int argc, tchar **argv, int cmd);
4137 static const struct imagex_command imagex_commands[] = {
4138 [CMD_APPEND] = {T("append"), imagex_capture_or_append},
4139 [CMD_APPLY] = {T("apply"), imagex_apply},
4140 [CMD_CAPTURE] = {T("capture"), imagex_capture_or_append},
4141 [CMD_DELETE] = {T("delete"), imagex_delete},
4142 [CMD_DIR ] = {T("dir"), imagex_dir},
4143 [CMD_EXPORT] = {T("export"), imagex_export},
4144 [CMD_EXTRACT] = {T("extract"), imagex_extract},
4145 [CMD_INFO] = {T("info"), imagex_info},
4146 [CMD_JOIN] = {T("join"), imagex_join},
4147 #if WIM_MOUNTING_SUPPORTED
4148 [CMD_MOUNT] = {T("mount"), imagex_mount_rw_or_ro},
4149 [CMD_MOUNTRW] = {T("mountrw"), imagex_mount_rw_or_ro},
4151 [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4152 [CMD_SPLIT] = {T("split"), imagex_split},
4153 #if WIM_MOUNTING_SUPPORTED
4154 [CMD_UNMOUNT] = {T("unmount"), imagex_unmount},
4156 [CMD_UPDATE] = {T("update"), imagex_update},
4157 [CMD_VERIFY] = {T("verify"), imagex_verify},
4162 /* Can be a directory or source list file. But source list file is probably
4163 * a rare use case, so just say directory. */
4164 # define SOURCE_STR T("DIRECTORY")
4166 /* Can only be a directory */
4167 # define TARGET_STR T("DIRECTORY")
4170 /* Can be a directory, NTFS volume, or source list file. */
4171 # define SOURCE_STR T("SOURCE")
4173 /* Can be a directory or NTFS volume. */
4174 # define TARGET_STR T("TARGET")
4178 static const tchar *usage_strings[] = {
4181 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4182 " [--boot] [--check] [--nocheck] [--config=FILE]\n"
4183 " [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4184 " [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4185 " [--wimboot] [--unix-data] [--dereference]\n"
4189 " %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4190 " [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4191 " [--no-attributes] [--rpfix] [--norpfix]\n"
4192 " [--include-invalid-names] [--wimboot] [--unix-data]\n"
4196 " %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4197 " [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4198 " [--config=FILE] [--threads=NUM_THREADS]\n"
4199 " [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4200 " [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4201 " [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4205 " %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4209 " %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4213 " %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4214 " [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4215 " [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4216 " [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4217 " [--wimboot] [--solid]\n"
4221 " %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4222 " [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4223 " [--to-stdout] [--no-acls] [--strict-acls]\n"
4224 " [--no-attributes] [--include-invalid-names]\n"
4225 " [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4229 " %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4230 " [--boot] [--check] [--nocheck] [--xml]\n"
4231 " [--extract-xml FILE] [--header] [--blobs]\n"
4235 " %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4237 #if WIM_MOUNTING_SUPPORTED
4240 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4241 " [--check] [--streams-interface=INTERFACE]\n"
4242 " [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4246 " %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4247 " [--check] [--streams-interface=INTERFACE]\n"
4248 " [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4254 " [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4255 " [--check] [--nocheck] [--solid]\n"
4260 " %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4262 #if WIM_MOUNTING_SUPPORTED
4265 " %"TS" DIRECTORY\n"
4266 " [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4271 " %"TS" WIMFILE [IMAGE]\n"
4272 " [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4273 " [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4274 " [--command=STRING] [--wimboot-config=FILE]\n"
4279 " %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4283 static const tchar *invocation_name;
4284 static int invocation_cmd = CMD_NONE;
4286 static const tchar *get_cmd_string(int cmd, bool nospace)
4288 static tchar buf[50];
4289 if (cmd == CMD_NONE) {
4290 return T("wimlib-imagex");
4291 } else if (invocation_cmd != CMD_NONE) {
4292 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4294 const tchar *format;
4297 format = T("%"TS"-%"TS"");
4299 format = T("%"TS" %"TS"");
4300 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4308 static const tchar *s =
4310 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4311 "Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers\n"
4312 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4313 "This is free software: you are free to change and redistribute it.\n"
4314 "There is NO WARRANTY, to the extent permitted by law.\n"
4316 "Report bugs to "PACKAGE_BUGREPORT".\n"
4323 help_or_version(int argc, tchar **argv, int cmd)
4328 for (i = 1; i < argc; i++) {
4330 if (p[0] == T('-') && p[1] == T('-')) {
4332 if (!tstrcmp(p, T("help"))) {
4333 if (cmd == CMD_NONE)
4338 } else if (!tstrcmp(p, T("version"))) {
4347 print_usage_string(int cmd, FILE *fp)
4349 tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4353 recommend_man_page(int cmd, FILE *fp)
4355 const tchar *format_str;
4357 format_str = T("Some uncommon options are not listed;\n"
4358 "See %"TS".pdf in the doc directory for more details.\n");
4360 format_str = T("Some uncommon options are not listed;\n"
4361 "Try `man %"TS"' for more details.\n");
4363 tfprintf(fp, format_str, get_cmd_string(cmd, true));
4367 usage(int cmd, FILE *fp)
4369 tfprintf(fp, T("Usage:\n"));
4370 print_usage_string(cmd, fp);
4371 tfprintf(fp, T("\n"));
4372 recommend_man_page(cmd, fp);
4378 tfprintf(fp, T("Usage:\n"));
4379 for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4380 print_usage_string(cmd, fp);
4381 tfprintf(fp, T("\n"));
4383 static const tchar *extra =
4386 " %"TS" --version\n"
4389 tfprintf(fp, extra, invocation_name, invocation_name);
4391 T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4392 "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4393 "For some commands IMAGE may be \"all\".\n"
4395 recommend_man_page(CMD_NONE, fp);
4398 /* Entry point for wimlib's ImageX implementation. On UNIX the command
4399 * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4400 * something else), while on Windows the command arguments will be UTF-16LE
4401 * encoded 'wchar_t' strings. */
4404 wmain(int argc, wchar_t **argv, wchar_t **envp)
4406 main(int argc, char **argv)
4413 imagex_info_file = stdout;
4414 invocation_name = tbasename(argv[0]);
4417 if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4418 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4422 setlocale(LC_ALL, "");
4423 codeset = nl_langinfo(CODESET);
4424 if (!strstr(codeset, "UTF-8") &&
4425 !strstr(codeset, "UTF8") &&
4426 !strstr(codeset, "utf-8") &&
4427 !strstr(codeset, "utf8"))
4430 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4431 " Maybe try: `export LANG=en_US.UTF-8'?\n"
4432 " Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4433 " to any value to force wimlib to use UTF-8.\n",
4439 #endif /* !__WIN32__ */
4442 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4443 if (igcase != NULL) {
4444 if (!tstrcmp(igcase, T("no")) ||
4445 !tstrcmp(igcase, T("0")))
4446 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4447 else if (!tstrcmp(igcase, T("yes")) ||
4448 !tstrcmp(igcase, T("1")))
4449 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4452 "WARNING: Ignoring unknown setting of "
4453 "WIMLIB_IMAGEX_IGNORE_CASE\n");
4458 /* Allow being invoked as wimCOMMAND (e.g. wimapply). */
4460 if (!tstrncmp(invocation_name, T("wim"), 3) &&
4461 tstrcmp(invocation_name, T("wimlib-imagex"))) {
4462 for (int i = 0; i < CMD_MAX; i++) {
4463 if (!tstrcmp(invocation_name + 3,
4464 imagex_commands[i].name))
4473 /* Unless already known from the invocation name, determine which
4474 * command was specified. */
4475 if (cmd == CMD_NONE) {
4477 imagex_error(T("No command specified!\n"));
4481 for (int i = 0; i < CMD_MAX; i++) {
4482 if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4487 if (cmd != CMD_NONE) {
4493 /* Handle --help and --version. --help can be either for the program as
4494 * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4495 * CMD_NONE). Note: help_or_version() will not return if a --help or
4496 * --version argument was found. */
4497 help_or_version(argc, argv, cmd);
4499 /* Bail if a valid command was not specified. */
4500 if (cmd == CMD_NONE) {
4501 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4506 /* Enable warning and error messages in wimlib to be more user-friendly.
4508 wimlib_set_print_errors(true);
4510 /* Initialize wimlib. */
4511 ret = wimlib_global_init(init_flags);
4513 goto out_check_status;
4515 /* Call the command handler function. */
4516 ret = imagex_commands[cmd].func(argc, argv, cmd);
4518 /* Check for error writing to standard output, especially since for some
4519 * commands, writing to standard output is part of the program's actual
4520 * behavior and not just for informational purposes. */
4521 if (ferror(stdout) || fclose(stdout)) {
4522 imagex_error_with_errno(T("error writing to standard output"));
4527 /* Exit status (ret): -1 indicates an error found by 'wimlib-imagex'
4528 * itself (not by wimlib). 0 indicates success. > 0 indicates a wimlib
4529 * error code from which an error message can be printed. */
4531 imagex_error(T("Exiting with error code %d:\n"
4533 wimlib_get_error_string(ret));
4534 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4535 imagex_error_with_errno(T("errno"));
4537 /* Make wimlib free any resources it's holding (although this is not
4538 * strictly necessary because the process is ending anyway). */
4539 wimlib_global_cleanup();