]> wimlib.net Git - wimlib/blob - programs/imagex.c
Add Windows tests for empty and max length reparse points
[wimlib] / programs / imagex.c
1 /*
2  * imagex.c
3  *
4  * Use wimlib to create, modify, extract, mount, unmount, or display information
5  * about a WIM file
6  */
7
8 /*
9  * Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers
10  *
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.
15  *
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.
20  *
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/>.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h" /* Need for PACKAGE_VERSION, etc. */
27 #endif
28
29 #include "wimlib.h"
30 #include "wimlib_tchar.h"
31
32 #include <ctype.h>
33 #include <errno.h>
34
35 #include <inttypes.h>
36 #include <libgen.h>
37 #include <limits.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <locale.h>
44
45 #ifdef HAVE_ALLOCA_H
46 #  include <alloca.h>
47 #endif
48
49 #define WIMLIB_COMPRESSION_TYPE_INVALID (-1)
50
51 #ifdef __WIN32__
52 #  include "imagex-win32.h"
53 #  define print_security_descriptor     win32_print_security_descriptor
54 #else /* __WIN32__ */
55 #  include <getopt.h>
56 #  include <langinfo.h>
57 #  define print_security_descriptor     default_print_security_descriptor
58 static inline void set_fd_to_binary_mode(int fd)
59 {
60 }
61 #endif /* !__WIN32 */
62
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.  */
67 #ifdef __WIN32__
68 #  define WIM_MOUNTING_SUPPORTED 0
69 #else
70 #  define WIM_MOUNTING_SUPPORTED 1
71 #endif
72
73 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
74
75 static inline bool
76 is_any_path_separator(tchar c)
77 {
78         return c == T('/') || c == T('\\');
79 }
80
81 /* Like basename(), but handles both forward and backwards slashes.  */
82 static tchar *
83 tbasename(tchar *path)
84 {
85         tchar *p = tstrchr(path, T('\0'));
86
87         for (;;) {
88                 if (p == path)
89                         return path;
90                 if (!is_any_path_separator(*--p))
91                         break;
92                 *p = T('\0');
93         }
94
95         for (;;) {
96                 if (p == path)
97                         return path;
98                 if (is_any_path_separator(*--p))
99                         return ++p;
100         }
101 }
102
103 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
104                                 opts, NULL)) != -1)
105
106 enum {
107         CMD_NONE = -1,
108         CMD_APPEND = 0,
109         CMD_APPLY,
110         CMD_CAPTURE,
111         CMD_DELETE,
112         CMD_DIR,
113         CMD_EXPORT,
114         CMD_EXTRACT,
115         CMD_INFO,
116         CMD_JOIN,
117 #if WIM_MOUNTING_SUPPORTED
118         CMD_MOUNT,
119         CMD_MOUNTRW,
120 #endif
121         CMD_OPTIMIZE,
122         CMD_SPLIT,
123 #if WIM_MOUNTING_SUPPORTED
124         CMD_UNMOUNT,
125 #endif
126         CMD_UPDATE,
127         CMD_VERIFY,
128         CMD_MAX,
129 };
130
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);
135
136 static bool imagex_be_quiet = false;
137 static FILE *imagex_info_file;
138
139 #define imagex_printf(format, ...) \
140                 tfprintf(imagex_info_file, format, ##__VA_ARGS__)
141
142 enum {
143         IMAGEX_ALLOW_OTHER_OPTION,
144         IMAGEX_BLOBS_OPTION,
145         IMAGEX_BOOT_OPTION,
146         IMAGEX_CHECK_OPTION,
147         IMAGEX_CHUNK_SIZE_OPTION,
148         IMAGEX_COMMAND_OPTION,
149         IMAGEX_COMMIT_OPTION,
150         IMAGEX_COMPRESS_OPTION,
151         IMAGEX_COMPRESS_SLOW_OPTION,
152         IMAGEX_CONFIG_OPTION,
153         IMAGEX_DEBUG_OPTION,
154         IMAGEX_DELTA_FROM_OPTION,
155         IMAGEX_DEREFERENCE_OPTION,
156         IMAGEX_DEST_DIR_OPTION,
157         IMAGEX_DETAILED_OPTION,
158         IMAGEX_EXTRACT_XML_OPTION,
159         IMAGEX_FLAGS_OPTION,
160         IMAGEX_FORCE_OPTION,
161         IMAGEX_HEADER_OPTION,
162         IMAGEX_INCLUDE_INVALID_NAMES_OPTION,
163         IMAGEX_LAZY_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_GLOBS_OPTION,
172         IMAGEX_NO_REPLACE_OPTION,
173         IMAGEX_NO_SOLID_SORT_OPTION,
174         IMAGEX_NULLGLOB_OPTION,
175         IMAGEX_ONE_FILE_ONLY_OPTION,
176         IMAGEX_PATH_OPTION,
177         IMAGEX_PIPABLE_OPTION,
178         IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
179         IMAGEX_REBUILD_OPTION,
180         IMAGEX_RECOMPRESS_OPTION,
181         IMAGEX_RECURSIVE_OPTION,
182         IMAGEX_REF_OPTION,
183         IMAGEX_RESUME_OPTION,
184         IMAGEX_RPFIX_OPTION,
185         IMAGEX_SOFT_OPTION,
186         IMAGEX_SOLID_CHUNK_SIZE_OPTION,
187         IMAGEX_SOLID_COMPRESS_OPTION,
188         IMAGEX_SOLID_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_CONFIG_OPTION,
199         IMAGEX_WIMBOOT_OPTION,
200         IMAGEX_XML_OPTION,
201 };
202
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},
215
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},
219         {NULL, 0, NULL, 0},
220 };
221
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},
255         {NULL, 0, NULL, 0},
256 };
257
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},
261         {NULL, 0, NULL, 0},
262 };
263
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},
268         {NULL, 0, NULL, 0},
269 };
270
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},
293         {NULL, 0, NULL, 0},
294 };
295
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},
313         {NULL, 0, NULL, 0},
314 };
315
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},
327         {NULL, 0, NULL, 0},
328 };
329
330 static const struct option join_options[] = {
331         {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
332         {NULL, 0, NULL, 0},
333 };
334
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},
343         {NULL, 0, NULL, 0},
344 };
345
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},
365         {NULL, 0, NULL, 0},
366 };
367
368 static const struct option split_options[] = {
369         {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
370         {NULL, 0, NULL, 0},
371 };
372
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},
380         {NULL, 0, NULL, 0},
381 };
382
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},
393
394         /* Default delete options */
395         {T("force"),       no_argument,       NULL, IMAGEX_FORCE_OPTION},
396         {T("recursive"),   no_argument,       NULL, IMAGEX_RECURSIVE_OPTION},
397
398         /* Global add option */
399         {T("config"),      required_argument, NULL, IMAGEX_CONFIG_OPTION},
400
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},
409
410         {NULL, 0, NULL, 0},
411 };
412
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},
416
417         {NULL, 0, NULL, 0},
418 };
419
420 #if 0
421 #       define _format_attribute(type, format_str, args_start) \
422                         __attribute__((format(type, format_str, args_start)))
423 #else
424 #       define _format_attribute(type, format_str, args_start)
425 #endif
426
427 /* Print formatted error message to stderr. */
428 static void _format_attribute(printf, 1, 2)
429 imagex_error(const tchar *format, ...)
430 {
431         va_list va;
432         va_start(va, format);
433         tfputs(T("ERROR: "), stderr);
434         tvfprintf(stderr, format, va);
435         tputc(T('\n'), stderr);
436         va_end(va);
437 }
438
439 /* Print formatted error message to stderr. */
440 static void _format_attribute(printf, 1, 2)
441 imagex_error_with_errno(const tchar *format, ...)
442 {
443         int errno_save = errno;
444         va_list va;
445         va_start(va, format);
446         tfputs(T("ERROR: "), stderr);
447         tvfprintf(stderr, format, va);
448         tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
449         va_end(va);
450 }
451
452 static int
453 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
454 {
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"
460                              "\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;
465         }
466         return 0;
467 }
468
469 static int
470 verify_image_is_single(int image)
471 {
472         if (image == WIMLIB_ALL_IMAGES) {
473                 imagex_error(T("Cannot specify all images for this action!"));
474                 return WIMLIB_ERR_INVALID_IMAGE;
475         }
476         return 0;
477 }
478
479 static int
480 verify_image_exists_and_is_single(int image, const tchar *image_name,
481                                   const tchar *wim_name)
482 {
483         int ret;
484         ret = verify_image_exists(image, image_name, wim_name);
485         if (ret == 0)
486                 ret = verify_image_is_single(image);
487         return ret;
488 }
489
490 static void
491 print_available_compression_types(FILE *fp)
492 {
493         static const tchar *s =
494         T(
495         "Available compression types:\n"
496         "\n"
497         "    none\n"
498         "    xpress (alias: \"fast\")\n"
499         "    lzx    (alias: \"maximum\") (default for capture)\n"
500         "    lzms   (alias: \"recovery\")\n"
501         "\n"
502         );
503         tfputs(s, fp);
504 }
505
506 /* Parse the argument to --compress */
507 static int
508 get_compression_type(tchar *optarg)
509 {
510         int ctype;
511         unsigned int compression_level = 0;
512         tchar *plevel;
513
514         plevel = tstrchr(optarg, T(':'));
515         if (plevel) {
516                 tchar *ptmp;
517                 unsigned long ultmp;
518
519                 *plevel++ = T('\0');
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;
525                 }
526                 compression_level = ultmp;
527         }
528
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;
539         else {
540                 imagex_error(T("Invalid compression type \"%"TS"\"!"), optarg);
541                 print_available_compression_types(stderr);
542                 return WIMLIB_COMPRESSION_TYPE_INVALID;
543         }
544
545         if (compression_level != 0)
546                 wimlib_set_default_compression_level(ctype, compression_level);
547         return ctype;
548 }
549
550 static void
551 set_compress_slow(void)
552 {
553 #if 0
554         fprintf(stderr, "WARNING: the '--compress-slow' option is deprecated.\n"
555                         "         Use the '--compress=TYPE:LEVEL' option instead.\n");
556 #endif
557         wimlib_set_default_compression_level(-1, 100);
558 }
559
560 struct string_set {
561         const tchar **strings;
562         unsigned num_strings;
563         unsigned num_alloc_strings;
564 };
565
566 #define STRING_SET_INITIALIZER \
567         { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
568
569 #define STRING_SET(_strings) \
570         struct string_set _strings = STRING_SET_INITIALIZER
571
572 static int
573 string_set_append(struct string_set *set, const tchar *glob)
574 {
575         unsigned num_alloc_strings = set->num_alloc_strings;
576
577         if (set->num_strings == num_alloc_strings) {
578                 const tchar **new_strings;
579
580                 num_alloc_strings += 4;
581                 new_strings = realloc(set->strings,
582                                       sizeof(set->strings[0]) * num_alloc_strings);
583                 if (!new_strings) {
584                         imagex_error(T("Out of memory!"));
585                         return -1;
586                 }
587                 set->strings = new_strings;
588                 set->num_alloc_strings = num_alloc_strings;
589         }
590         set->strings[set->num_strings++] = glob;
591         return 0;
592 }
593
594 static void
595 string_set_destroy(struct string_set *set)
596 {
597         free(set->strings);
598 }
599
600 static int
601 wim_reference_globs(WIMStruct *wim, struct string_set *set, int open_flags)
602 {
603         return wimlib_reference_resource_files(wim, set->strings,
604                                                set->num_strings,
605                                                WIMLIB_REF_FLAG_GLOB_ENABLE,
606                                                open_flags);
607 }
608
609 static void
610 do_resource_not_found_warning(const tchar *wimfile,
611                               const struct wimlib_wim_info *info,
612                               const struct string_set *refglobs)
613 {
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."),
618                                      wimfile);
619                 } else {
620                         imagex_error(T("Perhaps the '--ref' argument did not "
621                                        "specify all other parts of the split "
622                                        "WIM?"));
623                 }
624         } else {
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."));
627         }
628 }
629
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.  */
632 static off_t
633 file_get_size(const tchar *filename)
634 {
635         struct stat st;
636         if (tstat(filename, &st) == 0)
637                 return st.st_size;
638         else
639                 return (off_t)-1;
640 }
641
642 enum {
643         PARSE_STRING_SUCCESS = 0,
644         PARSE_STRING_FAILURE = 1,
645         PARSE_STRING_NONE = 2,
646 };
647
648 /*
649  * Parses a string token from an array of characters.
650  *
651  * Tokens are either whitespace-delimited, or double or single-quoted.
652  *
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
656  *           be '\0'.
657  *
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.
661  *
662  * @fn_ret:  Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
663  *           parsed string token will be returned here.
664  *
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.
669  */
670 static int
671 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
672 {
673         size_t len = *len_p;
674         tchar *line = *line_p;
675         tchar *fn;
676         tchar quote_char;
677
678         /* Skip leading whitespace */
679         for (;;) {
680                 if (len == 0)
681                         return PARSE_STRING_NONE;
682                 if (!istspace(*line) && *line != T('\0'))
683                         break;
684                 line++;
685                 len--;
686         }
687         quote_char = *line;
688         if (quote_char == T('"') || quote_char == T('\'')) {
689                 /* Quoted string */
690                 line++;
691                 len--;
692                 fn = line;
693                 line = tmemchr(line, quote_char, len);
694                 if (!line) {
695                         imagex_error(T("Missing closing quote: %"TS), fn - 1);
696                         return PARSE_STRING_FAILURE;
697                 }
698         } else {
699                 /* Unquoted string.  Go until whitespace.  Line is terminated
700                  * by '\0', so no need to check 'len'. */
701                 fn = line;
702                 do {
703                         line++;
704                 } while (!istspace(*line) && *line != T('\0'));
705         }
706         *line = T('\0');
707         len -= line - fn;
708         *len_p = len;
709         *line_p = line;
710         *fn_ret = fn;
711         return PARSE_STRING_SUCCESS;
712 }
713
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.)
717  *
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.
720  *
721  * @len:   Length of the line of data.
722  *
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.
726  *
727  * Returns true if the line was valid; false otherwise.  */
728 static bool
729 parse_source_list_line(tchar *line, size_t len,
730                        struct wimlib_capture_source *source)
731 {
732         /* SOURCE [DEST] */
733         int ret;
734         ret = parse_string(&line, &len, &source->fs_source_path);
735         if (ret != PARSE_STRING_SUCCESS)
736                 return false;
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;
741 }
742
743 /* Returns %true if the given line of length @len > 0 is a comment or empty line
744  * in the source list file format. */
745 static bool
746 is_comment_line(const tchar *line, size_t len)
747 {
748         for (;;) {
749                 if (*line == T('#') || *line == T(';'))
750                         return true;
751                 if (!istspace(*line) && *line != T('\0'))
752                         return false;
753                 ++line;
754                 --len;
755                 if (len == 0)
756                         return true;
757         }
758 }
759
760 static ssize_t
761 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
762 {
763         ssize_t nlines = 0;
764         tchar *contents = *contents_p;
765         size_t nchars = *nchars_p;
766         size_t i;
767
768         for (i = 0; i < nchars; i++)
769                 if (contents[i] == T('\n'))
770                         nlines++;
771
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));
775                 if (!contents) {
776                         imagex_error(T("Out of memory!"));
777                         return -1;
778                 }
779                 contents[nchars] = T('\n');
780                 *contents_p = contents;
781                 nchars++;
782                 nlines++;
783         }
784         *nchars_p = nchars;
785         return nlines;
786 }
787
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.)
790  *
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.
795  *
796  * @source_list_nbytes:    Number of bytes of data in the @source_list_contents
797  *                         buffer.
798  *
799  * @nsources_ret:          On success, the length of the returned array is
800  *                         returned here.
801  *
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
804  * be created.  */
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)
808 {
809         ssize_t nlines;
810         tchar *p;
811         struct wimlib_capture_source *sources;
812         size_t i, j;
813
814         nlines = text_file_count_lines(source_list_contents_p,
815                                        &source_list_nchars);
816         if (nlines < 0)
817                 return NULL;
818
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));
822         if (!sources) {
823                 imagex_error(T("out of memory"));
824                 return NULL;
825         }
826         p = *source_list_contents_p;
827         j = 0;
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;
833                 *endp = T('\0');
834                 if (!is_comment_line(p, len)) {
835                         if (!parse_source_list_line(p, len, &sources[j++])) {
836                                 free(sources);
837                                 return NULL;
838                         }
839                 }
840                 p = endp + 1;
841
842         }
843         *nsources_ret = j;
844         return sources;
845 }
846
847 /* Reads the contents of a file into memory. */
848 static char *
849 file_get_contents(const tchar *filename, size_t *len_ret)
850 {
851         struct stat stbuf;
852         void *buf = NULL;
853         size_t len;
854         FILE *fp;
855
856         if (tstat(filename, &stbuf) != 0) {
857                 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
858                 goto out;
859         }
860         len = stbuf.st_size;
861
862         fp = tfopen(filename, T("rb"));
863         if (!fp) {
864                 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
865                 goto out;
866         }
867
868         buf = malloc(len ? len : 1);
869         if (!buf) {
870                 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
871                                "contents of file \"%"TS"\""), len, filename);
872                 goto out_fclose;
873         }
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);
877                 goto out_free_buf;
878         }
879         *len_ret = len;
880         goto out_fclose;
881 out_free_buf:
882         free(buf);
883         buf = NULL;
884 out_fclose:
885         fclose(fp);
886 out:
887         return buf;
888 }
889
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
892  * error. */
893 static char *
894 stdin_get_contents(size_t *len_ret)
895 {
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 */
898         char *buf = NULL;
899         size_t newlen = 1024;
900         size_t pos = 0;
901         size_t inc = 1024;
902         for (;;) {
903                 char *p = realloc(buf, newlen);
904                 size_t bytes_read, bytes_to_read;
905                 if (!p) {
906                         imagex_error(T("out of memory while reading stdin"));
907                         break;
908                 }
909                 buf = p;
910                 bytes_to_read = newlen - pos;
911                 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
912                 pos += bytes_read;
913                 if (bytes_read != bytes_to_read) {
914                         if (feof(stdin)) {
915                                 *len_ret = pos;
916                                 return buf;
917                         } else {
918                                 imagex_error_with_errno(T("error reading stdin"));
919                                 break;
920                         }
921                 }
922                 newlen += inc;
923                 inc *= 3;
924                 inc /= 2;
925         }
926         free(buf);
927         return NULL;
928 }
929
930
931 static tchar *
932 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
933 {
934 #ifndef __WIN32__
935         /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
936          * */
937         *num_tchars_ret = num_bytes;
938         return text;
939 #else /* !__WIN32__ */
940         /* On Windows, translate the text to UTF-16LE */
941         wchar_t *text_wstr;
942         size_t num_wchars;
943
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)))
947         {
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;
954         } else {
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,
960                                              num_bytes,
961                                              &num_wchars);
962                 free(text);
963         }
964         *num_tchars_ret = num_wchars;
965         return text_wstr;
966 #endif /* __WIN32__ */
967 }
968
969 static tchar *
970 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
971 {
972         char *contents;
973         size_t num_bytes;
974
975         contents = file_get_contents(filename, &num_bytes);
976         if (!contents)
977                 return NULL;
978         return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
979 }
980
981 static tchar *
982 stdin_get_text_contents(size_t *num_tchars_ret)
983 {
984         char *contents;
985         size_t num_bytes;
986
987         contents = stdin_get_contents(&num_bytes);
988         if (!contents)
989                 return NULL;
990         return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
991 }
992
993 #define TO_PERCENT(numerator, denominator) \
994         (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
995
996 #define GIBIBYTE_MIN_NBYTES 10000000000ULL
997 #define MEBIBYTE_MIN_NBYTES 10000000ULL
998 #define KIBIBYTE_MIN_NBYTES 10000ULL
999
1000 static unsigned
1001 get_unit(uint64_t total_bytes, const tchar **name_ret)
1002 {
1003         if (total_bytes >= GIBIBYTE_MIN_NBYTES) {
1004                 *name_ret = T("GiB");
1005                 return 30;
1006         } else if (total_bytes >= MEBIBYTE_MIN_NBYTES) {
1007                 *name_ret = T("MiB");
1008                 return 20;
1009         } else if (total_bytes >= KIBIBYTE_MIN_NBYTES) {
1010                 *name_ret = T("KiB");
1011                 return 10;
1012         } else {
1013                 *name_ret = T("bytes");
1014                 return 0;
1015         }
1016 }
1017
1018 static struct wimlib_progress_info_scan last_scan_progress;
1019
1020 static void
1021 report_scan_progress(const struct wimlib_progress_info_scan *scan, bool done)
1022 {
1023         uint64_t prev_count, cur_count;
1024
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;
1028
1029         if (done || prev_count == 0 || cur_count >= prev_count + 100 ||
1030             cur_count % 128 == 0)
1031         {
1032                 unsigned unit_shift;
1033                 const tchar *unit_name;
1034
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,
1039                               unit_name,
1040                               scan->num_nondirs_scanned,
1041                               scan->num_dirs_scanned);
1042                 last_scan_progress = *scan;
1043         }
1044 }
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)
1050 {
1051         unsigned percent_done;
1052         unsigned unit_shift;
1053         const tchar *unit_name;
1054
1055         if (imagex_be_quiet)
1056                 return WIMLIB_PROGRESS_STATUS_CONTINUE;
1057         switch (msg) {
1058         case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
1059                 {
1060                         static bool first = true;
1061                         if (first) {
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"));
1068                                 first = false;
1069                         }
1070                 }
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);
1074
1075                 imagex_printf(T("\r%"PRIu64" %"TS" of %"PRIu64" %"TS" (uncompressed) "
1076                         "written (%u%% done)"),
1077                         info->write_streams.completed_bytes >> unit_shift,
1078                         unit_name,
1079                         info->write_streams.total_bytes >> unit_shift,
1080                         unit_name,
1081                         percent_done);
1082                 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1083                         imagex_printf(T("\n"));
1084                 break;
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"));
1089                 } else {
1090                         imagex_printf(T(" (loading as WIM path: \"%"TS"\")...\n"),
1091                                       info->scan.wim_target_path);
1092                 }
1093                 memset(&last_scan_progress, 0, sizeof(last_scan_progress));
1094                 break;
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);
1099                         break;
1100                 case WIMLIB_SCAN_DENTRY_EXCLUDED:
1101                         imagex_printf(T("\nExcluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1102                         break;
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);
1106                         break;
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.  */
1114                 #ifndef __WIN32__
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);
1120                 #endif
1121                         break;
1122                 default:
1123                         break;
1124                 }
1125                 break;
1126         case WIMLIB_PROGRESS_MSG_SCAN_END:
1127                 report_scan_progress(&info->scan, true);
1128                 imagex_printf(T("\n"));
1129                 break;
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,
1138                         unit_name,
1139                         info->integrity.total_bytes >> unit_shift,
1140                         unit_name,
1141                         percent_done);
1142                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1143                         imagex_printf(T("\n"));
1144                 break;
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,
1152                         unit_name,
1153                         info->integrity.total_bytes >> unit_shift,
1154                         unit_name,
1155                         percent_done);
1156                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1157                         imagex_printf(T("\n"));
1158                 break;
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);
1168                 break;
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"));
1178                 }
1179                 break;
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 file data: "
1185                           "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1186                         info->extract.completed_bytes >> unit_shift,
1187                         unit_name,
1188                         info->extract.total_bytes >> unit_shift,
1189                         unit_name,
1190                         percent_done);
1191                 if (info->extract.completed_bytes >= info->extract.total_bytes)
1192                         imagex_printf(T("\n"));
1193                 break;
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"));
1203                 }
1204                 break;
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);
1210                 }
1211                 break;
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,
1222                         unit_name,
1223                         info->split.total_bytes >> unit_shift,
1224                         unit_name,
1225                         percent_done);
1226                 break;
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);
1232                 }
1233                 break;
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);
1239                         break;
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);
1244                         break;
1245                 case WIMLIB_UPDATE_OP_ADD:
1246                 default:
1247                         break;
1248                 }
1249                 break;
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);
1253                 break;
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);
1257                 break;
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);
1264                         } else {
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"));
1269                         }
1270                 }
1271                 break;
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);
1276                 break;
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 file data: "
1282                           "%"PRIu64" %"TS" of %"PRIu64" %"TS" (%u%%) done"),
1283                         info->verify_streams.completed_bytes >> unit_shift,
1284                         unit_name,
1285                         info->verify_streams.total_bytes >> unit_shift,
1286                         unit_name,
1287                         percent_done);
1288                 if (info->verify_streams.completed_bytes == info->verify_streams.total_bytes)
1289                         imagex_printf(T("\n"));
1290                 break;
1291         default:
1292                 break;
1293         }
1294         fflush(imagex_info_file);
1295         return WIMLIB_PROGRESS_STATUS_CONTINUE;
1296 }
1297
1298 static unsigned
1299 parse_num_threads(const tchar *optarg)
1300 {
1301         tchar *tmp;
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!"));
1305                 return UINT_MAX;
1306         } else {
1307                 return ul_nthreads;
1308         }
1309 }
1310
1311 static uint32_t
1312 parse_chunk_size(const tchar *optarg)
1313 {
1314         tchar *tmp;
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"));
1319                 return UINT32_MAX;
1320         }
1321         if (*tmp) {
1322                 if (*tmp == T('k') || *tmp == T('K')) {
1323                         chunk_size <<= 10;
1324                         tmp++;
1325                 } else if (*tmp == T('m') || *tmp == T('M')) {
1326                         chunk_size <<= 20;
1327                         tmp++;
1328                 } else if (*tmp == T('g') || *tmp == T('G')) {
1329                         chunk_size <<= 30;
1330                         tmp++;
1331                 }
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"));
1334                         return UINT32_MAX;
1335                 }
1336         }
1337         if (chunk_size >= UINT32_MAX) {
1338                 imagex_error(T("Invalid chunk size specification; the value is too large!"));
1339                 return UINT32_MAX;
1340         }
1341         return chunk_size;
1342 }
1343
1344
1345 /*
1346  * Parse an option passed to an update command.
1347  *
1348  * @op:         One of WIMLIB_UPDATE_OP_* that indicates the command being
1349  *              parsed.
1350  *
1351  * @option:     Text string for the option (beginning with --)
1352  *
1353  * @cmd:        `struct wimlib_update_command' that is being constructed for
1354  *              this command.
1355  *
1356  * Returns true if the option was recognized; false if not.
1357  */
1358 static bool
1359 update_command_add_option(int op, const tchar *option,
1360                           struct wimlib_update_command *cmd)
1361 {
1362         bool recognized = true;
1363         switch (op) {
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;
1377                 else
1378                         recognized = false;
1379                 break;
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;
1385                 else
1386                         recognized = false;
1387                 break;
1388         default:
1389                 recognized = false;
1390                 break;
1391         }
1392         return recognized;
1393 }
1394
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,
1400 };
1401
1402 static void
1403 update_command_add_nonoption(int op, const tchar *nonoption,
1404                              struct wimlib_update_command *cmd,
1405                              unsigned num_nonoptions)
1406 {
1407         switch (op) {
1408         case WIMLIB_UPDATE_OP_ADD:
1409                 if (num_nonoptions == 0)
1410                         cmd->add.fs_source_path = (tchar*)nonoption;
1411                 else
1412                         cmd->add.wim_target_path = (tchar*)nonoption;
1413                 break;
1414         case WIMLIB_UPDATE_OP_DELETE:
1415                 cmd->delete_.wim_path = (tchar*)nonoption;
1416                 break;
1417         case WIMLIB_UPDATE_OP_RENAME:
1418                 if (num_nonoptions == 0)
1419                         cmd->rename.wim_source_path = (tchar*)nonoption;
1420                 else
1421                         cmd->rename.wim_target_path = (tchar*)nonoption;
1422                 break;
1423         }
1424 }
1425
1426 /*
1427  * Parse a command passed on stdin to `imagex update'.
1428  *
1429  * @line:       Text of the command.
1430  * @len:        Length of the line, including a null terminator
1431  *              at line[len - 1].
1432  *
1433  * @command:    A `struct wimlib_update_command' to fill in from the parsed
1434  *              line.
1435  *
1436  * @line_number: Line number of the command, for diagnostics.
1437  *
1438  * Returns true on success; returns false on parse error.
1439  */
1440 static bool
1441 parse_update_command(tchar *line, size_t len,
1442                      struct wimlib_update_command *command,
1443                      size_t line_number)
1444 {
1445         int ret;
1446         tchar *command_name;
1447         int op;
1448         size_t num_nonoptions;
1449
1450         /* Get the command name ("add", "delete", "rename") */
1451         ret = parse_string(&line, &len, &command_name);
1452         if (ret != PARSE_STRING_SUCCESS)
1453                 return false;
1454
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;
1461         } else {
1462                 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1463                              command_name, line_number);
1464                 return false;
1465         }
1466         command->op = op;
1467
1468         /* Parse additional options and non-options as needed */
1469         num_nonoptions = 0;
1470         for (;;) {
1471                 tchar *next_string;
1472
1473                 ret = parse_string(&line, &len, &next_string);
1474                 if (ret == PARSE_STRING_NONE) /* End of line */
1475                         break;
1476                 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1477                         return false;
1478                 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1479                         /* Option */
1480                         if (!update_command_add_option(op, next_string, command))
1481                         {
1482                                 imagex_error(T("Unrecognized option \"%"TS"\" to "
1483                                                "update command \"%"TS"\" on line %zu"),
1484                                              next_string, command_name, line_number);
1485
1486                                 return false;
1487                         }
1488                 } else {
1489                         /* Nonoption */
1490                         if (num_nonoptions == update_command_num_nonoptions[op])
1491                         {
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);
1498                                 return false;
1499                         }
1500                         update_command_add_nonoption(op, next_string,
1501                                                      command, num_nonoptions);
1502                         num_nonoptions++;
1503                 }
1504         }
1505
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);
1509                 return false;
1510         }
1511         return true;
1512 }
1513
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)
1517 {
1518         ssize_t nlines;
1519         tchar *p;
1520         struct wimlib_update_command *cmds;
1521         size_t i, j;
1522
1523         nlines = text_file_count_lines(cmd_file_contents_p,
1524                                        &cmd_file_nchars);
1525         if (nlines < 0)
1526                 return NULL;
1527
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));
1531         if (!cmds) {
1532                 imagex_error(T("out of memory"));
1533                 return NULL;
1534         }
1535         p = *cmd_file_contents_p;
1536         j = 0;
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;
1542                 *endp = T('\0');
1543                 if (!is_comment_line(p, len)) {
1544                         if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1545                                 free(cmds);
1546                                 return NULL;
1547                         }
1548                 }
1549                 p = endp + 1;
1550         }
1551         *num_cmds_ret = j;
1552         return cmds;
1553 }
1554
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.  */
1557 static int
1558 imagex_apply(int argc, tchar **argv, int cmd)
1559 {
1560         int c;
1561         int open_flags = 0;
1562         int image = WIMLIB_NO_IMAGE;
1563         WIMStruct *wim;
1564         struct wimlib_wim_info info;
1565         int ret;
1566         const tchar *wimfile;
1567         const tchar *target;
1568         const tchar *image_num_or_name = NULL;
1569         int extract_flags = 0;
1570
1571         STRING_SET(refglobs);
1572
1573         for_opt(c, apply_options) {
1574                 switch (c) {
1575                 case IMAGEX_CHECK_OPTION:
1576                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1577                         break;
1578                 case IMAGEX_VERBOSE_OPTION:
1579                         /* No longer does anything.  */
1580                         break;
1581                 case IMAGEX_REF_OPTION:
1582                         ret = string_set_append(&refglobs, optarg);
1583                         if (ret)
1584                                 goto out_free_refglobs;
1585                         break;
1586                 case IMAGEX_UNIX_DATA_OPTION:
1587                         extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1588                         break;
1589                 case IMAGEX_NO_ACLS_OPTION:
1590                         extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1591                         break;
1592                 case IMAGEX_STRICT_ACLS_OPTION:
1593                         extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1594                         break;
1595                 case IMAGEX_NO_ATTRIBUTES_OPTION:
1596                         extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
1597                         break;
1598                 case IMAGEX_NORPFIX_OPTION:
1599                         extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1600                         break;
1601                 case IMAGEX_RPFIX_OPTION:
1602                         extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1603                         break;
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;
1607                         break;
1608                 case IMAGEX_RESUME_OPTION:
1609                         extract_flags |= WIMLIB_EXTRACT_FLAG_RESUME;
1610                         break;
1611                 case IMAGEX_WIMBOOT_OPTION:
1612                         extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
1613                         break;
1614                 default:
1615                         goto out_usage;
1616                 }
1617         }
1618         argc -= optind;
1619         argv += optind;
1620         if (argc != 2 && argc != 3)
1621                 goto out_usage;
1622
1623         wimfile = argv[0];
1624
1625         if (!tstrcmp(wimfile, T("-"))) {
1626                 /* Attempt to apply pipable WIM from standard input.  */
1627                 if (argc == 2) {
1628                         image_num_or_name = NULL;
1629                         target = argv[1];
1630                 } else {
1631                         image_num_or_name = argv[1];
1632                         target = argv[2];
1633                 }
1634                 wim = NULL;
1635         } else {
1636                 ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
1637                                                     imagex_progress_func, NULL);
1638                 if (ret)
1639                         goto out_free_refglobs;
1640
1641                 wimlib_get_wim_info(wim, &info);
1642
1643                 if (argc >= 3) {
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);
1648                         if (ret)
1649                                 goto out_wimlib_free;
1650                         target = argv[2];
1651                 } else {
1652                         /* No image specified; default to image 1, but only if the WIM
1653                          * contains exactly one image.  */
1654
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);
1659                                 wimlib_free(wim);
1660                                 goto out_usage;
1661                         }
1662                         image = 1;
1663                         target = argv[1];
1664                 }
1665         }
1666
1667         if (refglobs.num_strings) {
1668                 if (wim == NULL) {
1669                         imagex_error(T("Can't specify --ref when applying from stdin!"));
1670                         ret = -1;
1671                         goto out_wimlib_free;
1672                 }
1673                 ret = wim_reference_globs(wim, &refglobs, open_flags);
1674                 if (ret)
1675                         goto out_wimlib_free;
1676         }
1677
1678 #ifndef __WIN32__
1679         {
1680                 /* Interpret a regular file or block device target as an NTFS
1681                  * volume.  */
1682                 struct stat stbuf;
1683
1684                 if (tstat(target, &stbuf)) {
1685                         if (errno != ENOENT) {
1686                                 imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1687                                                         target);
1688                                 ret = -1;
1689                                 goto out_wimlib_free;
1690                         }
1691                 } else {
1692                         if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1693                                 extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1694                 }
1695         }
1696 #endif
1697
1698         if (wim) {
1699                 ret = wimlib_extract_image(wim, image, target, extract_flags);
1700         } else {
1701                 set_fd_to_binary_mode(STDIN_FILENO);
1702                 ret = wimlib_extract_image_from_pipe_with_progress(
1703                                            STDIN_FILENO,
1704                                            image_num_or_name,
1705                                            target,
1706                                            extract_flags,
1707                                            imagex_progress_func,
1708                                            NULL);
1709         }
1710         if (ret == 0) {
1711                 imagex_printf(T("Done applying WIM image.\n"));
1712         } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
1713                 if (wim) {
1714                         do_resource_not_found_warning(wimfile, &info, &refglobs);
1715                 } else {
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."));
1720                 }
1721         }
1722 out_wimlib_free:
1723         wimlib_free(wim);
1724 out_free_refglobs:
1725         string_set_destroy(&refglobs);
1726         return ret;
1727
1728 out_usage:
1729         usage(CMD_APPLY, stderr);
1730         ret = -1;
1731         goto out_free_refglobs;
1732 }
1733
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
1737  * WIM file. */
1738 static int
1739 imagex_capture_or_append(int argc, tchar **argv, int cmd)
1740 {
1741         int c;
1742         int open_flags = 0;
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;
1752         int wim_fd;
1753         const tchar *name;
1754         const tchar *desc;
1755         const tchar *flags_element = NULL;
1756
1757         WIMStruct *wim;
1758         STRING_SET(base_wimfiles);
1759         WIMStruct **base_wims;
1760
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;
1765
1766         int ret;
1767         unsigned num_threads = 0;
1768
1769         tchar *source;
1770         tchar *source_copy;
1771
1772         tchar *config_file = NULL;
1773
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;
1779         size_t num_sources;
1780         bool name_defaulted;
1781
1782         for_opt(c, capture_or_append_options) {
1783                 switch (c) {
1784                 case IMAGEX_BOOT_OPTION:
1785                         add_flags |= WIMLIB_ADD_FLAG_BOOT;
1786                         break;
1787                 case IMAGEX_CHECK_OPTION:
1788                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1789                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1790                         break;
1791                 case IMAGEX_NOCHECK_OPTION:
1792                         write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
1793                         break;
1794                 case IMAGEX_CONFIG_OPTION:
1795                         config_file = optarg;
1796                         add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
1797                         break;
1798                 case IMAGEX_COMPRESS_OPTION:
1799                         compression_type = get_compression_type(optarg);
1800                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1801                                 goto out_err;
1802                         break;
1803                 case IMAGEX_COMPRESS_SLOW_OPTION:
1804                         set_compress_slow();
1805                         break;
1806                 case IMAGEX_CHUNK_SIZE_OPTION:
1807                         chunk_size = parse_chunk_size(optarg);
1808                         if (chunk_size == UINT32_MAX)
1809                                 goto out_err;
1810                         break;
1811                 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
1812                         solid_chunk_size = parse_chunk_size(optarg);
1813                         if (solid_chunk_size == UINT32_MAX)
1814                                 goto out_err;
1815                         break;
1816                 case IMAGEX_SOLID_COMPRESS_OPTION:
1817                         solid_ctype = get_compression_type(optarg);
1818                         if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
1819                                 goto out_err;
1820                         break;
1821                 case IMAGEX_SOLID_OPTION:
1822                         write_flags |= WIMLIB_WRITE_FLAG_SOLID;
1823                         break;
1824                 case IMAGEX_NO_SOLID_SORT_OPTION:
1825                         write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
1826                         break;
1827                 case IMAGEX_FLAGS_OPTION:
1828                         flags_element = optarg;
1829                         break;
1830                 case IMAGEX_DEREFERENCE_OPTION:
1831                         add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
1832                         break;
1833                 case IMAGEX_VERBOSE_OPTION:
1834                         /* No longer does anything.  */
1835                         break;
1836                 case IMAGEX_THREADS_OPTION:
1837                         num_threads = parse_num_threads(optarg);
1838                         if (num_threads == UINT_MAX)
1839                                 goto out_err;
1840                         break;
1841                 case IMAGEX_REBUILD_OPTION:
1842                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1843                         break;
1844                 case IMAGEX_UNIX_DATA_OPTION:
1845                         add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
1846                         break;
1847                 case IMAGEX_SOURCE_LIST_OPTION:
1848                         source_list = true;
1849                         break;
1850                 case IMAGEX_NO_ACLS_OPTION:
1851                         add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
1852                         break;
1853                 case IMAGEX_STRICT_ACLS_OPTION:
1854                         add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
1855                         break;
1856                 case IMAGEX_RPFIX_OPTION:
1857                         add_flags |= WIMLIB_ADD_FLAG_RPFIX;
1858                         break;
1859                 case IMAGEX_NORPFIX_OPTION:
1860                         add_flags |= WIMLIB_ADD_FLAG_NORPFIX;
1861                         break;
1862                 case IMAGEX_PIPABLE_OPTION:
1863                         write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1864                         break;
1865                 case IMAGEX_NOT_PIPABLE_OPTION:
1866                         write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
1867                         break;
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!"));
1872                                 goto out_err;
1873                         } else {
1874                                 tchar *colon;
1875                                 colon = tstrrchr(optarg, T(':'));
1876
1877                                 if (colon) {
1878                                         template_wimfile = optarg;
1879                                         *colon = T('\0');
1880                                         template_image_name_or_num = colon + 1;
1881                                 } else {
1882                                         template_wimfile = NULL;
1883                                         template_image_name_or_num = optarg;
1884                                 }
1885                         }
1886                         break;
1887                 case IMAGEX_DELTA_FROM_OPTION:
1888                         if (cmd != CMD_CAPTURE) {
1889                                 imagex_error(T("'--delta-from' is only "
1890                                                "valid for capture!"));
1891                                 goto out_usage;
1892                         }
1893                         ret = string_set_append(&base_wimfiles, optarg);
1894                         if (ret)
1895                                 goto out_free_base_wimfiles;
1896                         write_flags |= WIMLIB_WRITE_FLAG_SKIP_EXTERNAL_WIMS;
1897                         break;
1898                 case IMAGEX_WIMBOOT_OPTION:
1899                         add_flags |= WIMLIB_ADD_FLAG_WIMBOOT;
1900                         break;
1901                 default:
1902                         goto out_usage;
1903                 }
1904         }
1905         argc -= optind;
1906         argv += optind;
1907
1908         if (argc < 2 || argc > 4)
1909                 goto out_usage;
1910
1911         source = argv[0];
1912         wimfile = argv[1];
1913
1914         /* Set default compression type and parameters.  */
1915
1916
1917         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
1918                 /* No compression type specified.  Use the default.  */
1919
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;
1927                 } else {
1928                         /* Otherwise, default to LZX compression.  */
1929                         compression_type = WIMLIB_COMPRESSION_TYPE_LZX;
1930                 }
1931         }
1932
1933         if (!tstrcmp(wimfile, T("-"))) {
1934                 /* Writing captured WIM to standard output.  */
1935         #if 0
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).");
1941                         goto out_err;
1942                 }
1943         #else
1944                 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
1945         #endif
1946                 if (cmd == CMD_APPEND) {
1947                         imagex_error(T("Using standard output for append does "
1948                                        "not make sense."));
1949                         goto out_err;
1950                 }
1951                 wim_fd = STDOUT_FILENO;
1952                 wimfile = NULL;
1953                 imagex_info_file = stderr;
1954                 set_fd_to_binary_mode(wim_fd);
1955         }
1956
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
1962                          * base WIM.  */
1963                         template_wimfile = base_wimfiles.strings[0];
1964                 } else if (cmd == CMD_APPEND) {
1965                         /* Appending to WIM:  default to WIM being appended to.
1966                          */
1967                         template_wimfile = wimfile;
1968                 } else {
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 "
1974                                                "WIMs,\n"
1975                                                "      '--update-of' must "
1976                                                "specify WIMFILE:IMAGE!"));
1977                         } else {
1978                                 imagex_error(T("For capture of non-delta WIM, "
1979                                                "'--update-of' must specify "
1980                                                "WIMFILE:IMAGE!"));
1981                         }
1982                         goto out_usage;
1983                 }
1984         }
1985
1986         if (argc >= 3) {
1987                 name = argv[2];
1988                 name_defaulted = false;
1989         } else {
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;
1995
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;
2000         }
2001         /* Image description defaults to NULL if not given. */
2002         if (argc >= 4)
2003                 desc = argv[3];
2004         else
2005                 desc = NULL;
2006
2007         if (source_list) {
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);
2011                 } else {
2012                         source_list_contents = file_get_text_contents(source,
2013                                                                       &source_list_nchars);
2014                 }
2015                 if (!source_list_contents)
2016                         goto out_err;
2017
2018                 capture_sources = parse_source_list(&source_list_contents,
2019                                                     source_list_nchars,
2020                                                     &num_sources);
2021                 if (!capture_sources) {
2022                         ret = -1;
2023                         goto out_free_source_list_contents;
2024                 }
2025                 capture_sources_malloced = true;
2026         } else {
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;
2032                 num_sources = 1;
2033                 capture_sources_malloced = false;
2034                 source_list_contents = NULL;
2035         }
2036
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,
2041                                                     &wim,
2042                                                     imagex_progress_func,
2043                                                     NULL);
2044                 if (ret)
2045                         goto out_free_capture_sources;
2046         } else {
2047                 ret = wimlib_create_new_wim(compression_type, &wim);
2048                 if (ret)
2049                         goto out_free_capture_sources;
2050                 wimlib_register_progress_function(wim, imagex_progress_func, NULL);
2051         }
2052
2053         /* Set chunk size if non-default.  */
2054         if (chunk_size != UINT32_MAX) {
2055                 ret = wimlib_set_output_chunk_size(wim, chunk_size);
2056                 if (ret)
2057                         goto out_free_wim;
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);
2061                 if (ret)
2062                         goto out_free_wim;
2063         }
2064         if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2065                 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
2066                 if (ret)
2067                         goto out_free_wim;
2068         }
2069         if (solid_chunk_size != UINT32_MAX) {
2070                 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
2071                 if (ret)
2072                         goto out_free_wim;
2073         }
2074
2075 #ifndef __WIN32__
2076         /* Detect if source is regular file or block device and set NTFS volume
2077          * capture mode.  */
2078         if (!source_list) {
2079                 struct stat stbuf;
2080
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;
2086                         }
2087                 } else {
2088                         if (errno != ENOENT) {
2089                                 imagex_error_with_errno(T("Failed to stat "
2090                                                           "\"%"TS"\""), source);
2091                                 ret = -1;
2092                                 goto out_free_wim;
2093                         }
2094                 }
2095         }
2096 #endif
2097
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);
2106                      conflict_idx++)
2107                 {
2108                         tsprintf(name_end, T(" (%lu)"), conflict_idx);
2109                 }
2110         }
2111
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!"));
2119                         ret = -1;
2120                         goto out_free_wim;
2121                 }
2122
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);
2127                         if (ret)
2128                                 goto out_free_base_wims;
2129
2130                 }
2131
2132                 ret = wimlib_reference_resources(wim, base_wims,
2133                                                  base_wimfiles.num_strings, 0);
2134                 if (ret)
2135                         goto out_free_base_wims;
2136
2137                 if (base_wimfiles.num_strings == 1) {
2138                         imagex_printf(T("Capturing delta WIM based on \"%"TS"\"\n"),
2139                                       base_wimfiles.strings[0]);
2140                 } else {
2141                         imagex_printf(T("Capturing delta WIM based on %u WIMs\n"),
2142                                       base_wimfiles.num_strings);
2143                 }
2144
2145         } else {
2146                 base_wims = NULL;
2147         }
2148
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) {
2152
2153
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) {
2158                         template_wim = wim;
2159                 } else {
2160                         ret = wimlib_open_wim_with_progress(template_wimfile,
2161                                                             open_flags,
2162                                                             &template_wim,
2163                                                             imagex_progress_func,
2164                                                             NULL);
2165                         if (ret)
2166                                 goto out_free_base_wims;
2167                 }
2168
2169                 template_image = wimlib_resolve_image(template_wim,
2170                                                       template_image_name_or_num);
2171
2172                 if (template_image_name_or_num[0] == T('-')) {
2173                         tchar *tmp;
2174                         unsigned long n;
2175                         struct wimlib_wim_info info;
2176
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 &&
2180                             *tmp == T('\0') &&
2181                             tmp != template_image_name_or_num + 1)
2182                         {
2183                                 template_image = info.image_count - (n - 1);
2184                         }
2185                 }
2186                 ret = verify_image_exists_and_is_single(template_image,
2187                                                         template_image_name_or_num,
2188                                                         template_wimfile);
2189                 if (ret)
2190                         goto out_free_template_wim;
2191         } else {
2192                 template_wim = NULL;
2193         }
2194
2195         ret = wimlib_add_image_multisource(wim,
2196                                            capture_sources,
2197                                            num_sources,
2198                                            name,
2199                                            config_file,
2200                                            add_flags);
2201         if (ret)
2202                 goto out_free_template_wim;
2203
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;
2210
2211                 wimlib_get_wim_info(wim, &info);
2212
2213                 if (desc) {
2214                         ret = wimlib_set_image_descripton(wim,
2215                                                           info.image_count,
2216                                                           desc);
2217                         if (ret)
2218                                 goto out_free_template_wim;
2219                 }
2220
2221                 if (flags_element) {
2222                         ret = wimlib_set_image_flags(wim, info.image_count,
2223                                                      flags_element);
2224                         if (ret)
2225                                 goto out_free_template_wim;
2226                 }
2227
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,
2234                                                               info.image_count,
2235                                                               template_wim,
2236                                                               template_image,
2237                                                               0);
2238                         if (ret)
2239                                 goto out_free_template_wim;
2240                 }
2241         }
2242
2243         /* Write the new WIM or overwrite the existing WIM with the new image
2244          * appended.  */
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);
2250         } else {
2251                 ret = wimlib_write_to_fd(wim, wim_fd, WIMLIB_ALL_IMAGES,
2252                                          write_flags, num_threads);
2253         }
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);
2259 out_free_base_wims:
2260         for (size_t i = 0; i < base_wimfiles.num_strings; i++)
2261                 wimlib_free(base_wims[i]);
2262         free(base_wims);
2263 out_free_wim:
2264         wimlib_free(wim);
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);
2272         return ret;
2273
2274 out_usage:
2275         usage(cmd, stderr);
2276 out_err:
2277         ret = -1;
2278         goto out_free_base_wimfiles;
2279 }
2280
2281 /* Remove image(s) from a WIM. */
2282 static int
2283 imagex_delete(int argc, tchar **argv, int cmd)
2284 {
2285         int c;
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;
2290         WIMStruct *wim;
2291         int image;
2292         int ret;
2293
2294         for_opt(c, delete_options) {
2295                 switch (c) {
2296                 case IMAGEX_CHECK_OPTION:
2297                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2298                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2299                         break;
2300                 case IMAGEX_SOFT_OPTION:
2301                         write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
2302                         break;
2303                 default:
2304                         goto out_usage;
2305                 }
2306         }
2307         argc -= optind;
2308         argv += optind;
2309
2310         if (argc != 2) {
2311                 if (argc < 1)
2312                         imagex_error(T("Must specify a WIM file"));
2313                 if (argc < 2)
2314                         imagex_error(T("Must specify an image"));
2315                 goto out_usage;
2316         }
2317         wimfile = argv[0];
2318         image_num_or_name = argv[1];
2319
2320         ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
2321                                             imagex_progress_func, NULL);
2322         if (ret)
2323                 goto out;
2324
2325         image = wimlib_resolve_image(wim, image_num_or_name);
2326
2327         ret = verify_image_exists(image, image_num_or_name, wimfile);
2328         if (ret)
2329                 goto out_wimlib_free;
2330
2331         ret = wimlib_delete_image(wim, image);
2332         if (ret) {
2333                 imagex_error(T("Failed to delete image from \"%"TS"\""),
2334                              wimfile);
2335                 goto out_wimlib_free;
2336         }
2337
2338         ret = wimlib_overwrite(wim, write_flags, 0);
2339         if (ret) {
2340                 imagex_error(T("Failed to write the file \"%"TS"\" with image "
2341                                "deleted"), wimfile);
2342         }
2343 out_wimlib_free:
2344         wimlib_free(wim);
2345 out:
2346         return ret;
2347
2348 out_usage:
2349         usage(CMD_DELETE, stderr);
2350         ret = -1;
2351         goto out;
2352 }
2353
2354 struct print_dentry_options {
2355         bool detailed;
2356 };
2357
2358 static void
2359 print_dentry_full_path(const struct wimlib_dir_entry *dentry)
2360 {
2361         tprintf(T("%"TS"\n"), dentry->full_path);
2362 }
2363
2364 static const struct {
2365         uint32_t flag;
2366         const tchar *name;
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")},
2383 };
2384
2385 #define TIMESTR_MAX 100
2386
2387 static void
2388 timespec_to_string(const struct timespec *spec, tchar *buf)
2389 {
2390         time_t t = spec->tv_sec;
2391         struct tm tm;
2392         gmtime_r(&t, &tm);
2393         tstrftime(buf, TIMESTR_MAX, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
2394         buf[TIMESTR_MAX - 1] = '\0';
2395 }
2396
2397 static void
2398 print_time(const tchar *type, const struct timespec *spec)
2399 {
2400         tchar timestr[TIMESTR_MAX];
2401
2402         timespec_to_string(spec, timestr);
2403
2404         tprintf(T("%-20"TS"= %"TS"\n"), type, timestr);
2405 }
2406
2407 static void print_byte_field(const uint8_t field[], size_t len)
2408 {
2409         while (len--)
2410                 tprintf(T("%02hhx"), *field++);
2411 }
2412
2413 static void
2414 print_wim_information(const tchar *wimfile, const struct wimlib_wim_info *info)
2415 {
2416         tchar attr_string[256];
2417         tchar *p;
2418
2419         tputs(T("WIM Information:"));
2420         tputs(T("----------------"));
2421         tprintf(T("Path:           %"TS"\n"), wimfile);
2422         tprintf(T("GUID:           0x"));
2423         print_byte_field(info->guid, sizeof(info->guid));
2424         tputchar(T('\n'));
2425         tprintf(T("Version:        %u\n"), info->wim_version);
2426         tprintf(T("Image Count:    %d\n"), info->image_count);
2427         tprintf(T("Compression:    %"TS"\n"),
2428                 wimlib_get_compression_type_string(info->compression_type));
2429         tprintf(T("Chunk Size:     %"PRIu32" bytes\n"),
2430                 info->chunk_size);
2431         tprintf(T("Part Number:    %d/%d\n"), info->part_number, info->total_parts);
2432         tprintf(T("Boot Index:     %d\n"), info->boot_index);
2433         tprintf(T("Size:           %"PRIu64" bytes\n"), info->total_bytes);
2434
2435         attr_string[0] = T('\0');
2436
2437         if (info->pipable)
2438                 tstrcat(attr_string, T("Pipable, "));
2439
2440         if (info->has_integrity_table)
2441                 tstrcat(attr_string, T("Integrity info, "));
2442
2443         if (info->has_rpfix)
2444                 tstrcat(attr_string, T("Relative path junction, "));
2445
2446         if (info->resource_only)
2447                 tstrcat(attr_string, T("Resource only, "));
2448
2449         if (info->metadata_only)
2450                 tstrcat(attr_string, T("Metadata only, "));
2451
2452         if (info->is_marked_readonly)
2453                 tstrcat(attr_string, T("Readonly, "));
2454
2455         p = tstrchr(attr_string, T('\0'));
2456         if (p >= &attr_string[2] && p[-1] == T(' ') && p[-2] == T(','))
2457                 p[-2] = T('\0');
2458
2459         tprintf(T("Attributes:     %"TS"\n\n"), attr_string);
2460 }
2461
2462 static int
2463 print_resource(const struct wimlib_resource_entry *resource,
2464                void *_ignore)
2465 {
2466         tprintf(T("Hash              = 0x"));
2467         print_byte_field(resource->sha1_hash, sizeof(resource->sha1_hash));
2468         tputchar(T('\n'));
2469
2470         if (!resource->is_missing) {
2471                 tprintf(T("Uncompressed size = %"PRIu64" bytes\n"),
2472                         resource->uncompressed_size);
2473                 if (resource->packed) {
2474                         tprintf(T("Solid resource    = %"PRIu64" => %"PRIu64" "
2475                                   "bytes @ offset %"PRIu64"\n"),
2476                                 resource->raw_resource_uncompressed_size,
2477                                 resource->raw_resource_compressed_size,
2478                                 resource->raw_resource_offset_in_wim);
2479
2480                         tprintf(T("Solid offset      = %"PRIu64" bytes\n"),
2481                                 resource->offset);
2482                 } else {
2483                         tprintf(T("Compressed size   = %"PRIu64" bytes\n"),
2484                                 resource->compressed_size);
2485
2486                         tprintf(T("Offset in WIM     = %"PRIu64" bytes\n"),
2487                                 resource->offset);
2488                 }
2489
2490                 tprintf(T("Part Number       = %u\n"), resource->part_number);
2491                 tprintf(T("Reference Count   = %u\n"), resource->reference_count);
2492
2493                 tprintf(T("Flags             = "));
2494                 if (resource->is_compressed)
2495                         tprintf(T("WIM_RESHDR_FLAG_COMPRESSED  "));
2496                 if (resource->is_metadata)
2497                         tprintf(T("WIM_RESHDR_FLAG_METADATA  "));
2498                 if (resource->is_free)
2499                         tprintf(T("WIM_RESHDR_FLAG_FREE  "));
2500                 if (resource->is_spanned)
2501                         tprintf(T("WIM_RESHDR_FLAG_SPANNED  "));
2502                 if (resource->packed)
2503                         tprintf(T("WIM_RESHDR_FLAG_SOLID  "));
2504                 tputchar(T('\n'));
2505         }
2506         tputchar(T('\n'));
2507         return 0;
2508 }
2509
2510 static void
2511 print_blobs(WIMStruct *wim)
2512 {
2513         wimlib_iterate_lookup_table(wim, 0, print_resource, NULL);
2514 }
2515
2516 static void
2517 default_print_security_descriptor(const uint8_t *sd, size_t size)
2518 {
2519         tprintf(T("Security Descriptor = "));
2520         print_byte_field(sd, size);
2521         tputchar(T('\n'));
2522 }
2523
2524 static void
2525 print_dentry_detailed(const struct wimlib_dir_entry *dentry)
2526 {
2527
2528         tprintf(T(
2529 "----------------------------------------------------------------------------\n"));
2530         tprintf(T("Full Path           = \"%"TS"\"\n"), dentry->full_path);
2531         if (dentry->dos_name)
2532                 tprintf(T("Short Name          = \"%"TS"\"\n"), dentry->dos_name);
2533         tprintf(T("Attributes          = 0x%08x\n"), dentry->attributes);
2534         for (size_t i = 0; i < ARRAY_LEN(file_attr_flags); i++)
2535                 if (file_attr_flags[i].flag & dentry->attributes)
2536                         tprintf(T("    FILE_ATTRIBUTE_%"TS" is set\n"),
2537                                 file_attr_flags[i].name);
2538
2539         if (dentry->security_descriptor) {
2540                 print_security_descriptor(dentry->security_descriptor,
2541                                           dentry->security_descriptor_size);
2542         }
2543
2544         print_time(T("Creation Time"), &dentry->creation_time);
2545         print_time(T("Last Write Time"), &dentry->last_write_time);
2546         print_time(T("Last Access Time"), &dentry->last_access_time);
2547
2548
2549         if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT)
2550                 tprintf(T("Reparse Tag         = 0x%"PRIx32"\n"), dentry->reparse_tag);
2551
2552         tprintf(T("Link Group ID       = 0x%016"PRIx64"\n"), dentry->hard_link_group_id);
2553         tprintf(T("Link Count          = %"PRIu32"\n"), dentry->num_links);
2554
2555         if (dentry->unix_mode != 0) {
2556                 tprintf(T("UNIX Data           = uid:%"PRIu32" gid:%"PRIu32" "
2557                           "mode:0%"PRIo32" rdev:0x%"PRIx32"\n"),
2558                         dentry->unix_uid, dentry->unix_gid,
2559                         dentry->unix_mode, dentry->unix_rdev);
2560         }
2561
2562         for (uint32_t i = 0; i <= dentry->num_named_streams; i++) {
2563                 if (dentry->streams[i].stream_name) {
2564                         tprintf(T("\tNamed data stream \"%"TS"\":\n"),
2565                                 dentry->streams[i].stream_name);
2566                 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_ENCRYPTED) {
2567                         tprintf(T("\tRaw encrypted data stream:\n"));
2568                 } else if (dentry->attributes & WIMLIB_FILE_ATTRIBUTE_REPARSE_POINT) {
2569                         tprintf(T("\tReparse point stream:\n"));
2570                 } else {
2571                         tprintf(T("\tUnnamed data stream:\n"));
2572                 }
2573                 print_resource(&dentry->streams[i].resource, NULL);
2574         }
2575 }
2576
2577 static int
2578 print_dentry(const struct wimlib_dir_entry *dentry, void *_options)
2579 {
2580         const struct print_dentry_options *options = _options;
2581         if (!options->detailed)
2582                 print_dentry_full_path(dentry);
2583         else
2584                 print_dentry_detailed(dentry);
2585         return 0;
2586 }
2587
2588 /* Print the files contained in an image(s) in a WIM file. */
2589 static int
2590 imagex_dir(int argc, tchar **argv, int cmd)
2591 {
2592         const tchar *wimfile;
2593         WIMStruct *wim = NULL;
2594         int image;
2595         int ret;
2596         const tchar *path = WIMLIB_WIM_ROOT_PATH;
2597         int c;
2598         struct print_dentry_options options = {
2599                 .detailed = false,
2600         };
2601         int iterate_flags = WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2602
2603         for_opt(c, dir_options) {
2604                 switch (c) {
2605                 case IMAGEX_PATH_OPTION:
2606                         path = optarg;
2607                         break;
2608                 case IMAGEX_DETAILED_OPTION:
2609                         options.detailed = true;
2610                         break;
2611                 case IMAGEX_ONE_FILE_ONLY_OPTION:
2612                         iterate_flags &= ~WIMLIB_ITERATE_DIR_TREE_FLAG_RECURSIVE;
2613                         break;
2614                 default:
2615                         goto out_usage;
2616                 }
2617         }
2618         argc -= optind;
2619         argv += optind;
2620
2621         if (argc < 1) {
2622                 imagex_error(T("Must specify a WIM file"));
2623                 goto out_usage;
2624         }
2625         if (argc > 2) {
2626                 imagex_error(T("Too many arguments"));
2627                 goto out_usage;
2628         }
2629
2630         wimfile = argv[0];
2631         ret = wimlib_open_wim_with_progress(wimfile, 0, &wim,
2632                                             imagex_progress_func, NULL);
2633         if (ret)
2634                 goto out;
2635
2636         if (argc >= 2) {
2637                 image = wimlib_resolve_image(wim, argv[1]);
2638                 ret = verify_image_exists(image, argv[1], wimfile);
2639                 if (ret)
2640                         goto out_wimlib_free;
2641         } else {
2642                 /* No image specified; default to image 1, but only if the WIM
2643                  * contains exactly one image.  */
2644
2645                 struct wimlib_wim_info info;
2646
2647                 wimlib_get_wim_info(wim, &info);
2648                 if (info.image_count != 1) {
2649                         imagex_error(T("\"%"TS"\" contains %d images; Please "
2650                                        "select one (or all)."),
2651                                      wimfile, info.image_count);
2652                         wimlib_free(wim);
2653                         goto out_usage;
2654                 }
2655                 image = 1;
2656         }
2657
2658         ret = wimlib_iterate_dir_tree(wim, image, path, iterate_flags,
2659                                       print_dentry, &options);
2660 out_wimlib_free:
2661         wimlib_free(wim);
2662 out:
2663         return ret;
2664
2665 out_usage:
2666         usage(CMD_DIR, stderr);
2667         ret = -1;
2668         goto out;
2669 }
2670
2671 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
2672  * WIM file. */
2673 static int
2674 imagex_export(int argc, tchar **argv, int cmd)
2675 {
2676         int c;
2677         int open_flags = 0;
2678         int export_flags = WIMLIB_EXPORT_FLAG_GIFT;
2679         int write_flags = 0;
2680         int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
2681         const tchar *src_wimfile;
2682         const tchar *src_image_num_or_name;
2683         const tchar *dest_wimfile;
2684         int dest_wim_fd;
2685         const tchar *dest_name;
2686         const tchar *dest_desc;
2687         WIMStruct *src_wim;
2688         struct wimlib_wim_info src_info;
2689         WIMStruct *dest_wim;
2690         int ret;
2691         int image;
2692         struct stat stbuf;
2693         bool wim_is_new;
2694         STRING_SET(refglobs);
2695         unsigned num_threads = 0;
2696         uint32_t chunk_size = UINT32_MAX;
2697         uint32_t solid_chunk_size = UINT32_MAX;
2698         int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
2699
2700         for_opt(c, export_options) {
2701                 switch (c) {
2702                 case IMAGEX_BOOT_OPTION:
2703                         export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
2704                         break;
2705                 case IMAGEX_CHECK_OPTION:
2706                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2707                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2708                         break;
2709                 case IMAGEX_NOCHECK_OPTION:
2710                         write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
2711                         break;
2712                 case IMAGEX_COMPRESS_OPTION:
2713                         compression_type = get_compression_type(optarg);
2714                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
2715                                 goto out_err;
2716                         break;
2717                 case IMAGEX_COMPRESS_SLOW_OPTION:
2718                         set_compress_slow();
2719                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2720                         break;
2721                 case IMAGEX_RECOMPRESS_OPTION:
2722                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2723                         break;
2724                 case IMAGEX_SOLID_OPTION:
2725                         write_flags |= WIMLIB_WRITE_FLAG_SOLID;
2726                         break;
2727                 case IMAGEX_NO_SOLID_SORT_OPTION:
2728                         write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
2729                         break;
2730                 case IMAGEX_CHUNK_SIZE_OPTION:
2731                         chunk_size = parse_chunk_size(optarg);
2732                         if (chunk_size == UINT32_MAX)
2733                                 goto out_err;
2734                         break;
2735                 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
2736                         solid_chunk_size = parse_chunk_size(optarg);
2737                         if (solid_chunk_size == UINT32_MAX)
2738                                 goto out_err;
2739                         break;
2740                 case IMAGEX_SOLID_COMPRESS_OPTION:
2741                         solid_ctype = get_compression_type(optarg);
2742                         if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
2743                                 goto out_err;
2744                         break;
2745                 case IMAGEX_REF_OPTION:
2746                         ret = string_set_append(&refglobs, optarg);
2747                         if (ret)
2748                                 goto out_free_refglobs;
2749                         break;
2750                 case IMAGEX_THREADS_OPTION:
2751                         num_threads = parse_num_threads(optarg);
2752                         if (num_threads == UINT_MAX)
2753                                 goto out_err;
2754                         break;
2755                 case IMAGEX_REBUILD_OPTION:
2756                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2757                         break;
2758                 case IMAGEX_PIPABLE_OPTION:
2759                         write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2760                         break;
2761                 case IMAGEX_NOT_PIPABLE_OPTION:
2762                         write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
2763                         break;
2764                 case IMAGEX_WIMBOOT_OPTION:
2765                         export_flags |= WIMLIB_EXPORT_FLAG_WIMBOOT;
2766                         break;
2767                 default:
2768                         goto out_usage;
2769                 }
2770         }
2771         argc -= optind;
2772         argv += optind;
2773         if (argc < 3 || argc > 5)
2774                 goto out_usage;
2775         src_wimfile           = argv[0];
2776         src_image_num_or_name = argv[1];
2777         dest_wimfile          = argv[2];
2778         dest_name             = (argc >= 4) ? argv[3] : NULL;
2779         dest_desc             = (argc >= 5) ? argv[4] : NULL;
2780         ret = wimlib_open_wim_with_progress(src_wimfile, open_flags, &src_wim,
2781                                             imagex_progress_func, NULL);
2782         if (ret)
2783                 goto out_free_refglobs;
2784
2785         wimlib_get_wim_info(src_wim, &src_info);
2786
2787         /* Determine if the destination is an existing file or not.  If so, we
2788          * try to append the exported image(s) to it; otherwise, we create a new
2789          * WIM containing the exported image(s).  Furthermore, determine if we
2790          * need to write a pipable WIM directly to standard output.  */
2791
2792         if (tstrcmp(dest_wimfile, T("-")) == 0) {
2793         #if 0
2794                 if (!(write_flags & WIMLIB_WRITE_FLAG_PIPABLE)) {
2795                         imagex_error("Can't write a non-pipable WIM to "
2796                                      "standard output!  Specify --pipable\n"
2797                                      "       if you want to create a pipable WIM "
2798                                      "(but read the docs first).");
2799                         ret = -1;
2800                         goto out_free_src_wim;
2801                 }
2802         #else
2803                 write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
2804         #endif
2805                 dest_wimfile = NULL;
2806                 dest_wim_fd = STDOUT_FILENO;
2807                 imagex_info_file = stderr;
2808                 set_fd_to_binary_mode(dest_wim_fd);
2809         }
2810         errno = ENOENT;
2811         if (dest_wimfile != NULL && tstat(dest_wimfile, &stbuf) == 0) {
2812                 wim_is_new = false;
2813                 /* Destination file exists. */
2814
2815                 if (!S_ISREG(stbuf.st_mode)) {
2816                         imagex_error(T("\"%"TS"\" is not a regular file"),
2817                                      dest_wimfile);
2818                         ret = -1;
2819                         goto out_free_src_wim;
2820                 }
2821                 ret = wimlib_open_wim_with_progress(dest_wimfile,
2822                                                     open_flags |
2823                                                         WIMLIB_OPEN_FLAG_WRITE_ACCESS,
2824                                                     &dest_wim,
2825                                                     imagex_progress_func,
2826                                                     NULL);
2827                 if (ret)
2828                         goto out_free_src_wim;
2829
2830                 if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
2831                         /* The user specified a compression type, but we're
2832                          * exporting to an existing WIM.  Make sure the
2833                          * specified compression type is the same as the
2834                          * compression type of the existing destination WIM. */
2835                         struct wimlib_wim_info dest_info;
2836
2837                         wimlib_get_wim_info(dest_wim, &dest_info);
2838                         if (compression_type != dest_info.compression_type) {
2839                                 imagex_error(T("Cannot specify a compression type that is "
2840                                                "not the same as that used in the "
2841                                                "destination WIM"));
2842                                 ret = -1;
2843                                 goto out_free_dest_wim;
2844                         }
2845                 }
2846         } else {
2847                 wim_is_new = true;
2848
2849                 if (errno != ENOENT) {
2850                         imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
2851                                                 dest_wimfile);
2852                         ret = -1;
2853                         goto out_free_src_wim;
2854                 }
2855
2856                 /* dest_wimfile is not an existing file, so create a new WIM. */
2857
2858                 if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID) {
2859                         /* The user did not specify a compression type; default
2860                          * to that of the source WIM, unless --solid or
2861                          * --wimboot was specified.   */
2862
2863                         if (write_flags & WIMLIB_WRITE_FLAG_SOLID)
2864                                 compression_type = WIMLIB_COMPRESSION_TYPE_LZMS;
2865                         else if (export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2866                                 compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
2867                         else
2868                                 compression_type = src_info.compression_type;
2869                 }
2870                 ret = wimlib_create_new_wim(compression_type, &dest_wim);
2871                 if (ret)
2872                         goto out_free_src_wim;
2873
2874                 wimlib_register_progress_function(dest_wim,
2875                                                   imagex_progress_func, NULL);
2876
2877                 if ((export_flags & WIMLIB_EXPORT_FLAG_WIMBOOT)
2878                     && compression_type == WIMLIB_COMPRESSION_TYPE_XPRESS)
2879                 {
2880                         /* For --wimboot export, use small XPRESS chunks.  */
2881                         wimlib_set_output_chunk_size(dest_wim, 4096);
2882                 } else if (compression_type == src_info.compression_type &&
2883                            chunk_size == UINT32_MAX)
2884                 {
2885                         /* Use same chunk size if compression type is the same.  */
2886                         wimlib_set_output_chunk_size(dest_wim, src_info.chunk_size);
2887                 }
2888         }
2889
2890         if (chunk_size != UINT32_MAX) {
2891                 /* Set destination chunk size.  */
2892                 ret = wimlib_set_output_chunk_size(dest_wim, chunk_size);
2893                 if (ret)
2894                         goto out_free_dest_wim;
2895         }
2896         if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
2897                 ret = wimlib_set_output_pack_compression_type(dest_wim, solid_ctype);
2898                 if (ret)
2899                         goto out_free_dest_wim;
2900         }
2901         if (solid_chunk_size != UINT32_MAX) {
2902                 ret = wimlib_set_output_pack_chunk_size(dest_wim, solid_chunk_size);
2903                 if (ret)
2904                         goto out_free_dest_wim;
2905         }
2906
2907         image = wimlib_resolve_image(src_wim, src_image_num_or_name);
2908         ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
2909         if (ret)
2910                 goto out_free_dest_wim;
2911
2912         if (refglobs.num_strings) {
2913                 ret = wim_reference_globs(src_wim, &refglobs, open_flags);
2914                 if (ret)
2915                         goto out_free_dest_wim;
2916         }
2917
2918         if ((export_flags & WIMLIB_EXPORT_FLAG_BOOT) &&
2919             image == WIMLIB_ALL_IMAGES && src_info.boot_index == 0)
2920         {
2921                 imagex_error(T("--boot specified for all-images export, but source WIM "
2922                                "has no bootable image."));
2923                 ret = -1;
2924                 goto out_free_dest_wim;
2925         }
2926
2927         ret = wimlib_export_image(src_wim, image, dest_wim, dest_name,
2928                                   dest_desc, export_flags);
2929         if (ret) {
2930                 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
2931                         do_resource_not_found_warning(src_wimfile,
2932                                                       &src_info, &refglobs);
2933                 }
2934                 goto out_free_dest_wim;
2935         }
2936
2937         if (!wim_is_new)
2938                 ret = wimlib_overwrite(dest_wim, write_flags, num_threads);
2939         else if (dest_wimfile)
2940                 ret = wimlib_write(dest_wim, dest_wimfile, WIMLIB_ALL_IMAGES,
2941                                    write_flags, num_threads);
2942         else
2943                 ret = wimlib_write_to_fd(dest_wim, dest_wim_fd,
2944                                          WIMLIB_ALL_IMAGES, write_flags,
2945                                          num_threads);
2946 out_free_dest_wim:
2947         wimlib_free(dest_wim);
2948 out_free_src_wim:
2949         wimlib_free(src_wim);
2950 out_free_refglobs:
2951         string_set_destroy(&refglobs);
2952         return ret;
2953
2954 out_usage:
2955         usage(CMD_EXPORT, stderr);
2956 out_err:
2957         ret = -1;
2958         goto out_free_refglobs;
2959 }
2960
2961 /* Extract files or directories from a WIM image */
2962 static int
2963 imagex_extract(int argc, tchar **argv, int cmd)
2964 {
2965         int c;
2966         int open_flags = 0;
2967         int image;
2968         WIMStruct *wim;
2969         int ret;
2970         const tchar *wimfile;
2971         const tchar *image_num_or_name;
2972         tchar *dest_dir = T(".");
2973         int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
2974                             WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
2975                             WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
2976         int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
2977
2978         STRING_SET(refglobs);
2979
2980         tchar *root_path = WIMLIB_WIM_ROOT_PATH;
2981
2982         for_opt(c, extract_options) {
2983                 switch (c) {
2984                 case IMAGEX_CHECK_OPTION:
2985                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2986                         break;
2987                 case IMAGEX_VERBOSE_OPTION:
2988                         /* No longer does anything.  */
2989                         break;
2990                 case IMAGEX_REF_OPTION:
2991                         ret = string_set_append(&refglobs, optarg);
2992                         if (ret)
2993                                 goto out_free_refglobs;
2994                         break;
2995                 case IMAGEX_UNIX_DATA_OPTION:
2996                         extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
2997                         break;
2998                 case IMAGEX_NO_ACLS_OPTION:
2999                         extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
3000                         break;
3001                 case IMAGEX_STRICT_ACLS_OPTION:
3002                         extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
3003                         break;
3004                 case IMAGEX_NO_ATTRIBUTES_OPTION:
3005                         extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES;
3006                         break;
3007                 case IMAGEX_DEST_DIR_OPTION:
3008                         dest_dir = optarg;
3009                         break;
3010                 case IMAGEX_TO_STDOUT_OPTION:
3011                         extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
3012                         imagex_info_file = stderr;
3013                         imagex_be_quiet = true;
3014                         set_fd_to_binary_mode(STDOUT_FILENO);
3015                         break;
3016                 case IMAGEX_INCLUDE_INVALID_NAMES_OPTION:
3017                         extract_flags |= WIMLIB_EXTRACT_FLAG_REPLACE_INVALID_FILENAMES;
3018                         extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
3019                         break;
3020                 case IMAGEX_NO_GLOBS_OPTION:
3021                         extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3022                         break;
3023                 case IMAGEX_NULLGLOB_OPTION:
3024                         extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
3025                         break;
3026                 case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
3027                         notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
3028                         break;
3029                 case IMAGEX_WIMBOOT_OPTION:
3030                         extract_flags |= WIMLIB_EXTRACT_FLAG_WIMBOOT;
3031                         break;
3032                 default:
3033                         goto out_usage;
3034                 }
3035         }
3036         argc -= optind;
3037         argv += optind;
3038
3039         if (argc < 2)
3040                 goto out_usage;
3041
3042         if (!(extract_flags & (WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
3043                                WIMLIB_EXTRACT_FLAG_STRICT_GLOB)))
3044         {
3045                 imagex_error(T("Can't combine --no-globs and --nullglob!"));
3046                 goto out_err;
3047         }
3048
3049         wimfile = argv[0];
3050         image_num_or_name = argv[1];
3051
3052         argc -= 2;
3053         argv += 2;
3054
3055         ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3056                                             imagex_progress_func, NULL);
3057         if (ret)
3058                 goto out_free_refglobs;
3059
3060         image = wimlib_resolve_image(wim, image_num_or_name);
3061         ret = verify_image_exists_and_is_single(image,
3062                                                 image_num_or_name,
3063                                                 wimfile);
3064         if (ret)
3065                 goto out_wimlib_free;
3066
3067         if (refglobs.num_strings) {
3068                 ret = wim_reference_globs(wim, &refglobs, open_flags);
3069                 if (ret)
3070                         goto out_wimlib_free;
3071         }
3072
3073         if (argc == 0) {
3074                 argv = &root_path;
3075                 argc = 1;
3076                 extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
3077         }
3078
3079         while (argc != 0 && ret == 0) {
3080                 int num_paths;
3081
3082                 for (num_paths = 0;
3083                      num_paths < argc && argv[num_paths][0] != T('@');
3084                      num_paths++)
3085                         ;
3086
3087                 if (num_paths) {
3088                         ret = wimlib_extract_paths(wim, image, dest_dir,
3089                                                    (const tchar **)argv,
3090                                                    num_paths,
3091                                                    extract_flags | notlist_extract_flags);
3092                         argc -= num_paths;
3093                         argv += num_paths;
3094                 } else {
3095                         ret = wimlib_extract_pathlist(wim, image, dest_dir,
3096                                                       argv[0] + 1,
3097                                                       extract_flags);
3098                         argc--;
3099                         argv++;
3100                 }
3101         }
3102
3103         if (ret == 0) {
3104                 if (!imagex_be_quiet)
3105                         imagex_printf(T("Done extracting files.\n"));
3106         } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
3107                 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3108                                       WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3109                         == (WIMLIB_EXTRACT_FLAG_STRICT_GLOB |
3110                             WIMLIB_EXTRACT_FLAG_GLOB_PATHS))
3111                 {
3112                         tfprintf(stderr,
3113                                  T("Note: You can use the '--nullglob' "
3114                                    "option to ignore missing files.\n"));
3115                 }
3116                 tfprintf(stderr, T("Note: You can use `%"TS"' to see what "
3117                                    "files and directories\n"
3118                                    "      are in the WIM image.\n"),
3119                                 get_cmd_string(CMD_DIR, false));
3120         } else if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND) {
3121                 struct wimlib_wim_info info;
3122
3123                 wimlib_get_wim_info(wim, &info);
3124                 do_resource_not_found_warning(wimfile, &info, &refglobs);
3125         }
3126 out_wimlib_free:
3127         wimlib_free(wim);
3128 out_free_refglobs:
3129         string_set_destroy(&refglobs);
3130         return ret;
3131
3132 out_usage:
3133         usage(CMD_EXTRACT, stderr);
3134 out_err:
3135         ret = -1;
3136         goto out_free_refglobs;
3137 }
3138
3139 /* Prints information about a WIM file; also can mark an image as bootable,
3140  * change the name of an image, or change the description of an image. */
3141 static int
3142 imagex_info(int argc, tchar **argv, int cmd)
3143 {
3144         int c;
3145         bool boot         = false;
3146         bool check        = false;
3147         bool nocheck      = false;
3148         bool header       = false;
3149         bool blobs        = false;
3150         bool xml          = false;
3151         bool short_header = true;
3152         const tchar *xml_out_file = NULL;
3153         const tchar *wimfile;
3154         const tchar *image_num_or_name;
3155         const tchar *new_name;
3156         const tchar *new_desc;
3157         WIMStruct *wim;
3158         int image;
3159         int ret;
3160         int open_flags = 0;
3161         struct wimlib_wim_info info;
3162
3163         for_opt(c, info_options) {
3164                 switch (c) {
3165                 case IMAGEX_BOOT_OPTION:
3166                         boot = true;
3167                         break;
3168                 case IMAGEX_CHECK_OPTION:
3169                         check = true;
3170                         break;
3171                 case IMAGEX_NOCHECK_OPTION:
3172                         nocheck = true;
3173                         break;
3174                 case IMAGEX_HEADER_OPTION:
3175                         header = true;
3176                         short_header = false;
3177                         break;
3178                 case IMAGEX_BLOBS_OPTION:
3179                         blobs = true;
3180                         short_header = false;
3181                         break;
3182                 case IMAGEX_XML_OPTION:
3183                         xml = true;
3184                         short_header = false;
3185                         break;
3186                 case IMAGEX_EXTRACT_XML_OPTION:
3187                         xml_out_file = optarg;
3188                         short_header = false;
3189                         break;
3190                 case IMAGEX_METADATA_OPTION:
3191                         imagex_error(T("The --metadata option has been removed. "
3192                                        "Use 'wimdir --detail' instead."));
3193                         goto out_err;
3194                 default:
3195                         goto out_usage;
3196                 }
3197         }
3198
3199         argc -= optind;
3200         argv += optind;
3201         if (argc < 1 || argc > 4)
3202                 goto out_usage;
3203
3204         wimfile           = argv[0];
3205         image_num_or_name = (argc >= 2) ? argv[1] : T("all");
3206         new_name          = (argc >= 3) ? argv[2] : NULL;
3207         new_desc          = (argc >= 4) ? argv[3] : NULL;
3208
3209         if (check && nocheck) {
3210                 imagex_error(T("Can't specify both --check and --nocheck"));
3211                 goto out_err;
3212         }
3213
3214         if (check)
3215                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3216
3217         ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3218                                             imagex_progress_func, NULL);
3219         if (ret)
3220                 goto out;
3221
3222         wimlib_get_wim_info(wim, &info);
3223
3224         image = wimlib_resolve_image(wim, image_num_or_name);
3225         ret = WIMLIB_ERR_INVALID_IMAGE;
3226         if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
3227                 verify_image_exists(image, image_num_or_name, wimfile);
3228                 if (boot) {
3229                         imagex_error(T("If you would like to set the boot "
3230                                        "index to 0, specify image \"0\" with "
3231                                        "the --boot flag."));
3232                 }
3233                 goto out_wimlib_free;
3234         }
3235
3236         if (boot && info.image_count == 0) {
3237                 imagex_error(T("--boot is meaningless on a WIM with no images"));
3238                 goto out_wimlib_free;
3239         }
3240
3241         if (image == WIMLIB_ALL_IMAGES && info.image_count > 1) {
3242                 if (boot) {
3243                         imagex_error(T("Cannot specify the --boot flag "
3244                                        "without specifying a specific "
3245                                        "image in a multi-image WIM"));
3246                         goto out_wimlib_free;
3247                 }
3248                 if (new_name) {
3249                         imagex_error(T("Cannot specify the NEW_NAME "
3250                                        "without specifying a specific "
3251                                        "image in a multi-image WIM"));
3252                         goto out_wimlib_free;
3253                 }
3254         }
3255
3256         /* Operations that print information are separated from operations that
3257          * recreate the WIM file. */
3258         if (!new_name && !boot) {
3259
3260                 /* Read-only operations */
3261
3262                 if (image == WIMLIB_NO_IMAGE) {
3263                         imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\""),
3264                                      image_num_or_name, wimfile);
3265                         goto out_wimlib_free;
3266                 }
3267
3268                 if (image == WIMLIB_ALL_IMAGES && short_header)
3269                         print_wim_information(wimfile, &info);
3270
3271                 if (header)
3272                         wimlib_print_header(wim);
3273
3274                 if (blobs) {
3275                         if (info.total_parts != 1) {
3276                                 tfprintf(stderr, T("Warning: Only showing the blobs "
3277                                                    "for part %d of a %d-part WIM.\n"),
3278                                          info.part_number, info.total_parts);
3279                         }
3280                         print_blobs(wim);
3281                 }
3282
3283                 if (xml) {
3284                         ret = wimlib_extract_xml_data(wim, stdout);
3285                         if (ret)
3286                                 goto out_wimlib_free;
3287                 }
3288
3289                 if (xml_out_file) {
3290                         FILE *fp;
3291
3292                         fp = tfopen(xml_out_file, T("wb"));
3293                         if (!fp) {
3294                                 imagex_error_with_errno(T("Failed to open the "
3295                                                           "file \"%"TS"\" for "
3296                                                           "writing"),
3297                                                         xml_out_file);
3298                                 ret = -1;
3299                                 goto out_wimlib_free;
3300                         }
3301                         ret = wimlib_extract_xml_data(wim, fp);
3302                         if (fclose(fp)) {
3303                                 imagex_error(T("Failed to close the file "
3304                                                "\"%"TS"\""),
3305                                              xml_out_file);
3306                                 ret = -1;
3307                         }
3308                         if (ret)
3309                                 goto out_wimlib_free;
3310                 }
3311
3312                 if (short_header)
3313                         wimlib_print_available_images(wim, image);
3314
3315                 ret = 0;
3316         } else {
3317
3318                 /* Modification operations */
3319
3320                 if (image == WIMLIB_ALL_IMAGES)
3321                         image = 1;
3322
3323                 if (image == WIMLIB_NO_IMAGE && new_name) {
3324                         imagex_error(T("Cannot specify new_name (\"%"TS"\") "
3325                                        "when using image 0"), new_name);
3326                         ret = -1;
3327                         goto out_wimlib_free;
3328                 }
3329
3330                 if (boot) {
3331                         if (image == info.boot_index) {
3332                                 imagex_printf(T("Image %d is already marked as "
3333                                           "bootable.\n"), image);
3334                                 boot = false;
3335                         } else {
3336                                 imagex_printf(T("Marking image %d as bootable.\n"),
3337                                         image);
3338                                 info.boot_index = image;
3339                                 ret = wimlib_set_wim_info(wim, &info,
3340                                                           WIMLIB_CHANGE_BOOT_INDEX);
3341                                 if (ret)
3342                                         goto out_wimlib_free;
3343                         }
3344                 }
3345                 if (new_name) {
3346                         if (!tstrcmp(wimlib_get_image_name(wim, image), new_name))
3347                         {
3348                                 imagex_printf(T("Image %d is already named \"%"TS"\".\n"),
3349                                         image, new_name);
3350                                 new_name = NULL;
3351                         } else {
3352                                 imagex_printf(T("Changing the name of image %d to "
3353                                           "\"%"TS"\".\n"), image, new_name);
3354                                 ret = wimlib_set_image_name(wim, image, new_name);
3355                                 if (ret)
3356                                         goto out_wimlib_free;
3357                         }
3358                 }
3359                 if (new_desc) {
3360                         const tchar *old_desc;
3361                         old_desc = wimlib_get_image_description(wim, image);
3362                         if (old_desc && !tstrcmp(old_desc, new_desc)) {
3363                                 imagex_printf(T("The description of image %d is already "
3364                                           "\"%"TS"\".\n"), image, new_desc);
3365                                 new_desc = NULL;
3366                         } else {
3367                                 imagex_printf(T("Changing the description of image %d "
3368                                           "to \"%"TS"\".\n"), image, new_desc);
3369                                 ret = wimlib_set_image_descripton(wim, image,
3370                                                                   new_desc);
3371                                 if (ret)
3372                                         goto out_wimlib_free;
3373                         }
3374                 }
3375
3376                 /* Only call wimlib_overwrite() if something actually needs to
3377                  * be changed.  */
3378                 if (boot || new_name || new_desc ||
3379                     (check && !info.has_integrity_table) ||
3380                     (nocheck && info.has_integrity_table))
3381                 {
3382                         int write_flags = 0;
3383
3384                         if (check)
3385                                 write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3386                         if (nocheck)
3387                                 write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3388                         ret = wimlib_overwrite(wim, write_flags, 1);
3389                 } else {
3390                         imagex_printf(T("The file \"%"TS"\" was not modified "
3391                                         "because nothing needed to be done.\n"),
3392                                       wimfile);
3393                         ret = 0;
3394                 }
3395         }
3396 out_wimlib_free:
3397         wimlib_free(wim);
3398 out:
3399         return ret;
3400
3401 out_usage:
3402         usage(CMD_INFO, stderr);
3403 out_err:
3404         ret = -1;
3405         goto out;
3406 }
3407
3408 /* Join split WIMs into one part WIM */
3409 static int
3410 imagex_join(int argc, tchar **argv, int cmd)
3411 {
3412         int c;
3413         int swm_open_flags = 0;
3414         int wim_write_flags = 0;
3415         const tchar *output_path;
3416         int ret;
3417
3418         for_opt(c, join_options) {
3419                 switch (c) {
3420                 case IMAGEX_CHECK_OPTION:
3421                         swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3422                         wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3423                         break;
3424                 default:
3425                         goto out_usage;
3426                 }
3427         }
3428         argc -= optind;
3429         argv += optind;
3430
3431         if (argc < 2) {
3432                 imagex_error(T("Must specify one or more split WIM (.swm) "
3433                                "parts to join"));
3434                 goto out_usage;
3435         }
3436         output_path = argv[0];
3437         ret = wimlib_join_with_progress((const tchar * const *)++argv,
3438                                         --argc,
3439                                         output_path,
3440                                         swm_open_flags,
3441                                         wim_write_flags,
3442                                         imagex_progress_func,
3443                                         NULL);
3444 out:
3445         return ret;
3446
3447 out_usage:
3448         usage(CMD_JOIN, stderr);
3449         ret = -1;
3450         goto out;
3451 }
3452
3453 #if WIM_MOUNTING_SUPPORTED
3454
3455 /* Mounts a WIM image.  */
3456 static int
3457 imagex_mount_rw_or_ro(int argc, tchar **argv, int cmd)
3458 {
3459         int c;
3460         int mount_flags = 0;
3461         int open_flags = 0;
3462         const tchar *staging_dir = NULL;
3463         const tchar *wimfile;
3464         const tchar *dir;
3465         WIMStruct *wim;
3466         struct wimlib_wim_info info;
3467         int image;
3468         int ret;
3469
3470         STRING_SET(refglobs);
3471
3472         if (cmd == CMD_MOUNTRW) {
3473                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
3474                 open_flags |= WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3475         }
3476
3477         for_opt(c, mount_options) {
3478                 switch (c) {
3479                 case IMAGEX_ALLOW_OTHER_OPTION:
3480                         mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
3481                         break;
3482                 case IMAGEX_CHECK_OPTION:
3483                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3484                         break;
3485                 case IMAGEX_DEBUG_OPTION:
3486                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
3487                         break;
3488                 case IMAGEX_STREAMS_INTERFACE_OPTION:
3489                         if (!tstrcasecmp(optarg, T("none")))
3490                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
3491                         else if (!tstrcasecmp(optarg, T("xattr")))
3492                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
3493                         else if (!tstrcasecmp(optarg, T("windows")))
3494                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
3495                         else {
3496                                 imagex_error(T("Unknown stream interface \"%"TS"\""),
3497                                              optarg);
3498                                 goto out_usage;
3499                         }
3500                         break;
3501                 case IMAGEX_REF_OPTION:
3502                         ret = string_set_append(&refglobs, optarg);
3503                         if (ret)
3504                                 goto out_free_refglobs;
3505                         break;
3506                 case IMAGEX_STAGING_DIR_OPTION:
3507                         staging_dir = optarg;
3508                         break;
3509                 case IMAGEX_UNIX_DATA_OPTION:
3510                         mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
3511                         break;
3512                 default:
3513                         goto out_usage;
3514                 }
3515         }
3516         argc -= optind;
3517         argv += optind;
3518         if (argc != 2 && argc != 3)
3519                 goto out_usage;
3520
3521         wimfile = argv[0];
3522
3523         ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3524                                             imagex_progress_func, NULL);
3525         if (ret)
3526                 goto out_free_refglobs;
3527
3528         wimlib_get_wim_info(wim, &info);
3529
3530         if (argc >= 3) {
3531                 /* Image explicitly specified.  */
3532                 image = wimlib_resolve_image(wim, argv[1]);
3533                 dir = argv[2];
3534                 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
3535                 if (ret)
3536                         goto out_free_wim;
3537         } else {
3538                 /* No image specified; default to image 1, but only if the WIM
3539                  * contains exactly one image.  */
3540
3541                 if (info.image_count != 1) {
3542                         imagex_error(T("\"%"TS"\" contains %d images; Please "
3543                                        "select one."), wimfile, info.image_count);
3544                         wimlib_free(wim);
3545                         goto out_usage;
3546                 }
3547                 image = 1;
3548                 dir = argv[1];
3549         }
3550
3551         if (refglobs.num_strings) {
3552                 ret = wim_reference_globs(wim, &refglobs, open_flags);
3553                 if (ret)
3554                         goto out_free_wim;
3555         }
3556
3557         ret = wimlib_mount_image(wim, image, dir, mount_flags, staging_dir);
3558         if (ret) {
3559                 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
3560                                "on \"%"TS"\""),
3561                              image, wimfile, dir);
3562         }
3563 out_free_wim:
3564         wimlib_free(wim);
3565 out_free_refglobs:
3566         string_set_destroy(&refglobs);
3567         return ret;
3568
3569 out_usage:
3570         usage(cmd, stderr);
3571         ret = -1;
3572         goto out_free_refglobs;
3573 }
3574 #endif /* WIM_MOUNTING_SUPPORTED */
3575
3576 /* Rebuild a WIM file */
3577 static int
3578 imagex_optimize(int argc, tchar **argv, int cmd)
3579 {
3580         int c;
3581         int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3582         int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
3583         int compression_type = WIMLIB_COMPRESSION_TYPE_INVALID;
3584         uint32_t chunk_size = UINT32_MAX;
3585         uint32_t solid_chunk_size = UINT32_MAX;
3586         int solid_ctype = WIMLIB_COMPRESSION_TYPE_INVALID;
3587         int ret;
3588         WIMStruct *wim;
3589         const tchar *wimfile;
3590         off_t old_size;
3591         off_t new_size;
3592         unsigned num_threads = 0;
3593
3594         for_opt(c, optimize_options) {
3595                 switch (c) {
3596                 case IMAGEX_CHECK_OPTION:
3597                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3598                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3599                         break;
3600                 case IMAGEX_NOCHECK_OPTION:
3601                         write_flags |= WIMLIB_WRITE_FLAG_NO_CHECK_INTEGRITY;
3602                         break;
3603                 case IMAGEX_COMPRESS_OPTION:
3604                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3605                         compression_type = get_compression_type(optarg);
3606                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
3607                                 goto out_err;
3608                         break;
3609                 case IMAGEX_COMPRESS_SLOW_OPTION:
3610                         set_compress_slow();
3611                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3612                         break;
3613                 case IMAGEX_RECOMPRESS_OPTION:
3614                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3615                         break;
3616                 case IMAGEX_CHUNK_SIZE_OPTION:
3617                         chunk_size = parse_chunk_size(optarg);
3618                         if (chunk_size == UINT32_MAX)
3619                                 goto out_err;
3620                         break;
3621                 case IMAGEX_SOLID_CHUNK_SIZE_OPTION:
3622                         solid_chunk_size = parse_chunk_size(optarg);
3623                         if (solid_chunk_size == UINT32_MAX)
3624                                 goto out_err;
3625                         break;
3626                 case IMAGEX_SOLID_COMPRESS_OPTION:
3627                         solid_ctype = get_compression_type(optarg);
3628                         if (solid_ctype == WIMLIB_COMPRESSION_TYPE_INVALID)
3629                                 goto out_err;
3630                         break;
3631                 case IMAGEX_SOLID_OPTION:
3632                         write_flags |= WIMLIB_WRITE_FLAG_SOLID;
3633                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
3634                         break;
3635                 case IMAGEX_NO_SOLID_SORT_OPTION:
3636                         write_flags |= WIMLIB_WRITE_FLAG_NO_SOLID_SORT;
3637                         break;
3638                 case IMAGEX_THREADS_OPTION:
3639                         num_threads = parse_num_threads(optarg);
3640                         if (num_threads == UINT_MAX)
3641                                 goto out_err;
3642                         break;
3643                 case IMAGEX_PIPABLE_OPTION:
3644                         write_flags |= WIMLIB_WRITE_FLAG_PIPABLE;
3645                         break;
3646                 case IMAGEX_NOT_PIPABLE_OPTION:
3647                         write_flags |= WIMLIB_WRITE_FLAG_NOT_PIPABLE;
3648                         break;
3649                 default:
3650                         goto out_usage;
3651                 }
3652         }
3653         argc -= optind;
3654         argv += optind;
3655
3656         if (argc != 1)
3657                 goto out_usage;
3658
3659         wimfile = argv[0];
3660
3661         ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3662                                             imagex_progress_func, NULL);
3663         if (ret)
3664                 goto out;
3665
3666         if (compression_type != WIMLIB_COMPRESSION_TYPE_INVALID) {
3667                 /* Change compression type.  */
3668                 ret = wimlib_set_output_compression_type(wim, compression_type);
3669                 if (ret)
3670                         goto out_wimlib_free;
3671         }
3672
3673         if (chunk_size != UINT32_MAX) {
3674                 /* Change chunk size.  */
3675                 ret = wimlib_set_output_chunk_size(wim, chunk_size);
3676                 if (ret)
3677                         goto out_wimlib_free;
3678         }
3679         if (solid_ctype != WIMLIB_COMPRESSION_TYPE_INVALID) {
3680                 ret = wimlib_set_output_pack_compression_type(wim, solid_ctype);
3681                 if (ret)
3682                         goto out_wimlib_free;
3683         }
3684         if (solid_chunk_size != UINT32_MAX) {
3685                 ret = wimlib_set_output_pack_chunk_size(wim, solid_chunk_size);
3686                 if (ret)
3687                         goto out_wimlib_free;
3688         }
3689
3690         old_size = file_get_size(wimfile);
3691         tprintf(T("\"%"TS"\" original size: "), wimfile);
3692         if (old_size == -1)
3693                 tputs(T("Unknown"));
3694         else
3695                 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
3696
3697         ret = wimlib_overwrite(wim, write_flags, num_threads);
3698         if (ret) {
3699                 imagex_error(T("Optimization of \"%"TS"\" failed."), wimfile);
3700                 goto out_wimlib_free;
3701         }
3702
3703         new_size = file_get_size(wimfile);
3704         tprintf(T("\"%"TS"\" optimized size: "), wimfile);
3705         if (new_size == -1)
3706                 tputs(T("Unknown"));
3707         else
3708                 tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
3709
3710         tfputs(T("Space saved: "), stdout);
3711         if (new_size != -1 && old_size != -1) {
3712                 tprintf(T("%lld KiB\n"),
3713                        ((long long)old_size - (long long)new_size) >> 10);
3714         } else {
3715                 tputs(T("Unknown"));
3716         }
3717         ret = 0;
3718 out_wimlib_free:
3719         wimlib_free(wim);
3720 out:
3721         return ret;
3722
3723 out_usage:
3724         usage(CMD_OPTIMIZE, stderr);
3725 out_err:
3726         ret = -1;
3727         goto out;
3728 }
3729
3730 /* Split a WIM into a spanned set */
3731 static int
3732 imagex_split(int argc, tchar **argv, int cmd)
3733 {
3734         int c;
3735         int open_flags = 0;
3736         int write_flags = 0;
3737         unsigned long part_size;
3738         tchar *tmp;
3739         int ret;
3740         WIMStruct *wim;
3741
3742         for_opt(c, split_options) {
3743                 switch (c) {
3744                 case IMAGEX_CHECK_OPTION:
3745                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3746                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3747                         break;
3748                 default:
3749                         goto out_usage;
3750                 }
3751         }
3752         argc -= optind;
3753         argv += optind;
3754
3755         if (argc != 3)
3756                 goto out_usage;
3757
3758         part_size = tstrtod(argv[2], &tmp) * (1 << 20);
3759         if (tmp == argv[2] || *tmp) {
3760                 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
3761                 imagex_error(T("The part size must be an integer or "
3762                                "floating-point number of megabytes."));
3763                 goto out_err;
3764         }
3765         ret = wimlib_open_wim_with_progress(argv[0], open_flags, &wim,
3766                                             imagex_progress_func, NULL);
3767         if (ret)
3768                 goto out;
3769
3770         ret = wimlib_split(wim, argv[1], part_size, write_flags);
3771         wimlib_free(wim);
3772 out:
3773         return ret;
3774
3775 out_usage:
3776         usage(CMD_SPLIT, stderr);
3777 out_err:
3778         ret = -1;
3779         goto out;
3780 }
3781
3782 #if WIM_MOUNTING_SUPPORTED
3783 /* Unmounts a mounted WIM image. */
3784 static int
3785 imagex_unmount(int argc, tchar **argv, int cmd)
3786 {
3787         int c;
3788         int unmount_flags = 0;
3789         int ret;
3790
3791         for_opt(c, unmount_options) {
3792                 switch (c) {
3793                 case IMAGEX_COMMIT_OPTION:
3794                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
3795                         break;
3796                 case IMAGEX_CHECK_OPTION:
3797                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
3798                         break;
3799                 case IMAGEX_REBUILD_OPTION:
3800                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
3801                         break;
3802                 case IMAGEX_LAZY_OPTION:
3803                 case IMAGEX_FORCE_OPTION:
3804                         /* Now, unmount is lazy by default.  However, committing
3805                          * the image will fail with
3806                          * WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY if there are open
3807                          * file descriptors on the WIM image.  The
3808                          * WIMLIB_UNMOUNT_FLAG_FORCE option forces these file
3809                          * descriptors to be closed.  */
3810                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_FORCE;
3811                         break;
3812                 case IMAGEX_NEW_IMAGE_OPTION:
3813                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_NEW_IMAGE;
3814                         break;
3815                 default:
3816                         goto out_usage;
3817                 }
3818         }
3819         argc -= optind;
3820         argv += optind;
3821         if (argc != 1)
3822                 goto out_usage;
3823
3824         if (unmount_flags & WIMLIB_UNMOUNT_FLAG_NEW_IMAGE) {
3825                 if (!(unmount_flags & WIMLIB_UNMOUNT_FLAG_COMMIT)) {
3826                         imagex_error(T("--new-image is meaningless "
3827                                        "without --commit also specified!"));
3828                         goto out_err;
3829                 }
3830         }
3831
3832         ret = wimlib_unmount_image_with_progress(argv[0], unmount_flags,
3833                                                  imagex_progress_func, NULL);
3834         if (ret) {
3835                 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
3836                 if (ret == WIMLIB_ERR_MOUNTED_IMAGE_IS_BUSY) {
3837                         imagex_printf(T(
3838                                 "\tNote: Use --commit --force to force changes "
3839                                         "to be committed, regardless\n"
3840                                 "\t      of open files.\n"));
3841                 }
3842         }
3843 out:
3844         return ret;
3845
3846 out_usage:
3847         usage(CMD_UNMOUNT, stderr);
3848 out_err:
3849         ret = -1;
3850         goto out;
3851 }
3852 #endif /* WIM_MOUNTING_SUPPORTED */
3853
3854 /*
3855  * Add, delete, or rename files in a WIM image.
3856  */
3857 static int
3858 imagex_update(int argc, tchar **argv, int cmd)
3859 {
3860         const tchar *wimfile;
3861         int image;
3862         WIMStruct *wim;
3863         int ret;
3864         int open_flags = WIMLIB_OPEN_FLAG_WRITE_ACCESS;
3865         int write_flags = 0;
3866         int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
3867         int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
3868                                 WIMLIB_ADD_FLAG_VERBOSE |
3869                                 WIMLIB_ADD_FLAG_WINCONFIG;
3870         int default_delete_flags = 0;
3871         unsigned num_threads = 0;
3872         int c;
3873         tchar *cmd_file_contents;
3874         size_t cmd_file_nchars;
3875         struct wimlib_update_command *cmds;
3876         size_t num_cmds;
3877         tchar *command_str = NULL;
3878         tchar *config_file = NULL;
3879         tchar *wimboot_config = NULL;
3880
3881         for_opt(c, update_options) {
3882                 switch (c) {
3883                 /* Generic or write options */
3884                 case IMAGEX_THREADS_OPTION:
3885                         num_threads = parse_num_threads(optarg);
3886                         if (num_threads == UINT_MAX)
3887                                 goto out_err;
3888                         break;
3889                 case IMAGEX_CHECK_OPTION:
3890                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
3891                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
3892                         break;
3893                 case IMAGEX_REBUILD_OPTION:
3894                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
3895                         break;
3896                 case IMAGEX_COMMAND_OPTION:
3897                         if (command_str) {
3898                                 imagex_error(T("--command may only be specified "
3899                                                "one time.  Please provide\n"
3900                                                "       the update commands "
3901                                                "on standard input instead."));
3902                                 goto out_err;
3903                         }
3904                         command_str = tstrdup(optarg);
3905                         if (!command_str) {
3906                                 imagex_error(T("Out of memory!"));
3907                                 goto out_err;
3908                         }
3909                         break;
3910                 case IMAGEX_WIMBOOT_CONFIG_OPTION:
3911                         wimboot_config = optarg;
3912                         break;
3913                 /* Default delete options */
3914                 case IMAGEX_FORCE_OPTION:
3915                         default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
3916                         break;
3917                 case IMAGEX_RECURSIVE_OPTION:
3918                         default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
3919                         break;
3920
3921                 /* Global add option */
3922                 case IMAGEX_CONFIG_OPTION:
3923                         default_add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
3924                         config_file = optarg;
3925                         break;
3926
3927                 /* Default add options */
3928                 case IMAGEX_VERBOSE_OPTION:
3929                         /* No longer does anything.  */
3930                         break;
3931                 case IMAGEX_DEREFERENCE_OPTION:
3932                         default_add_flags |= WIMLIB_ADD_FLAG_DEREFERENCE;
3933                         break;
3934                 case IMAGEX_UNIX_DATA_OPTION:
3935                         default_add_flags |= WIMLIB_ADD_FLAG_UNIX_DATA;
3936                         break;
3937                 case IMAGEX_NO_ACLS_OPTION:
3938                         default_add_flags |= WIMLIB_ADD_FLAG_NO_ACLS;
3939                         break;
3940                 case IMAGEX_STRICT_ACLS_OPTION:
3941                         default_add_flags |= WIMLIB_ADD_FLAG_STRICT_ACLS;
3942                         break;
3943                 case IMAGEX_NO_REPLACE_OPTION:
3944                         default_add_flags |= WIMLIB_ADD_FLAG_NO_REPLACE;
3945                         break;
3946                 default:
3947                         goto out_usage;
3948                 }
3949         }
3950         argv += optind;
3951         argc -= optind;
3952
3953         if (argc != 1 && argc != 2)
3954                 goto out_usage;
3955         wimfile = argv[0];
3956
3957         ret = wimlib_open_wim_with_progress(wimfile, open_flags, &wim,
3958                                             imagex_progress_func, NULL);
3959         if (ret)
3960                 goto out_free_command_str;
3961
3962         if (argc >= 2) {
3963                 /* Image explicitly specified.  */
3964                 image = wimlib_resolve_image(wim, argv[1]);
3965                 ret = verify_image_exists_and_is_single(image, argv[1],
3966                                                         wimfile);
3967                 if (ret)
3968                         goto out_wimlib_free;
3969         } else {
3970                 /* No image specified; default to image 1, but only if the WIM
3971                  * contains exactly one image.  */
3972                 struct wimlib_wim_info info;
3973
3974                 wimlib_get_wim_info(wim, &info);
3975                 if (info.image_count != 1) {
3976                         imagex_error(T("\"%"TS"\" contains %d images; Please select one."),
3977                                      wimfile, info.image_count);
3978                         wimlib_free(wim);
3979                         goto out_usage;
3980                 }
3981                 image = 1;
3982         }
3983
3984         /* Read update commands from standard input, or the command string if
3985          * specified.  */
3986         if (command_str) {
3987                 cmd_file_contents = NULL;
3988                 cmds = parse_update_command_file(&command_str, tstrlen(command_str),
3989                                                  &num_cmds);
3990                 if (!cmds) {
3991                         ret = -1;
3992                         goto out_free_cmd_file_contents;
3993                 }
3994         } else if (!wimboot_config) {
3995                 if (isatty(STDIN_FILENO)) {
3996                         tputs(T("Reading update commands from standard input..."));
3997                         recommend_man_page(CMD_UPDATE, stdout);
3998                 }
3999                 cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
4000                 if (!cmd_file_contents) {
4001                         ret = -1;
4002                         goto out_wimlib_free;
4003                 }
4004
4005                 /* Parse the update commands */
4006                 cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
4007                                                  &num_cmds);
4008                 if (!cmds) {
4009                         ret = -1;
4010                         goto out_free_cmd_file_contents;
4011                 }
4012         } else {
4013                 cmd_file_contents = NULL;
4014                 cmds = NULL;
4015                 num_cmds = 0;
4016         }
4017
4018         /* Set default flags and capture config on the update commands */
4019         for (size_t i = 0; i < num_cmds; i++) {
4020                 switch (cmds[i].op) {
4021                 case WIMLIB_UPDATE_OP_ADD:
4022                         cmds[i].add.add_flags |= default_add_flags;
4023                         cmds[i].add.config_file = config_file;
4024                         break;
4025                 case WIMLIB_UPDATE_OP_DELETE:
4026                         cmds[i].delete_.delete_flags |= default_delete_flags;
4027                         break;
4028                 default:
4029                         break;
4030                 }
4031         }
4032
4033         /* Execute the update commands */
4034         ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags);
4035         if (ret)
4036                 goto out_free_cmds;
4037
4038         if (wimboot_config) {
4039                 /* --wimboot-config=FILE is short for an
4040                  * "add FILE /Windows/System32/WimBootCompress.ini" command.
4041                  */
4042                 struct wimlib_update_command cmd;
4043
4044                 cmd.op = WIMLIB_UPDATE_OP_ADD;
4045                 cmd.add.fs_source_path = wimboot_config;
4046                 cmd.add.wim_target_path = T("/Windows/System32/WimBootCompress.ini");
4047                 cmd.add.config_file = NULL;
4048                 cmd.add.add_flags = 0;
4049
4050                 ret = wimlib_update_image(wim, image, &cmd, 1, update_flags);
4051                 if (ret)
4052                         goto out_free_cmds;
4053         }
4054
4055         /* Overwrite the updated WIM */
4056         ret = wimlib_overwrite(wim, write_flags, num_threads);
4057 out_free_cmds:
4058         free(cmds);
4059 out_free_cmd_file_contents:
4060         free(cmd_file_contents);
4061 out_wimlib_free:
4062         wimlib_free(wim);
4063 out_free_command_str:
4064         free(command_str);
4065         return ret;
4066
4067 out_usage:
4068         usage(CMD_UPDATE, stderr);
4069 out_err:
4070         ret = -1;
4071         goto out_free_command_str;
4072 }
4073
4074 /* Verify a WIM file.  */
4075 static int
4076 imagex_verify(int argc, tchar **argv, int cmd)
4077 {
4078         int ret;
4079         const tchar *wimfile;
4080         WIMStruct *wim;
4081         int open_flags = WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4082         int verify_flags = 0;
4083         STRING_SET(refglobs);
4084         int c;
4085
4086         for_opt(c, verify_options) {
4087                 switch (c) {
4088                 case IMAGEX_REF_OPTION:
4089                         ret = string_set_append(&refglobs, optarg);
4090                         if (ret)
4091                                 goto out_free_refglobs;
4092                         break;
4093                 case IMAGEX_NOCHECK_OPTION:
4094                         open_flags &= ~WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
4095                         break;
4096                 default:
4097                         goto out_usage;
4098                 }
4099         }
4100
4101         argv += optind;
4102         argc -= optind;
4103
4104         if (argc != 1) {
4105                 if (argc == 0)
4106                         imagex_error(T("Must specify a WIM file!"));
4107                 else
4108                         imagex_error(T("At most one WIM file can be specified!"));
4109                 goto out_usage;
4110         }
4111
4112         wimfile = argv[0];
4113
4114         ret = wimlib_open_wim_with_progress(wimfile,
4115                                             open_flags,
4116                                             &wim,
4117                                             imagex_progress_func,
4118                                             NULL);
4119         if (ret)
4120                 goto out_free_refglobs;
4121
4122         ret = wim_reference_globs(wim, &refglobs, open_flags);
4123         if (ret)
4124                 goto out_wimlib_free;
4125
4126         ret = wimlib_verify_wim(wim, verify_flags);
4127         if (ret) {
4128                 tputc(T('\n'), stderr);
4129                 imagex_error(T("\"%"TS"\" failed verification!"),
4130                              wimfile);
4131                 if (ret == WIMLIB_ERR_RESOURCE_NOT_FOUND &&
4132                     refglobs.num_strings == 0)
4133                 {
4134                         imagex_printf(T("Note: if this WIM file is not standalone, "
4135                                         "use the --ref option to specify the other parts.\n"));
4136                 }
4137         } else {
4138                 imagex_printf(T("\n\"%"TS"\" was successfully verified.\n"),
4139                               wimfile);
4140         }
4141
4142 out_wimlib_free:
4143         wimlib_free(wim);
4144 out_free_refglobs:
4145         string_set_destroy(&refglobs);
4146         return ret;
4147
4148 out_usage:
4149         usage(CMD_VERIFY, stderr);
4150         ret = -1;
4151         goto out_free_refglobs;
4152 }
4153
4154 struct imagex_command {
4155         const tchar *name;
4156         int (*func)(int argc, tchar **argv, int cmd);
4157 };
4158
4159 static const struct imagex_command imagex_commands[] = {
4160         [CMD_APPEND]   = {T("append"),   imagex_capture_or_append},
4161         [CMD_APPLY]    = {T("apply"),    imagex_apply},
4162         [CMD_CAPTURE]  = {T("capture"),  imagex_capture_or_append},
4163         [CMD_DELETE]   = {T("delete"),   imagex_delete},
4164         [CMD_DIR ]     = {T("dir"),      imagex_dir},
4165         [CMD_EXPORT]   = {T("export"),   imagex_export},
4166         [CMD_EXTRACT]  = {T("extract"),  imagex_extract},
4167         [CMD_INFO]     = {T("info"),     imagex_info},
4168         [CMD_JOIN]     = {T("join"),     imagex_join},
4169 #if WIM_MOUNTING_SUPPORTED
4170         [CMD_MOUNT]    = {T("mount"),    imagex_mount_rw_or_ro},
4171         [CMD_MOUNTRW]  = {T("mountrw"),  imagex_mount_rw_or_ro},
4172 #endif
4173         [CMD_OPTIMIZE] = {T("optimize"), imagex_optimize},
4174         [CMD_SPLIT]    = {T("split"),    imagex_split},
4175 #if WIM_MOUNTING_SUPPORTED
4176         [CMD_UNMOUNT]  = {T("unmount"),  imagex_unmount},
4177 #endif
4178         [CMD_UPDATE]   = {T("update"),   imagex_update},
4179         [CMD_VERIFY]   = {T("verify"),   imagex_verify},
4180 };
4181
4182 #ifdef __WIN32__
4183
4184    /* Can be a directory or source list file.  But source list file is probably
4185     * a rare use case, so just say directory.  */
4186 #  define SOURCE_STR T("DIRECTORY")
4187
4188    /* Can only be a directory  */
4189 #  define TARGET_STR T("DIRECTORY")
4190
4191 #else
4192    /* Can be a directory, NTFS volume, or source list file. */
4193 #  define SOURCE_STR T("SOURCE")
4194
4195    /* Can be a directory or NTFS volume.  */
4196 #  define TARGET_STR T("TARGET")
4197
4198 #endif
4199
4200 static const tchar *usage_strings[] = {
4201 [CMD_APPEND] =
4202 T(
4203 "    %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4204 "                    [--boot] [--check] [--nocheck] [--config=FILE]\n"
4205 "                    [--threads=NUM_THREADS] [--no-acls] [--strict-acls]\n"
4206 "                    [--rpfix] [--norpfix] [--update-of=[WIMFILE:]IMAGE]\n"
4207 "                    [--wimboot] [--unix-data] [--dereference]\n"
4208 ),
4209 [CMD_APPLY] =
4210 T(
4211 "    %"TS" WIMFILE [IMAGE] " TARGET_STR "\n"
4212 "                    [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
4213 "                    [--no-attributes] [--rpfix] [--norpfix]\n"
4214 "                    [--include-invalid-names] [--wimboot] [--unix-data]\n"
4215 ),
4216 [CMD_CAPTURE] =
4217 T(
4218 "    %"TS" " SOURCE_STR " WIMFILE [IMAGE_NAME [IMAGE_DESC]]\n"
4219 "                    [--compress=TYPE] [--boot] [--check] [--nocheck]\n"
4220 "                    [--config=FILE] [--threads=NUM_THREADS]\n"
4221 "                    [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
4222 "                    [--update-of=[WIMFILE:]IMAGE] [--delta-from=WIMFILE]\n"
4223 "                    [--wimboot] [--unix-data] [--dereference] [--solid]\n"
4224 ),
4225 [CMD_DELETE] =
4226 T(
4227 "    %"TS" WIMFILE IMAGE [--check] [--soft]\n"
4228 ),
4229 [CMD_DIR] =
4230 T(
4231 "    %"TS" WIMFILE IMAGE [--path=PATH] [--detailed]\n"
4232 ),
4233 [CMD_EXPORT] =
4234 T(
4235 "    %"TS" SRC_WIMFILE SRC_IMAGE DEST_WIMFILE\n"
4236 "                        [DEST_IMAGE_NAME [DEST_IMAGE_DESC]]\n"
4237 "                    [--boot] [--check] [--nocheck] [--compress=TYPE]\n"
4238 "                    [--ref=\"GLOB\"] [--threads=NUM_THREADS] [--rebuild]\n"
4239 "                    [--wimboot] [--solid]\n"
4240 ),
4241 [CMD_EXTRACT] =
4242 T(
4243 "    %"TS" WIMFILE IMAGE [(PATH | @LISTFILE)...]\n"
4244 "                    [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
4245 "                    [--to-stdout] [--no-acls] [--strict-acls]\n"
4246 "                    [--no-attributes] [--include-invalid-names]\n"
4247 "                    [--no-globs] [--nullglob] [--preserve-dir-structure]\n"
4248 ),
4249 [CMD_INFO] =
4250 T(
4251 "    %"TS" WIMFILE [IMAGE [NEW_NAME [NEW_DESC]]]\n"
4252 "                    [--boot] [--check] [--nocheck] [--xml]\n"
4253 "                    [--extract-xml FILE] [--header] [--blobs]\n"
4254 ),
4255 [CMD_JOIN] =
4256 T(
4257 "    %"TS" OUT_WIMFILE SPLIT_WIM_PART... [--check]\n"
4258 ),
4259 #if WIM_MOUNTING_SUPPORTED
4260 [CMD_MOUNT] =
4261 T(
4262 "    %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4263 "                    [--check] [--streams-interface=INTERFACE]\n"
4264 "                    [--ref=\"GLOB\"] [--allow-other] [--unix-data]\n"
4265 ),
4266 [CMD_MOUNTRW] =
4267 T(
4268 "    %"TS" WIMFILE [IMAGE] DIRECTORY\n"
4269 "                    [--check] [--streams-interface=INTERFACE]\n"
4270 "                    [--staging-dir=CMD_DIR] [--allow-other] [--unix-data]\n"
4271 ),
4272 #endif
4273 [CMD_OPTIMIZE] =
4274 T(
4275 "    %"TS" WIMFILE\n"
4276 "                    [--recompress] [--compress=TYPE] [--threads=NUM_THREADS]\n"
4277 "                    [--check] [--nocheck] [--solid]\n"
4278 "\n"
4279 ),
4280 [CMD_SPLIT] =
4281 T(
4282 "    %"TS" WIMFILE SPLIT_WIM_PART_1 PART_SIZE_MB [--check]\n"
4283 ),
4284 #if WIM_MOUNTING_SUPPORTED
4285 [CMD_UNMOUNT] =
4286 T(
4287 "    %"TS" DIRECTORY\n"
4288 "                    [--commit] [--force] [--new-image] [--check] [--rebuild]\n"
4289 ),
4290 #endif
4291 [CMD_UPDATE] =
4292 T(
4293 "    %"TS" WIMFILE [IMAGE]\n"
4294 "                    [--check] [--rebuild] [--threads=NUM_THREADS]\n"
4295 "                    [DEFAULT_ADD_OPTIONS] [DEFAULT_DELETE_OPTIONS]\n"
4296 "                    [--command=STRING] [--wimboot-config=FILE]\n"
4297 "                    [< CMDFILE]\n"
4298 ),
4299 [CMD_VERIFY] =
4300 T(
4301 "    %"TS" WIMFILE [--ref=\"GLOB\"]\n"
4302 ),
4303 };
4304
4305 static const tchar *invocation_name;
4306 static int invocation_cmd = CMD_NONE;
4307
4308 static const tchar *get_cmd_string(int cmd, bool nospace)
4309 {
4310         static tchar buf[50];
4311         if (cmd == CMD_NONE) {
4312                 return T("wimlib-imagex");
4313         } else if (invocation_cmd != CMD_NONE) {
4314                 tsprintf(buf, T("wim%"TS), imagex_commands[cmd].name);
4315         } else {
4316                 const tchar *format;
4317
4318                 if (nospace)
4319                         format = T("%"TS"-%"TS"");
4320                 else
4321                         format = T("%"TS" %"TS"");
4322                 tsprintf(buf, format, invocation_name, imagex_commands[cmd].name);
4323         }
4324         return buf;
4325 }
4326
4327 static void
4328 version(void)
4329 {
4330         static const tchar *s =
4331         T(
4332 "wimlib-imagex (distributed with " PACKAGE " " PACKAGE_VERSION ")\n"
4333 "Copyright (C) 2012, 2013, 2014, 2015 Eric Biggers\n"
4334 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
4335 "This is free software: you are free to change and redistribute it.\n"
4336 "There is NO WARRANTY, to the extent permitted by law.\n"
4337 "\n"
4338 "Report bugs to "PACKAGE_BUGREPORT".\n"
4339         );
4340         tfputs(s, stdout);
4341 }
4342
4343
4344 static void
4345 help_or_version(int argc, tchar **argv, int cmd)
4346 {
4347         int i;
4348         const tchar *p;
4349
4350         for (i = 1; i < argc; i++) {
4351                 p = argv[i];
4352                 if (p[0] == T('-') && p[1] == T('-')) {
4353                         p += 2;
4354                         if (!tstrcmp(p, T("help"))) {
4355                                 if (cmd == CMD_NONE)
4356                                         usage_all(stdout);
4357                                 else
4358                                         usage(cmd, stdout);
4359                                 exit(0);
4360                         } else if (!tstrcmp(p, T("version"))) {
4361                                 version();
4362                                 exit(0);
4363                         }
4364                 }
4365         }
4366 }
4367
4368 static void
4369 print_usage_string(int cmd, FILE *fp)
4370 {
4371         tfprintf(fp, usage_strings[cmd], get_cmd_string(cmd, false));
4372 }
4373
4374 static void
4375 recommend_man_page(int cmd, FILE *fp)
4376 {
4377         const tchar *format_str;
4378 #ifdef __WIN32__
4379         format_str = T("Some uncommon options are not listed;\n"
4380                        "See %"TS".pdf in the doc directory for more details.\n");
4381 #else
4382         format_str = T("Some uncommon options are not listed;\n"
4383                        "Try `man %"TS"' for more details.\n");
4384 #endif
4385         tfprintf(fp, format_str, get_cmd_string(cmd, true));
4386 }
4387
4388 static void
4389 usage(int cmd, FILE *fp)
4390 {
4391         tfprintf(fp, T("Usage:\n"));
4392         print_usage_string(cmd, fp);
4393         tfprintf(fp, T("\n"));
4394         recommend_man_page(cmd, fp);
4395 }
4396
4397 static void
4398 usage_all(FILE *fp)
4399 {
4400         tfprintf(fp, T("Usage:\n"));
4401         for (int cmd = 0; cmd < CMD_MAX; cmd++) {
4402                 print_usage_string(cmd, fp);
4403                 tfprintf(fp, T("\n"));
4404         }
4405         static const tchar *extra =
4406         T(
4407 "    %"TS" --help\n"
4408 "    %"TS" --version\n"
4409 "\n"
4410         );
4411         tfprintf(fp, extra, invocation_name, invocation_name);
4412         tfprintf(fp,
4413                  T("IMAGE can be the 1-based index or name of an image in the WIM file.\n"
4414                    "For some commands IMAGE is optional if the WIM file only contains one image.\n"
4415                    "For some commands IMAGE may be \"all\".\n"
4416                    "\n"));
4417         recommend_man_page(CMD_NONE, fp);
4418 }
4419
4420 /* Entry point for wimlib's ImageX implementation.  On UNIX the command
4421  * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
4422  * something else), while on Windows the command arguments will be UTF-16LE
4423  * encoded 'wchar_t' strings. */
4424 int
4425 #ifdef __WIN32__
4426 wmain(int argc, wchar_t **argv, wchar_t **envp)
4427 #else
4428 main(int argc, char **argv)
4429 #endif
4430 {
4431         int ret;
4432         int init_flags = 0;
4433         int cmd;
4434
4435         imagex_info_file = stdout;
4436         invocation_name = tbasename(argv[0]);
4437
4438 #ifndef __WIN32__
4439         if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
4440                 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
4441         } else {
4442                 char *codeset;
4443
4444                 setlocale(LC_ALL, "");
4445                 codeset = nl_langinfo(CODESET);
4446                 if (!strstr(codeset, "UTF-8") &&
4447                     !strstr(codeset, "UTF8") &&
4448                     !strstr(codeset, "utf-8") &&
4449                     !strstr(codeset, "utf8"))
4450                 {
4451                         fprintf(stderr,
4452 "WARNING: Running %"TS" in a UTF-8 locale is recommended!\n"
4453 "         Maybe try: `export LANG=en_US.UTF-8'?\n"
4454 "         Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
4455 "         to any value to force wimlib to use UTF-8.\n",
4456                         invocation_name);
4457
4458                 }
4459         }
4460
4461 #endif /* !__WIN32__ */
4462
4463         {
4464                 tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE"));
4465                 if (igcase != NULL) {
4466                         if (!tstrcmp(igcase, T("no")) ||
4467                             !tstrcmp(igcase, T("0")))
4468                                 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE;
4469                         else if (!tstrcmp(igcase, T("yes")) ||
4470                                  !tstrcmp(igcase, T("1")))
4471                                 init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE;
4472                         else {
4473                                 fprintf(stderr,
4474                                         "WARNING: Ignoring unknown setting of "
4475                                         "WIMLIB_IMAGEX_IGNORE_CASE\n");
4476                         }
4477                 }
4478         }
4479
4480         /* Allow being invoked as wimCOMMAND (e.g. wimapply).  */
4481         cmd = CMD_NONE;
4482         if (!tstrncmp(invocation_name, T("wim"), 3) &&
4483             tstrcmp(invocation_name, T("wimlib-imagex"))) {
4484                 for (int i = 0; i < CMD_MAX; i++) {
4485                         if (!tstrcmp(invocation_name + 3,
4486                                      imagex_commands[i].name))
4487                         {
4488                                 invocation_cmd = i;
4489                                 cmd = i;
4490                                 break;
4491                         }
4492                 }
4493         }
4494
4495         /* Unless already known from the invocation name, determine which
4496          * command was specified.  */
4497         if (cmd == CMD_NONE) {
4498                 if (argc < 2) {
4499                         imagex_error(T("No command specified!\n"));
4500                         usage_all(stderr);
4501                         exit(2);
4502                 }
4503                 for (int i = 0; i < CMD_MAX; i++) {
4504                         if (!tstrcmp(argv[1], imagex_commands[i].name)) {
4505                                 cmd = i;
4506                                 break;
4507                         }
4508                 }
4509                 if (cmd != CMD_NONE) {
4510                         argc--;
4511                         argv++;
4512                 }
4513         }
4514
4515         /* Handle --help and --version.  --help can be either for the program as
4516          * a whole (cmd == CMD_NONE) or just for a specific command (cmd !=
4517          * CMD_NONE).  Note: help_or_version() will not return if a --help or
4518          * --version argument was found.  */
4519         help_or_version(argc, argv, cmd);
4520
4521         /* Bail if a valid command was not specified.  */
4522         if (cmd == CMD_NONE) {
4523                 imagex_error(T("Unrecognized command: `%"TS"'\n"), argv[1]);
4524                 usage_all(stderr);
4525                 exit(2);
4526         }
4527
4528         /* Enable warning and error messages in wimlib to be more user-friendly.
4529          * */
4530         wimlib_set_print_errors(true);
4531
4532         /* Initialize wimlib.  */
4533         ret = wimlib_global_init(init_flags);
4534         if (ret)
4535                 goto out_check_status;
4536
4537         /* Call the command handler function.  */
4538         ret = imagex_commands[cmd].func(argc, argv, cmd);
4539
4540         /* Check for error writing to standard output, especially since for some
4541          * commands, writing to standard output is part of the program's actual
4542          * behavior and not just for informational purposes.  */
4543         if (ferror(stdout) || fclose(stdout)) {
4544                 imagex_error_with_errno(T("error writing to standard output"));
4545                 if (ret == 0)
4546                         ret = -1;
4547         }
4548 out_check_status:
4549         /* Exit status (ret):  -1 indicates an error found by 'wimlib-imagex'
4550          * itself (not by wimlib).  0 indicates success.  > 0 indicates a wimlib
4551          * error code from which an error message can be printed.  */
4552         if (ret > 0) {
4553                 imagex_error(T("Exiting with error code %d:\n"
4554                                "       %"TS"."), ret,
4555                              wimlib_get_error_string(ret));
4556                 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
4557                         imagex_error_with_errno(T("errno"));
4558         }
4559         /* Make wimlib free any resources it's holding (although this is not
4560          * strictly necessary because the process is ending anyway).  */
4561         wimlib_global_cleanup();
4562         return ret;
4563 }