]> wimlib.net Git - wimlib/blob - programs/imagex.c
0d271e43eba4ea145a39d125b21c84bcd340c781
[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 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 #include "config.h"
26
27 #include "wimlib.h"
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <getopt.h>
32
33 #include <inttypes.h>
34 #include <libgen.h>
35 #include <limits.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41 #include <locale.h>
42
43 #ifdef HAVE_ALLOCA_H
44 #include <alloca.h>
45 #endif
46
47 #ifdef __WIN32__
48 #  include "imagex-win32.h"
49 #else
50 #  include <glob.h>
51 #endif
52
53 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
54
55 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (char**)argv, "", \
56                                 opts, NULL)) != -1)
57
58 enum imagex_op_type {
59         APPEND = 0,
60         APPLY,
61         CAPTURE,
62         DELETE,
63         DIR,
64         EXPORT,
65         INFO,
66         JOIN,
67         MOUNT,
68         MOUNTRW,
69         OPTIMIZE,
70         SPLIT,
71         UNMOUNT,
72 };
73
74 static void usage(int cmd_type);
75 static void usage_all();
76
77
78 static const char *usage_strings[] = {
79 [APPEND] =
80 IMAGEX_PROGNAME" append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
81 "                     [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
82 "                     [--verbose] [--dereference] [--config=FILE]\n"
83 "                     [--threads=NUM_THREADS] [--rebuild] [--unix-data]\n"
84 "                     [--source-list] [--noacls]\n",
85 [APPLY] =
86 IMAGEX_PROGNAME" apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
87 "                    (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
88 "                    [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n"
89 "                    [--noacls]\n",
90 [CAPTURE] =
91 IMAGEX_PROGNAME" capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
92 "                      [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
93 "                      [--flags EDITION_ID] [--verbose] [--dereference]\n"
94 "                      [--config=FILE] [--threads=NUM_THREADS] [--unix-data]\n"
95 "                      [--source-list] [--noacls]\n",
96 [DELETE] =
97 IMAGEX_PROGNAME" delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n",
98 [DIR] =
99 IMAGEX_PROGNAME" dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n",
100 [EXPORT] =
101 IMAGEX_PROGNAME" export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n"
102 "              DEST_WIMFILE [DEST_IMAGE_NAME] [DEST_IMAGE_DESCRIPTION]\n"
103 "              [--boot] [--check] [--compress=TYPE] [--ref=\"GLOB\"]\n"
104 "              [--threads=NUM_THREADS] [--rebuild]\n",
105 [INFO] =
106 IMAGEX_PROGNAME" info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
107 "                   [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
108 "                   [--xml] [--extract-xml FILE] [--metadata]\n",
109 [JOIN] =
110 IMAGEX_PROGNAME" join [--check] WIMFILE SPLIT_WIM...\n",
111 [MOUNT] =
112 IMAGEX_PROGNAME" mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n"
113 "                    [--check] [--debug] [--streams-interface=INTERFACE]\n"
114 "                    [--ref=\"GLOB\"] [--unix-data] [--allow-other]\n",
115 [MOUNTRW] =
116 IMAGEX_PROGNAME" mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
117 "                      [--check] [--debug] [--streams-interface=INTERFACE]\n"
118 "                      [--staging-dir=DIR] [--unix-data] [--allow-other]\n",
119 [OPTIMIZE] =
120 IMAGEX_PROGNAME" optimize WIMFILE [--check] [--recompress] [--compress=TYPE]\n",
121 [SPLIT] =
122 IMAGEX_PROGNAME" split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n",
123 [UNMOUNT] =
124 IMAGEX_PROGNAME" unmount DIRECTORY [--commit] [--check] [--rebuild]\n",
125 };
126
127 static const struct option apply_options[] = {
128         {"check",     no_argument,       NULL, 'c'},
129         {"hardlink",  no_argument,       NULL, 'h'},
130         {"symlink",   no_argument,       NULL, 's'},
131         {"verbose",   no_argument,       NULL, 'v'},
132         {"ref",       required_argument, NULL, 'r'},
133         {"unix-data", no_argument,       NULL, 'U'},
134         {"noacls",    no_argument,       NULL, 'N'},
135         {NULL, 0, NULL, 0},
136 };
137 static const struct option capture_or_append_options[] = {
138         {"boot",        no_argument,       NULL, 'b'},
139         {"check",       no_argument,       NULL, 'c'},
140         {"compress",    required_argument, NULL, 'x'},
141         {"config",      required_argument, NULL, 'C'},
142         {"dereference", no_argument,       NULL, 'L'},
143         {"flags",       required_argument, NULL, 'f'},
144         {"verbose",     no_argument,       NULL, 'v'},
145         {"threads",     required_argument, NULL, 't'},
146         {"rebuild",     no_argument,       NULL, 'R'},
147         {"unix-data",   no_argument,       NULL, 'U'},
148         {"source-list", no_argument,       NULL, 'S'},
149         {"noacls",      no_argument,       NULL, 'N'},
150         {NULL, 0, NULL, 0},
151 };
152 static const struct option delete_options[] = {
153         {"check", no_argument, NULL, 'c'},
154         {"soft",  no_argument, NULL, 's'},
155         {NULL, 0, NULL, 0},
156 };
157
158 static const struct option export_options[] = {
159         {"boot",       no_argument,       NULL, 'b'},
160         {"check",      no_argument,       NULL, 'c'},
161         {"compress",   required_argument, NULL, 'x'},
162         {"ref",        required_argument, NULL, 'r'},
163         {"threads",    required_argument, NULL, 't'},
164         {"rebuild",    no_argument,       NULL, 'R'},
165         {NULL, 0, NULL, 0},
166 };
167
168 static const struct option info_options[] = {
169         {"boot",         no_argument, NULL, 'b'},
170         {"check",        no_argument, NULL, 'c'},
171         {"extract-xml",  required_argument, NULL, 'X'},
172         {"header",       no_argument, NULL, 'h'},
173         {"lookup-table", no_argument, NULL, 'l'},
174         {"metadata",     no_argument, NULL, 'm'},
175         {"xml",          no_argument, NULL, 'x'},
176         {NULL, 0, NULL, 0},
177 };
178
179 static const struct option join_options[] = {
180         {"check", no_argument, NULL, 'c'},
181         {NULL, 0, NULL, 0},
182 };
183
184 static const struct option mount_options[] = {
185         {"check",             no_argument,       NULL, 'c'},
186         {"debug",             no_argument,       NULL, 'd'},
187         {"streams-interface", required_argument, NULL, 's'},
188         {"ref",               required_argument, NULL, 'r'},
189         {"staging-dir",       required_argument, NULL, 'D'},
190         {"unix-data",         no_argument,       NULL, 'U'},
191         {"allow-other",       no_argument,       NULL, 'A'},
192         {NULL, 0, NULL, 0},
193 };
194
195 static const struct option optimize_options[] = {
196         {"check",      no_argument, NULL, 'c'},
197         {"recompress", no_argument, NULL, 'r'},
198         {NULL, 0, NULL, 0},
199 };
200
201 static const struct option split_options[] = {
202         {"check", no_argument, NULL, 'c'},
203         {NULL, 0, NULL, 0},
204 };
205
206 static const struct option unmount_options[] = {
207         {"commit",  no_argument, NULL, 'c'},
208         {"check",   no_argument, NULL, 'C'},
209         {"rebuild", no_argument, NULL, 'R'},
210         {NULL, 0, NULL, 0},
211 };
212
213
214
215 /* Print formatted error message to stderr. */
216 static void imagex_error(const char *format, ...)
217 {
218         va_list va;
219         va_start(va, format);
220         fputs("ERROR: ", stderr);
221         vfprintf(stderr, format, va);
222         putc('\n', stderr);
223         va_end(va);
224 }
225
226 /* Print formatted error message to stderr. */
227 static void imagex_error_with_errno(const char *format, ...)
228 {
229         int errno_save = errno;
230         va_list va;
231         va_start(va, format);
232         fputs("ERROR: ", stderr);
233         vfprintf(stderr, format, va);
234         fprintf(stderr, ": %s\n", strerror(errno_save));
235         va_end(va);
236 }
237
238 static int verify_image_exists(int image, const char *image_name,
239                                const char *wim_name)
240 {
241         if (image == WIMLIB_NO_IMAGE) {
242                 imagex_error("\"%s\" is not a valid image in `%s'!\n"
243                              "       Please specify a 1-based image index or "
244                              "image name.\n"
245                              "       You may use `"IMAGEX_PROGNAME" info' to list the images "
246                              "contained in a WIM.",
247                              image_name, wim_name);
248                 return -1;
249         }
250         return 0;
251 }
252
253 static int verify_image_is_single(int image)
254 {
255         if (image == WIMLIB_ALL_IMAGES) {
256                 imagex_error("Cannot specify all images for this action!");
257                 return -1;
258         }
259         return 0;
260 }
261
262 static int verify_image_exists_and_is_single(int image, const char *image_name,
263                                              const char *wim_name)
264 {
265         int ret;
266         ret = verify_image_exists(image, image_name, wim_name);
267         if (ret == 0)
268                 ret = verify_image_is_single(image);
269         return ret;
270 }
271
272 /* Parse the argument to --compress */
273 static int get_compression_type(const char *optarg)
274 {
275         if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0)
276                 return WIMLIB_COMPRESSION_TYPE_LZX;
277         else if (strcasecmp(optarg, "fast") == 0 || strcasecmp(optarg, "xpress") == 0)
278                 return WIMLIB_COMPRESSION_TYPE_XPRESS;
279         else if (strcasecmp(optarg, "none") == 0)
280                 return WIMLIB_COMPRESSION_TYPE_NONE;
281         else {
282                 imagex_error("Invalid compression type `%s'! Must be "
283                              "\"maximum\", \"fast\", or \"none\".", optarg);
284                 return WIMLIB_COMPRESSION_TYPE_INVALID;
285         }
286 }
287
288 /* Returns the size of a file given its name, or -1 if the file does not exist
289  * or its size cannot be determined.  */
290 static off_t file_get_size(const char *filename)
291 {
292         struct stat st;
293         if (stat(filename, &st) == 0)
294                 return st.st_size;
295         else
296                 return (off_t)-1;
297 }
298
299 static const char *default_capture_config =
300 "[ExclusionList]\n"
301 "\\$ntfs.log\n"
302 "\\hiberfil.sys\n"
303 "\\pagefile.sys\n"
304 "\\System Volume Information\n"
305 "\\RECYCLER\n"
306 "\\Windows\\CSC\n"
307 "\n"
308 "[CompressionExclusionList]\n"
309 "*.mp3\n"
310 "*.zip\n"
311 "*.cab\n"
312 "\\WINDOWS\\inf\\*.pnf\n";
313
314 /* Read standard input until EOF and return the full contents in a malloc()ed
315  * buffer and the number of bytes of data in @len_ret.  Returns NULL on read
316  * error. */
317 static char *stdin_get_contents(size_t *len_ret)
318 {
319         /* stdin can, of course, be a pipe or other non-seekable file, so the
320          * total length of the data cannot be pre-determined */
321         char *buf = NULL;
322         size_t newlen = 1024;
323         size_t pos = 0;
324         size_t inc = 1024;
325         for (;;) {
326                 char *p = realloc(buf, newlen);
327                 size_t bytes_read, bytes_to_read;
328                 if (!p) {
329                         imagex_error("out of memory while reading stdin");
330                         break;
331                 }
332                 buf = p;
333                 bytes_to_read = newlen - pos;
334                 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
335                 pos += bytes_read;
336                 if (bytes_read != bytes_to_read) {
337                         if (feof(stdin)) {
338                                 *len_ret = pos;
339                                 return buf;
340                         } else {
341                                 imagex_error_with_errno("error reading stdin");
342                                 break;
343                         }
344                 }
345                 newlen += inc;
346                 inc *= 3;
347                 inc /= 2;
348         }
349         free(buf);
350         return NULL;
351 }
352
353 enum {
354         PARSE_FILENAME_SUCCESS = 0,
355         PARSE_FILENAME_FAILURE = 1,
356         PARSE_FILENAME_NONE = 2,
357 };
358
359 /*
360  * Parses a filename in the source list file format.  (See the man page for
361  * 'wimlib-imagex capture' for details on this format and the meaning.)
362  * Accepted formats for filenames are an unquoted string (whitespace-delimited),
363  * or a double or single-quoted string.
364  *
365  * @line_p:  Pointer to the pointer to the line of data.  Will be updated
366  *           to point past the filename iff the return value is
367  *           PARSE_FILENAME_SUCCESS.  If *len_p > 0, (*line_p)[*len_p - 1] must
368  *           be '\0'.
369  *
370  * @len_p:   @len_p initially stores the length of the line of data, which may
371  *           be 0, and it will be updated to the number of bytes remaining in
372  *           the line iff the return value is PARSE_FILENAME_SUCCESS.
373  *
374  * @fn_ret:  Iff the return value is PARSE_FILENAME_SUCCESS, a pointer to the
375  *           parsed filename will be returned here.
376  *
377  * Returns: PARSE_FILENAME_SUCCESS if a filename was successfully parsed; or
378  *          PARSE_FILENAME_FAILURE if the data was invalid due to a missing
379  *          closing quote; or PARSE_FILENAME_NONE if the line ended before the
380  *          beginning of a filename was found.
381  */
382 static int parse_filename(char **line_p, size_t *len_p, char **fn_ret)
383 {
384         size_t len = *len_p;
385         char *line = *line_p;
386         char *fn;
387         char quote_char;
388
389         /* Skip leading whitespace */
390         for (;;) {
391                 if (len == 0)
392                         return PARSE_FILENAME_NONE;
393                 if (!isspace(*line) && *line != '\0')
394                         break;
395                 line++;
396                 len--;
397         }
398         quote_char = *line;
399         if (quote_char == '"' || quote_char == '\'') {
400                 /* Quoted filename */
401                 line++;
402                 len--;
403                 fn = line;
404                 line = memchr(line, quote_char, len);
405                 if (!line) {
406                         imagex_error("Missing closing quote: %s", fn - 1);
407                         return PARSE_FILENAME_FAILURE;
408                 }
409         } else {
410                 /* Unquoted filename.  Go until whitespace.  Line is terminated
411                  * by '\0', so no need to check 'len'. */
412                 fn = line;
413                 do {
414                         line++;
415                 } while (!isspace(*line) && *line != '\0');
416         }
417         *line = '\0';
418         len -= line - fn;
419         *len_p = len;
420         *line_p = line;
421         *fn_ret = fn;
422         return PARSE_FILENAME_SUCCESS;
423 }
424
425 /* Parses a line of data (not an empty line or comment) in the source list file
426  * format.  (See the man page for 'wimlib-imagex capture' for details on this
427  * format and the meaning.)
428  *
429  * @line:  Line of data to be parsed.  line[len - 1] must be '\0', unless
430  *         len == 0.  The data in @line will be modified by this function call.
431  *
432  * @len:   Length of the line of data.
433  *
434  * @source:  On success, the capture source and target described by the line is
435  *           written into this destination.  Note that it will contain pointers
436  *           to data in the @line array.
437  *
438  * Returns true if the line was valid; false otherwise.  */
439 static bool
440 parse_source_list_line(char *line, size_t len,
441                        struct wimlib_capture_source *source)
442 {
443         /* SOURCE [DEST] */
444         int ret;
445         ret = parse_filename(&line, &len, &source->fs_source_path);
446         if (ret != PARSE_FILENAME_SUCCESS)
447                 return false;
448         ret = parse_filename(&line, &len, &source->wim_target_path);
449         if (ret == PARSE_FILENAME_NONE)
450                 source->wim_target_path = source->fs_source_path;
451         return ret != PARSE_FILENAME_FAILURE;
452 }
453
454 /* Returns %true if the given line of length @len > 0 is a comment or empty line
455  * in the source list file format. */
456 static bool is_comment_line(const char *line, size_t len)
457 {
458         for (;;) {
459                 if (*line == '#')
460                         return true;
461                 if (!isspace(*line) && *line != '\0')
462                         return false;
463                 ++line;
464                 --len;
465                 if (len == 0)
466                         return true;
467         }
468 }
469
470 /* Parses a file in the source list format.  (See the man page for
471  * 'wimlib-imagex capture' for details on this format and the meaning.)
472  *
473  * @source_list_contents:  Contents of the source list file.  Note that this
474  *                         buffer will be modified to save memory allocations,
475  *                         and cannot be freed until the returned array of
476  *                         wimlib_capture_source's has also been freed.
477  *
478  * @source_list_nbytes:    Number of bytes of data in the @source_list_contents
479  *                         buffer.
480  *
481  * @nsources_ret:          On success, the length of the returned array is
482  *                         returned here.
483  *
484  * Returns:   An array of `struct wimlib_capture_source's that can be passed to
485  * the wimlib_add_image_multisource() function to specify how a WIM image is to
486  * be created.  */
487 static struct wimlib_capture_source *
488 parse_source_list(char *source_list_contents, size_t source_list_nbytes,
489                   size_t *nsources_ret)
490 {
491         size_t nlines;
492         char *p;
493         struct wimlib_capture_source *sources;
494         size_t i, j;
495
496         nlines = 0;
497         for (i = 0; i < source_list_nbytes; i++)
498                 if (source_list_contents[i] == '\n')
499                         nlines++;
500         sources = calloc(nlines, sizeof(*sources));
501         if (!sources) {
502                 imagex_error("out of memory");
503                 return NULL;
504         }
505         p = source_list_contents;
506         j = 0;
507         for (i = 0; i < nlines; i++) {
508                 /* XXX: Could use rawmemchr() here instead, but it may not be
509                  * available on all platforms. */
510                 char *endp = memchr(p, '\n', source_list_nbytes);
511                 size_t len = endp - p + 1;
512                 *endp = '\0';
513                 if (!is_comment_line(p, len)) {
514                         if (!parse_source_list_line(p, len, &sources[j++])) {
515                                 free(sources);
516                                 return NULL;
517                         }
518                 }
519                 p = endp + 1;
520
521         }
522         *nsources_ret = j;
523         return sources;
524 }
525
526 /* Reads the contents of a file into memory. */
527 static char *file_get_contents(const char *filename, size_t *len_ret)
528 {
529         struct stat stbuf;
530         char *buf = NULL;
531         size_t len;
532         FILE *fp;
533
534         if (stat(filename, &stbuf) != 0) {
535                 imagex_error_with_errno("Failed to stat the file `%s'", filename);
536                 goto out;
537         }
538         len = stbuf.st_size;
539
540         fp = fopen(filename, "rb");
541         if (!fp) {
542                 imagex_error_with_errno("Failed to open the file `%s'", filename);
543                 goto out;
544         }
545
546         buf = malloc(len);
547         if (!buf) {
548                 imagex_error("Failed to allocate buffer of %zu bytes to hold "
549                              "contents of file `%s'", len, filename);
550                 goto out_fclose;
551         }
552         if (fread(buf, 1, len, fp) != len) {
553                 imagex_error_with_errno("Failed to read %lu bytes from the "
554                                         "file `%s'", len, filename);
555                 goto out_free_buf;
556         }
557         *len_ret = len;
558         goto out_fclose;
559 out_free_buf:
560         free(buf);
561         buf = NULL;
562 out_fclose:
563         fclose(fp);
564 out:
565         return buf;
566 }
567
568 /* Return 0 if a path names a file to which the current user has write access;
569  * -1 otherwise (and print an error message). */
570 static int file_writable(const char *path)
571 {
572         int ret;
573         ret = access(path, W_OK);
574         if (ret != 0)
575                 imagex_error_with_errno("Can't modify `%s'", path);
576         return ret;
577 }
578
579 #define TO_PERCENT(numerator, denominator) \
580         (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
581
582 /* Given an enumerated value for WIM compression type, return a descriptive
583  * string. */
584 static const char *get_data_type(int ctype)
585 {
586         switch (ctype) {
587         case WIMLIB_COMPRESSION_TYPE_NONE:
588                 return "uncompressed";
589         case WIMLIB_COMPRESSION_TYPE_LZX:
590                 return "LZX-compressed";
591         case WIMLIB_COMPRESSION_TYPE_XPRESS:
592                 return "XPRESS-compressed";
593         }
594         return NULL;
595 }
596
597 /* Progress callback function passed to various wimlib functions. */
598 static int imagex_progress_func(enum wimlib_progress_msg msg,
599                                 const union wimlib_progress_info *info)
600 {
601         unsigned percent_done;
602         switch (msg) {
603         case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
604                 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
605                                           info->write_streams.total_bytes);
606                 if (info->write_streams.completed_streams == 0) {
607                         const char *data_type;
608
609                         data_type = get_data_type(info->write_streams.compression_type);
610                         printf("Writing %s data using %u thread%s\n",
611                                data_type, info->write_streams.num_threads,
612                                (info->write_streams.num_threads == 1) ? "" : "s");
613                 }
614                 printf("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) "
615                        "written (%u%% done)",
616                        info->write_streams.completed_bytes >> 20,
617                        info->write_streams.total_bytes >> 20,
618                        percent_done);
619                 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
620                         putchar('\n');
621                 break;
622         case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
623                 printf("Scanning `%s'", info->scan.source);
624                 if (*info->scan.wim_target_path) {
625                         printf(" (loading as WIM path: `/%s')...\n",
626                                info->scan.wim_target_path);
627                 } else {
628                         printf(" (loading as root of WIM image)...\n");
629                 }
630                 break;
631         case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
632                 if (info->scan.excluded)
633                         printf("Excluding `%s' from capture\n", info->scan.cur_path);
634                 else
635                         printf("Scanning `%s'\n", info->scan.cur_path);
636                 break;
637         /*case WIMLIB_PROGRESS_MSG_SCAN_END:*/
638                 /*break;*/
639         case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
640                 percent_done = TO_PERCENT(info->integrity.completed_bytes,
641                                           info->integrity.total_bytes);
642                 printf("\rVerifying integrity of `%s': %"PRIu64" MiB "
643                        "of %"PRIu64" MiB (%u%%) done",
644                        info->integrity.filename,
645                        info->integrity.completed_bytes >> 20,
646                        info->integrity.total_bytes >> 20,
647                        percent_done);
648                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
649                         putchar('\n');
650                 break;
651         case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
652                 percent_done = TO_PERCENT(info->integrity.completed_bytes,
653                                           info->integrity.total_bytes);
654                 printf("\rCalculating integrity table for WIM: %"PRIu64" MiB "
655                        "of %"PRIu64" MiB (%u%%) done",
656                        info->integrity.completed_bytes >> 20,
657                        info->integrity.total_bytes >> 20,
658                        percent_done);
659                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
660                         putchar('\n');
661                 break;
662         case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
663                 printf("Applying image %d (%s) from `%s' to %s `%s'\n",
664                        info->extract.image,
665                        info->extract.image_name,
666                        info->extract.wimfile_name,
667                        ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
668                                 "NTFS volume" : "directory"),
669                        info->extract.target);
670                 break;
671         /*case WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN:*/
672                 /*printf("Applying directory structure to %s\n",*/
673                        /*info->extract.target);*/
674                 /*break;*/
675         case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
676                 percent_done = TO_PERCENT(info->extract.completed_bytes,
677                                           info->extract.total_bytes);
678                 printf("\rExtracting files: "
679                        "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done",
680                        info->extract.completed_bytes >> 20,
681                        info->extract.total_bytes >> 20,
682                        percent_done);
683                 if (info->extract.completed_bytes >= info->extract.total_bytes)
684                         putchar('\n');
685                 break;
686         case WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY:
687                 puts(info->extract.cur_path);
688                 break;
689         case WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS:
690                 printf("Setting timestamps on all extracted files...\n");
691                 break;
692         case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END:
693                 if (info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
694                         printf("Unmounting NTFS volume `%s'...\n",
695                                info->extract.target);
696                 }
697                 break;
698         case WIMLIB_PROGRESS_MSG_JOIN_STREAMS:
699                 percent_done = TO_PERCENT(info->join.completed_bytes,
700                                           info->join.total_bytes);
701                 printf("Writing resources from part %u of %u: "
702                        "%"PRIu64 " MiB of %"PRIu64" MiB (%u%%) written\n",
703                        (info->join.completed_parts == info->join.total_parts) ?
704                                 info->join.completed_parts : info->join.completed_parts + 1,
705                        info->join.total_parts,
706                        info->join.completed_bytes >> 20,
707                        info->join.total_bytes >> 20,
708                        percent_done);
709                 break;
710         case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
711                 percent_done = TO_PERCENT(info->split.completed_bytes,
712                                           info->split.total_bytes);
713                 printf("Writing `%s': %"PRIu64" MiB of %"PRIu64" MiB (%u%%) written\n",
714                        info->split.part_name,
715                        info->split.completed_bytes >> 20,
716                        info->split.total_bytes >> 20,
717                        percent_done);
718                 break;
719         case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
720                 if (info->split.completed_bytes == info->split.total_bytes) {
721                         printf("Finished writing %u split WIM parts\n",
722                                info->split.cur_part_number);
723                 }
724                 break;
725         default:
726                 break;
727         }
728         fflush(stdout);
729         return 0;
730 }
731
732 /* Open all the split WIM parts that correspond to a file glob.
733  *
734  * @first_part specifies the first part of the split WIM and it may be either
735  * included or omitted from the glob. */
736 static int open_swms_from_glob(const char *swm_glob,
737                                const char *first_part,
738                                int open_flags,
739                                WIMStruct ***additional_swms_ret,
740                                unsigned *num_additional_swms_ret)
741 {
742         unsigned num_additional_swms = 0;
743         WIMStruct **additional_swms = NULL;
744         glob_t globbuf;
745         int ret;
746
747         /* Warning: glob() is replaced in Windows native builds */
748         ret = glob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
749         if (ret != 0) {
750                 if (ret == GLOB_NOMATCH) {
751                         imagex_error("Found no files for glob \"%s\"",
752                                      swm_glob);
753                 } else {
754                         imagex_error_with_errno("Failed to process glob "
755                                                 "\"%s\"", swm_glob);
756                 }
757                 ret = -1;
758                 goto out;
759         }
760         num_additional_swms = globbuf.gl_pathc;
761         additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0]));
762         if (!additional_swms) {
763                 imagex_error("Out of memory");
764                 ret = -1;
765                 goto out_globfree;
766         }
767         unsigned offset = 0;
768         for (unsigned i = 0; i < num_additional_swms; i++) {
769                 if (strcmp(globbuf.gl_pathv[i], first_part) == 0) {
770                         offset++;
771                         continue;
772                 }
773                 ret = wimlib_open_wim(globbuf.gl_pathv[i],
774                                       open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK,
775                                       &additional_swms[i - offset],
776                                       imagex_progress_func);
777                 if (ret != 0)
778                         goto out_close_swms;
779         }
780         *additional_swms_ret = additional_swms;
781         *num_additional_swms_ret = num_additional_swms - offset;
782         ret = 0;
783         goto out_globfree;
784 out_close_swms:
785         for (unsigned i = 0; i < num_additional_swms; i++)
786                 wimlib_free(additional_swms[i]);
787         free(additional_swms);
788 out_globfree:
789         globfree(&globbuf);
790 out:
791         return ret;
792 }
793
794
795 static unsigned parse_num_threads(const char *optarg)
796 {
797         char *tmp;
798         unsigned nthreads = strtoul(optarg, &tmp, 10);
799         if (nthreads == UINT_MAX || *tmp || tmp == optarg) {
800                 imagex_error("Number of threads must be a non-negative integer!");
801                 return UINT_MAX;
802         } else {
803                 return nthreads;
804         }
805 }
806
807
808 /* Apply one image, or all images, from a WIM file into a directory, OR apply
809  * one image from a WIM file to a NTFS volume. */
810 static int imagex_apply(int argc, char **argv)
811 {
812         int c;
813         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
814         int image;
815         int num_images;
816         WIMStruct *w;
817         int ret;
818         const char *wimfile;
819         const char *target;
820         const char *image_num_or_name;
821         int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
822
823         const char *swm_glob = NULL;
824         WIMStruct **additional_swms = NULL;
825         unsigned num_additional_swms = 0;
826
827         for_opt(c, apply_options) {
828                 switch (c) {
829                 case 'c':
830                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
831                         break;
832                 case 'h':
833                         extract_flags |= WIMLIB_EXTRACT_FLAG_HARDLINK;
834                         break;
835                 case 's':
836                         extract_flags |= WIMLIB_EXTRACT_FLAG_SYMLINK;
837                         break;
838                 case 'v':
839                         extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE;
840                         break;
841                 case 'r':
842                         swm_glob = optarg;
843                         break;
844                 case 'U':
845                         extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
846                         break;
847                 case 'N':
848                         extract_flags |= WIMLIB_EXTRACT_FLAG_NOACLS;
849                         break;
850                 default:
851                         usage(APPLY);
852                         return -1;
853                 }
854         }
855         argc -= optind;
856         argv += optind;
857         if (argc != 2 && argc != 3) {
858                 usage(APPLY);
859                 return -1;
860         }
861
862         wimfile = argv[0];
863         if (argc == 2) {
864                 image_num_or_name = "1";
865                 target = argv[1];
866         } else {
867                 image_num_or_name = argv[1];
868                 target = argv[2];
869         }
870
871         ret = wimlib_open_wim(wimfile, open_flags, &w, imagex_progress_func);
872         if (ret != 0)
873                 return ret;
874
875         image = wimlib_resolve_image(w, image_num_or_name);
876         ret = verify_image_exists(image, image_num_or_name, wimfile);
877         if (ret != 0)
878                 goto out;
879
880         num_images = wimlib_get_num_images(w);
881         if (argc == 2 && num_images != 1) {
882                 imagex_error("`%s' contains %d images; Please select one "
883                              "(or all)", wimfile, num_images);
884                 usage(APPLY);
885                 ret = -1;
886                 goto out;
887         }
888
889         if (swm_glob) {
890                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
891                                           &additional_swms,
892                                           &num_additional_swms);
893                 if (ret != 0)
894                         goto out;
895         }
896
897         struct stat stbuf;
898
899         ret = stat(target, &stbuf);
900         if (ret == 0) {
901                 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
902                         extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
903         } else {
904                 if (errno != ENOENT) {
905                         imagex_error_with_errno("Failed to stat `%s'", target);
906                         ret = -1;
907                         goto out;
908                 }
909         }
910
911 #ifdef __WIN32__
912         win32_acquire_restore_privileges();
913 #endif
914         ret = wimlib_extract_image(w, image, target, extract_flags,
915                                    additional_swms, num_additional_swms,
916                                    imagex_progress_func);
917         if (ret == 0)
918                 printf("Done applying WIM image.\n");
919 #ifdef __WIN32__
920         win32_release_restore_privileges();
921 #endif
922 out:
923         wimlib_free(w);
924         if (additional_swms) {
925                 for (unsigned i = 0; i < num_additional_swms; i++)
926                         wimlib_free(additional_swms[i]);
927                 free(additional_swms);
928         }
929         return ret;
930 }
931
932 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
933  * directory trees.  'wimlib-imagex capture': create a new WIM file containing
934  * the desired image.  'wimlib-imagex append': add a new image to an existing
935  * WIM file. */
936 static int imagex_capture_or_append(int argc, char **argv)
937 {
938         int c;
939         int open_flags = 0;
940         int add_image_flags = 0;
941         int write_flags = 0;
942         int compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
943         const char *wimfile;
944         const char *name;
945         const char *desc;
946         const char *flags_element = NULL;
947         WIMStruct *w = NULL;
948         int ret;
949         int cur_image;
950         int cmd = strcmp(argv[0], "append") ? CAPTURE : APPEND;
951         unsigned num_threads = 0;
952
953         char *source;
954         size_t source_name_len;
955         char *source_copy;
956
957         const char *config_file = NULL;
958         char *config_str = NULL;
959         size_t config_len;
960
961         bool source_list = false;
962         size_t source_list_nbytes;
963         char *source_list_contents = NULL;
964         bool capture_sources_malloced = false;
965         struct wimlib_capture_source *capture_sources;
966         size_t num_sources;
967
968         for_opt(c, capture_or_append_options) {
969                 switch (c) {
970                 case 'b':
971                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
972                         break;
973                 case 'c':
974                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
975                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
976                         break;
977                 case 'C':
978                         config_file = optarg;
979                         break;
980                 case 'x':
981                         compression_type = get_compression_type(optarg);
982                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
983                                 return -1;
984                         break;
985                 case 'f':
986                         flags_element = optarg;
987                         break;
988                 case 'L':
989                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
990                         break;
991                 case 'v':
992                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
993                         break;
994                 case 't':
995                         num_threads = parse_num_threads(optarg);
996                         if (num_threads == UINT_MAX)
997                                 return -1;
998                         break;
999                 case 'R':
1000                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1001                         break;
1002                 case 'U':
1003                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
1004                         break;
1005                 case 'S':
1006                         source_list = true;
1007                         break;
1008                 case 'N':
1009                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS;
1010                         break;
1011                 default:
1012                         usage(cmd);
1013                         return -1;
1014                 }
1015         }
1016         argc -= optind;
1017         argv += optind;
1018
1019         if (argc < 2 || argc > 4) {
1020                 usage(cmd);
1021                 return -1;
1022         }
1023
1024         source = argv[0];
1025         wimfile = argv[1];
1026
1027         if (argc >= 3) {
1028                 name = argv[2];
1029         } else {
1030                 /* Set default name to SOURCE argument, omitting any directory
1031                  * prefixes and trailing slashes.  This requires making a copy
1032                  * of @source. */
1033                 source_name_len = strlen(source);
1034                 source_copy = alloca(source_name_len + 1);
1035                 name = basename(strcpy(source_copy, source));
1036         }
1037         /* Image description defaults to NULL if not given. */
1038         desc = (argc >= 4) ? argv[3] : NULL;
1039
1040         if (source_list) {
1041                 /* Set up capture sources in source list mode */
1042                 if (source[0] == '-' && source[1] == '\0') {
1043                         source_list_contents = stdin_get_contents(&source_list_nbytes);
1044                 } else {
1045                         source_list_contents = file_get_contents(source,
1046                                                                  &source_list_nbytes);
1047                 }
1048                 if (!source_list_contents)
1049                         return -1;
1050
1051                 capture_sources = parse_source_list(source_list_contents,
1052                                                     source_list_nbytes,
1053                                                     &num_sources);
1054                 if (!capture_sources) {
1055                         ret = -1;
1056                         goto out;
1057                 }
1058                 capture_sources_malloced = true;
1059         } else {
1060                 /* Set up capture source in non-source-list mode (could be
1061                  * either "normal" mode or "NTFS mode"--- see the man page). */
1062                 capture_sources = alloca(sizeof(struct wimlib_capture_source));
1063                 capture_sources[0].fs_source_path = source;
1064                 capture_sources[0].wim_target_path = NULL;
1065                 capture_sources[0].reserved = 0;
1066                 num_sources = 1;
1067         }
1068
1069         if (config_file) {
1070                 config_str = file_get_contents(config_file, &config_len);
1071                 if (!config_str) {
1072                         ret = -1;
1073                         goto out;
1074                 }
1075         }
1076
1077         if (cmd == APPEND)
1078                 ret = wimlib_open_wim(wimfile, open_flags, &w,
1079                                       imagex_progress_func);
1080         else
1081                 ret = wimlib_create_new_wim(compression_type, &w);
1082         if (ret != 0)
1083                 goto out;
1084
1085         if (!source_list) {
1086                 struct stat stbuf;
1087                 ret = stat(source, &stbuf);
1088                 if (ret == 0) {
1089                         if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
1090                                 printf("Capturing WIM image from NTFS filesystem on `%s'\n",
1091                                        source);
1092                                 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
1093                         }
1094                 } else {
1095                         if (errno != ENOENT) {
1096                                 imagex_error_with_errno("Failed to stat `%s'", source);
1097                                 ret = -1;
1098                                 goto out;
1099                         }
1100                 }
1101         }
1102 #ifdef __WIN32__
1103         win32_acquire_capture_privileges();
1104 #endif
1105
1106         ret = wimlib_add_image_multisource(w, capture_sources,
1107                                            num_sources, name,
1108                                            (config_str ? config_str :
1109                                                 default_capture_config),
1110                                            (config_str ? config_len :
1111                                                 strlen(default_capture_config)),
1112                                            add_image_flags,
1113                                            imagex_progress_func);
1114         if (ret != 0)
1115                 goto out_release_privs;
1116         cur_image = wimlib_get_num_images(w);
1117         if (desc) {
1118                 ret = wimlib_set_image_descripton(w, cur_image, desc);
1119                 if (ret != 0)
1120                         goto out_release_privs;
1121         }
1122         if (flags_element) {
1123                 ret = wimlib_set_image_flags(w, cur_image, flags_element);
1124                 if (ret != 0)
1125                         goto out_release_privs;
1126         }
1127         if (cmd == APPEND) {
1128                 ret = wimlib_overwrite(w, write_flags, num_threads,
1129                                        imagex_progress_func);
1130         } else {
1131                 ret = wimlib_write(w, wimfile, WIMLIB_ALL_IMAGES, write_flags,
1132                                    num_threads, imagex_progress_func);
1133         }
1134         if (ret == WIMLIB_ERR_REOPEN)
1135                 ret = 0;
1136         if (ret != 0)
1137                 imagex_error("Failed to write the WIM file `%s'", wimfile);
1138 out_release_privs:
1139 #ifdef __WIN32__
1140         win32_release_capture_privileges();
1141 #endif
1142 out:
1143         wimlib_free(w);
1144         free(config_str);
1145         free(source_list_contents);
1146         if (capture_sources_malloced)
1147                 free(capture_sources);
1148         return ret;
1149 }
1150
1151 /* Remove image(s) from a WIM. */
1152 static int imagex_delete(int argc, char **argv)
1153 {
1154         int c;
1155         int open_flags = 0;
1156         int write_flags = 0;
1157         const char *wimfile;
1158         const char *image_num_or_name;
1159         WIMStruct *w;
1160         int image;
1161         int ret;
1162
1163         for_opt(c, delete_options) {
1164                 switch (c) {
1165                 case 'c':
1166                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1167                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1168                         break;
1169                 case 's':
1170                         write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
1171                         break;
1172                 default:
1173                         usage(DELETE);
1174                         return -1;
1175                 }
1176         }
1177         argc -= optind;
1178         argv += optind;
1179
1180         if (argc != 2) {
1181                 if (argc < 1)
1182                         imagex_error("Must specify a WIM file");
1183                 if (argc < 2)
1184                         imagex_error("Must specify an image");
1185                 usage(DELETE);
1186                 return -1;
1187         }
1188         wimfile = argv[0];
1189         image_num_or_name = argv[1];
1190
1191         ret = file_writable(wimfile);
1192         if (ret != 0)
1193                 return ret;
1194
1195         ret = wimlib_open_wim(wimfile, open_flags, &w,
1196                               imagex_progress_func);
1197         if (ret != 0)
1198                 return ret;
1199
1200         image = wimlib_resolve_image(w, image_num_or_name);
1201
1202         ret = verify_image_exists(image, image_num_or_name, wimfile);
1203         if (ret != 0)
1204                 goto out;
1205
1206         ret = wimlib_delete_image(w, image);
1207         if (ret != 0) {
1208                 imagex_error("Failed to delete image from `%s'", wimfile);
1209                 goto out;
1210         }
1211
1212         ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func);
1213         if (ret == WIMLIB_ERR_REOPEN)
1214                 ret = 0;
1215         if (ret != 0) {
1216                 imagex_error("Failed to write the file `%s' with image "
1217                              "deleted", wimfile);
1218         }
1219 out:
1220         wimlib_free(w);
1221         return ret;
1222 }
1223
1224 /* Print the files contained in an image(s) in a WIM file. */
1225 static int imagex_dir(int argc, char **argv)
1226 {
1227         const char *wimfile;
1228         WIMStruct *w;
1229         int image;
1230         int ret;
1231         int num_images;
1232
1233         if (argc < 2) {
1234                 imagex_error("Must specify a WIM file");
1235                 usage(DIR);
1236                 return -1;
1237         }
1238         if (argc > 3) {
1239                 imagex_error("Too many arguments");
1240                 usage(DIR);
1241                 return -1;
1242         }
1243
1244         wimfile = argv[1];
1245         ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w,
1246                               imagex_progress_func);
1247         if (ret != 0)
1248                 return ret;
1249
1250         if (argc == 3) {
1251                 image = wimlib_resolve_image(w, argv[2]);
1252                 ret = verify_image_exists(image, argv[2], wimfile);
1253                 if (ret != 0)
1254                         goto out;
1255         } else {
1256                 /* Image was not specified.  If the WIM only contains one image,
1257                  * choose that one; otherwise, print an error. */
1258                 num_images = wimlib_get_num_images(w);
1259                 if (num_images != 1) {
1260                         imagex_error("The file `%s' contains %d images; Please "
1261                                      "select one.", wimfile, num_images);
1262                         usage(DIR);
1263                         ret = -1;
1264                         goto out;
1265                 }
1266                 image = 1;
1267         }
1268
1269         ret = wimlib_print_files(w, image);
1270 out:
1271         wimlib_free(w);
1272         return ret;
1273 }
1274
1275 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
1276  * WIM file. */
1277 static int imagex_export(int argc, char **argv)
1278 {
1279         int c;
1280         int open_flags = 0;
1281         int export_flags = 0;
1282         int write_flags = 0;
1283         int compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
1284         bool compression_type_specified = false;
1285         const char *src_wimfile;
1286         const char *src_image_num_or_name;
1287         const char *dest_wimfile;
1288         const char *dest_name;
1289         const char *dest_desc;
1290         WIMStruct *src_w = NULL;
1291         WIMStruct *dest_w = NULL;
1292         int ret;
1293         int image;
1294         struct stat stbuf;
1295         bool wim_is_new;
1296         const char *swm_glob = NULL;
1297         WIMStruct **additional_swms = NULL;
1298         unsigned num_additional_swms = 0;
1299         unsigned num_threads = 0;
1300
1301         for_opt(c, export_options) {
1302                 switch (c) {
1303                 case 'b':
1304                         export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
1305                         break;
1306                 case 'c':
1307                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1308                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1309                         break;
1310                 case 'x':
1311                         compression_type = get_compression_type(optarg);
1312                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1313                                 return -1;
1314                         compression_type_specified = true;
1315                         break;
1316                 case 'r':
1317                         swm_glob = optarg;
1318                         break;
1319                 case 't':
1320                         num_threads = parse_num_threads(optarg);
1321                         if (num_threads == UINT_MAX)
1322                                 return -1;
1323                         break;
1324                 case 'R':
1325                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1326                         break;
1327                 default:
1328                         usage(EXPORT);
1329                         return -1;
1330                 }
1331         }
1332         argc -= optind;
1333         argv += optind;
1334         if (argc < 3 || argc > 5) {
1335                 usage(EXPORT);
1336                 return -1;
1337         }
1338         src_wimfile           = argv[0];
1339         src_image_num_or_name = argv[1];
1340         dest_wimfile          = argv[2];
1341         dest_name             = (argc >= 4) ? argv[3] : NULL;
1342         dest_desc             = (argc >= 5) ? argv[4] : NULL;
1343         ret = wimlib_open_wim(src_wimfile,
1344                               open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w,
1345                               imagex_progress_func);
1346         if (ret != 0)
1347                 return ret;
1348
1349         /* Determine if the destination is an existing file or not.
1350          * If so, we try to append the exported image(s) to it; otherwise, we
1351          * create a new WIM containing the exported image(s). */
1352         if (stat(dest_wimfile, &stbuf) == 0) {
1353                 int dest_ctype;
1354
1355                 wim_is_new = false;
1356                 /* Destination file exists. */
1357
1358                 if (!S_ISREG(stbuf.st_mode)) {
1359                         imagex_error("`%s' is not a regular file",
1360                                      dest_wimfile);
1361                         ret = -1;
1362                         goto out;
1363                 }
1364                 ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w,
1365                                       imagex_progress_func);
1366                 if (ret != 0)
1367                         goto out;
1368
1369                 ret = file_writable(dest_wimfile);
1370                 if (ret != 0)
1371                         goto out;
1372
1373                 dest_ctype = wimlib_get_compression_type(dest_w);
1374                 if (compression_type_specified
1375                     && compression_type != dest_ctype)
1376                 {
1377                         imagex_error("Cannot specify a compression type that is "
1378                                      "not the same as that used in the "
1379                                      "destination WIM");
1380                         ret = -1;
1381                         goto out;
1382                 }
1383         } else {
1384                 wim_is_new = true;
1385                 /* dest_wimfile is not an existing file, so create a new WIM. */
1386                 if (!compression_type_specified)
1387                         compression_type = wimlib_get_compression_type(src_w);
1388                 if (errno == ENOENT) {
1389                         ret = wimlib_create_new_wim(compression_type, &dest_w);
1390                         if (ret != 0)
1391                                 goto out;
1392                 } else {
1393                         imagex_error_with_errno("Cannot stat file `%s'",
1394                                                 dest_wimfile);
1395                         ret = -1;
1396                         goto out;
1397                 }
1398         }
1399
1400         image = wimlib_resolve_image(src_w, src_image_num_or_name);
1401         ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
1402         if (ret != 0)
1403                 goto out;
1404
1405         if (swm_glob) {
1406                 ret = open_swms_from_glob(swm_glob, src_wimfile, open_flags,
1407                                           &additional_swms,
1408                                           &num_additional_swms);
1409                 if (ret != 0)
1410                         goto out;
1411         }
1412
1413         ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc,
1414                                   export_flags, additional_swms,
1415                                   num_additional_swms, imagex_progress_func);
1416         if (ret != 0)
1417                 goto out;
1418
1419
1420         if (wim_is_new)
1421                 ret = wimlib_write(dest_w, dest_wimfile, WIMLIB_ALL_IMAGES,
1422                                    write_flags, num_threads,
1423                                    imagex_progress_func);
1424         else
1425                 ret = wimlib_overwrite(dest_w, write_flags, num_threads,
1426                                        imagex_progress_func);
1427 out:
1428         if (ret == WIMLIB_ERR_REOPEN)
1429                 ret = 0;
1430         wimlib_free(src_w);
1431         wimlib_free(dest_w);
1432         if (additional_swms) {
1433                 for (unsigned i = 0; i < num_additional_swms; i++)
1434                         wimlib_free(additional_swms[i]);
1435                 free(additional_swms);
1436         }
1437         return ret;
1438 }
1439
1440 /* Prints information about a WIM file; also can mark an image as bootable,
1441  * change the name of an image, or change the description of an image. */
1442 static int imagex_info(int argc, char **argv)
1443 {
1444         int c;
1445         bool boot         = false;
1446         bool check        = false;
1447         bool header       = false;
1448         bool lookup_table = false;
1449         bool xml          = false;
1450         bool metadata     = false;
1451         bool short_header = true;
1452         const char *xml_out_file = NULL;
1453         const char *wimfile;
1454         const char *image_num_or_name = "all";
1455         const char *new_name = NULL;
1456         const char *new_desc = NULL;
1457         WIMStruct *w;
1458         FILE *fp;
1459         int image;
1460         int ret;
1461         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1462         int part_number;
1463         int total_parts;
1464         int num_images;
1465
1466         for_opt(c, info_options) {
1467                 switch (c) {
1468                 case 'b':
1469                         boot = true;
1470                         break;
1471                 case 'c':
1472                         check = true;
1473                         break;
1474                 case 'h':
1475                         header = true;
1476                         short_header = false;
1477                         break;
1478                 case 'l':
1479                         lookup_table = true;
1480                         short_header = false;
1481                         break;
1482                 case 'x':
1483                         xml = true;
1484                         short_header = false;
1485                         break;
1486                 case 'X':
1487                         xml_out_file = optarg;
1488                         short_header = false;
1489                         break;
1490                 case 'm':
1491                         metadata = true;
1492                         short_header = false;
1493                         break;
1494                 default:
1495                         usage(INFO);
1496                         return -1;
1497                 }
1498         }
1499
1500         argc -= optind;
1501         argv += optind;
1502         if (argc == 0 || argc > 4) {
1503                 usage(INFO);
1504                 return -1;
1505         }
1506         wimfile = argv[0];
1507         if (argc > 1) {
1508                 image_num_or_name = argv[1];
1509                 if (argc > 2) {
1510                         new_name = argv[2];
1511                         if (argc > 3) {
1512                                 new_desc = argv[3];
1513                         }
1514                 }
1515         }
1516
1517         if (check)
1518                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1519
1520         ret = wimlib_open_wim(wimfile, open_flags, &w,
1521                               imagex_progress_func);
1522         if (ret != 0)
1523                 return ret;
1524
1525         part_number = wimlib_get_part_number(w, &total_parts);
1526
1527         image = wimlib_resolve_image(w, image_num_or_name);
1528         if (image == WIMLIB_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) {
1529                 imagex_error("The image `%s' does not exist",
1530                              image_num_or_name);
1531                 if (boot)
1532                         imagex_error("If you would like to set the boot "
1533                                      "index to 0, specify image \"0\" with "
1534                                      "the --boot flag.");
1535                 ret = WIMLIB_ERR_INVALID_IMAGE;
1536                 goto out;
1537         }
1538
1539         num_images = wimlib_get_num_images(w);
1540
1541         if (num_images == 0) {
1542                 if (boot) {
1543                         imagex_error("--boot is meaningless on a WIM with no "
1544                                      "images");
1545                         ret = WIMLIB_ERR_INVALID_IMAGE;
1546                         goto out;
1547                 }
1548         }
1549
1550         if (image == WIMLIB_ALL_IMAGES && num_images > 1) {
1551                 if (boot) {
1552                         imagex_error("Cannot specify the --boot flag "
1553                                      "without specifying a specific "
1554                                      "image in a multi-image WIM");
1555                         ret = WIMLIB_ERR_INVALID_IMAGE;
1556                         goto out;
1557                 }
1558                 if (new_name) {
1559                         imagex_error("Cannot specify the NEW_NAME "
1560                                      "without specifying a specific "
1561                                      "image in a multi-image WIM");
1562                         ret = WIMLIB_ERR_INVALID_IMAGE;
1563                         goto out;
1564                 }
1565         }
1566
1567         /* Operations that print information are separated from operations that
1568          * recreate the WIM file. */
1569         if (!new_name && !boot) {
1570
1571                 /* Read-only operations */
1572
1573                 if (image == WIMLIB_NO_IMAGE) {
1574                         imagex_error("`%s' is not a valid image",
1575                                      image_num_or_name);
1576                         ret = WIMLIB_ERR_INVALID_IMAGE;
1577                         goto out;
1578                 }
1579
1580                 if (image == WIMLIB_ALL_IMAGES && short_header)
1581                         wimlib_print_wim_information(w);
1582
1583                 if (header)
1584                         wimlib_print_header(w);
1585
1586                 if (lookup_table) {
1587                         if (total_parts != 1) {
1588                                 printf("Warning: Only showing the lookup table "
1589                                        "for part %d of a %d-part WIM.\n",
1590                                        part_number, total_parts);
1591                         }
1592                         wimlib_print_lookup_table(w);
1593                 }
1594
1595                 if (xml) {
1596                         ret = wimlib_extract_xml_data(w, stdout);
1597                         if (ret != 0)
1598                                 goto out;
1599                 }
1600
1601                 if (xml_out_file) {
1602                         fp = fopen(xml_out_file, "wb");
1603                         if (!fp) {
1604                                 imagex_error_with_errno("Failed to open the "
1605                                                         "file `%s' for "
1606                                                         "writing ",
1607                                                         xml_out_file);
1608                                 ret = -1;
1609                                 goto out;
1610                         }
1611                         ret = wimlib_extract_xml_data(w, fp);
1612                         if (fclose(fp) != 0) {
1613                                 imagex_error("Failed to close the file `%s'",
1614                                              xml_out_file);
1615                                 ret = -1;
1616                         }
1617
1618                         if (ret != 0)
1619                                 goto out;
1620                 }
1621
1622                 if (short_header)
1623                         wimlib_print_available_images(w, image);
1624
1625                 if (metadata) {
1626                         ret = wimlib_print_metadata(w, image);
1627                         if (ret != 0)
1628                                 goto out;
1629                 }
1630         } else {
1631
1632                 /* Modification operations */
1633                 if (total_parts != 1) {
1634                         imagex_error("Modifying a split WIM is not supported.");
1635                         ret = -1;
1636                         goto out;
1637                 }
1638                 if (image == WIMLIB_ALL_IMAGES)
1639                         image = 1;
1640
1641                 if (image == WIMLIB_NO_IMAGE && new_name) {
1642                         imagex_error("Cannot specify new_name (`%s') when "
1643                                      "using image 0", new_name);
1644                         ret = -1;
1645                         goto out;
1646                 }
1647
1648                 if (boot) {
1649                         if (image == wimlib_get_boot_idx(w)) {
1650                                 printf("Image %d is already marked as "
1651                                        "bootable.\n", image);
1652                                 boot = false;
1653                         } else {
1654                                 printf("Marking image %d as bootable.\n",
1655                                        image);
1656                                 wimlib_set_boot_idx(w, image);
1657                         }
1658                 }
1659                 if (new_name) {
1660                         if (strcmp(wimlib_get_image_name(w, image),
1661                                                 new_name) == 0) {
1662                                 printf("Image %d is already named \"%s\".\n",
1663                                        image, new_name);
1664                                 new_name = NULL;
1665                         } else {
1666                                 printf("Changing the name of image %d to "
1667                                        "\"%s\".\n", image, new_name);
1668                                 ret = wimlib_set_image_name(w, image, new_name);
1669                                 if (ret != 0)
1670                                         goto out;
1671                         }
1672                 }
1673                 if (new_desc) {
1674                         const char *old_desc;
1675                         old_desc = wimlib_get_image_description(w, image);
1676                         if (old_desc && strcmp(old_desc, new_desc) == 0) {
1677                                 printf("The description of image %d is already "
1678                                        "\"%s\".\n", image, new_desc);
1679                                 new_desc = NULL;
1680                         } else {
1681                                 printf("Changing the description of image %d "
1682                                        "to \"%s\".\n", image, new_desc);
1683                                 ret = wimlib_set_image_descripton(w, image,
1684                                                                   new_desc);
1685                                 if (ret != 0)
1686                                         goto out;
1687                         }
1688                 }
1689
1690                 /* Only call wimlib_overwrite() if something actually needs to
1691                  * be changed. */
1692                 if (boot || new_name || new_desc ||
1693                     (check && !wimlib_has_integrity_table(w)))
1694                 {
1695                         int write_flags;
1696
1697                         ret = file_writable(wimfile);
1698                         if (ret != 0)
1699                                 return ret;
1700
1701                         if (check)
1702                                 write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1703                         else
1704                                 write_flags = 0;
1705
1706                         ret = wimlib_overwrite(w, write_flags, 1,
1707                                                imagex_progress_func);
1708                         if (ret == WIMLIB_ERR_REOPEN)
1709                                 ret = 0;
1710                 } else {
1711                         printf("The file `%s' was not modified because nothing "
1712                                "needed to be done.\n", wimfile);
1713                         ret = 0;
1714                 }
1715         }
1716 out:
1717         wimlib_free(w);
1718         return ret;
1719 }
1720
1721 /* Join split WIMs into one part WIM */
1722 static int imagex_join(int argc, char **argv)
1723 {
1724         int c;
1725         int swm_open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1726         int wim_write_flags = 0;
1727         const char *output_path;
1728
1729         for_opt(c, join_options) {
1730                 switch (c) {
1731                 case 'c':
1732                         swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1733                         wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1734                         break;
1735                 default:
1736                         goto err;
1737                 }
1738         }
1739         argc -= optind;
1740         argv += optind;
1741
1742         if (argc < 2) {
1743                 imagex_error("Must specify one or more split WIM (.swm) parts "
1744                              "to join");
1745                 goto err;
1746         }
1747         output_path = argv[0];
1748         return wimlib_join((const char **)++argv, --argc, output_path,
1749                            swm_open_flags, wim_write_flags,
1750                            imagex_progress_func);
1751 err:
1752         usage(JOIN);
1753         return -1;
1754 }
1755
1756 /* Mounts an image using a FUSE mount. */
1757 static int imagex_mount_rw_or_ro(int argc, char **argv)
1758 {
1759         int c;
1760         int mount_flags = 0;
1761         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1762         const char *wimfile;
1763         const char *dir;
1764         WIMStruct *w;
1765         int image;
1766         int num_images;
1767         int ret;
1768         const char *swm_glob = NULL;
1769         WIMStruct **additional_swms = NULL;
1770         unsigned num_additional_swms = 0;
1771         const char *staging_dir = NULL;
1772
1773         if (strcmp(argv[0], "mountrw") == 0)
1774                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
1775
1776         for_opt(c, mount_options) {
1777                 switch (c) {
1778                 case 'A':
1779                         mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
1780                         break;
1781                 case 'c':
1782                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1783                         break;
1784                 case 'd':
1785                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
1786                         break;
1787                 case 's':
1788                         if (strcasecmp(optarg, "none") == 0)
1789                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
1790                         else if (strcasecmp(optarg, "xattr") == 0)
1791                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
1792                         else if (strcasecmp(optarg, "windows") == 0)
1793                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
1794                         else {
1795                                 imagex_error("Unknown stream interface \"%s\"", optarg);
1796                                 goto mount_usage;
1797                         }
1798                         break;
1799                 case 'r':
1800                         swm_glob = optarg;
1801                         break;
1802                 case 'D':
1803                         staging_dir = optarg;
1804                         break;
1805                 case 'U':
1806                         mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
1807                         break;
1808                 default:
1809                         goto mount_usage;
1810                 }
1811         }
1812         argc -= optind;
1813         argv += optind;
1814         if (argc != 2 && argc != 3)
1815                 goto mount_usage;
1816
1817         wimfile = argv[0];
1818
1819         ret = wimlib_open_wim(wimfile, open_flags, &w,
1820                               imagex_progress_func);
1821         if (ret != 0)
1822                 return ret;
1823
1824         if (swm_glob) {
1825                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
1826                                           &additional_swms,
1827                                           &num_additional_swms);
1828                 if (ret != 0)
1829                         goto out;
1830         }
1831
1832         if (argc == 2) {
1833                 image = 1;
1834                 num_images = wimlib_get_num_images(w);
1835                 if (num_images != 1) {
1836                         imagex_error("The file `%s' contains %d images; Please "
1837                                      "select one.", wimfile, num_images);
1838                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
1839                                         ? MOUNTRW : MOUNT);
1840                         ret = -1;
1841                         goto out;
1842                 }
1843                 dir = argv[1];
1844         } else {
1845                 image = wimlib_resolve_image(w, argv[1]);
1846                 dir = argv[2];
1847                 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
1848                 if (ret != 0)
1849                         goto out;
1850         }
1851
1852         if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1853                 ret = file_writable(wimfile);
1854                 if (ret != 0)
1855                         goto out;
1856         }
1857
1858         ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms,
1859                                  num_additional_swms, staging_dir);
1860         if (ret != 0) {
1861                 imagex_error("Failed to mount image %d from `%s' on `%s'",
1862                              image, wimfile, dir);
1863
1864         }
1865 out:
1866         wimlib_free(w);
1867         if (additional_swms) {
1868                 for (unsigned i = 0; i < num_additional_swms; i++)
1869                         wimlib_free(additional_swms[i]);
1870                 free(additional_swms);
1871         }
1872         return ret;
1873 mount_usage:
1874         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
1875                         ? MOUNTRW : MOUNT);
1876         return -1;
1877 }
1878
1879 /* Rebuild a WIM file */
1880 static int imagex_optimize(int argc, char **argv)
1881 {
1882         int c;
1883         int open_flags = 0;
1884         int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
1885         int ret;
1886         WIMStruct *w;
1887         const char *wimfile;
1888         off_t old_size;
1889         off_t new_size;
1890
1891         for_opt(c, optimize_options) {
1892                 switch (c) {
1893                 case 'c':
1894                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1895                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1896                         break;
1897                 case 'r':
1898                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
1899                         break;
1900                 default:
1901                         usage(OPTIMIZE);
1902                         return -1;
1903                 }
1904         }
1905         argc -= optind;
1906         argv += optind;
1907
1908         if (argc != 1) {
1909                 usage(OPTIMIZE);
1910                 return -1;
1911         }
1912
1913         wimfile = argv[0];
1914
1915         ret = wimlib_open_wim(wimfile, open_flags, &w,
1916                               imagex_progress_func);
1917         if (ret != 0)
1918                 return ret;
1919
1920         old_size = file_get_size(argv[0]);
1921         printf("`%s' original size: ", wimfile);
1922         if (old_size == -1)
1923                 puts("Unknown");
1924         else
1925                 printf("%"PRIu64" KiB\n", old_size >> 10);
1926
1927         ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func);
1928
1929         if (ret == 0) {
1930                 new_size = file_get_size(argv[0]);
1931                 printf("`%s' optimized size: ", wimfile);
1932                 if (new_size == -1)
1933                         puts("Unknown");
1934                 else
1935                         printf("%"PRIu64" KiB\n", new_size >> 10);
1936
1937                 fputs("Space saved: ", stdout);
1938                 if (new_size != -1 && old_size != -1) {
1939                         printf("%lld KiB\n",
1940                                ((long long)old_size - (long long)new_size) >> 10);
1941                 } else {
1942                         puts("Unknown");
1943                 }
1944         }
1945
1946         wimlib_free(w);
1947         return ret;
1948 }
1949
1950 /* Split a WIM into a spanned set */
1951 static int imagex_split(int argc, char **argv)
1952 {
1953         int c;
1954         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1955         int write_flags = 0;
1956         unsigned long part_size;
1957         char *tmp;
1958         int ret;
1959         WIMStruct *w;
1960
1961         for_opt(c, split_options) {
1962                 switch (c) {
1963                 case 'c':
1964                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1965                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1966                         break;
1967                 default:
1968                         usage(SPLIT);
1969                         return -1;
1970                 }
1971         }
1972         argc -= optind;
1973         argv += optind;
1974
1975         if (argc != 3) {
1976                 usage(SPLIT);
1977                 return -1;
1978         }
1979         part_size = strtod(argv[2], &tmp) * (1 << 20);
1980         if (tmp == argv[2] || *tmp) {
1981                 imagex_error("Invalid part size \"%s\"", argv[2]);
1982                 imagex_error("The part size must be an integer or floating-point number of megabytes.");
1983                 return -1;
1984         }
1985         ret = wimlib_open_wim(argv[0], open_flags, &w, imagex_progress_func);
1986         if (ret != 0)
1987                 return ret;
1988         ret = wimlib_split(w, argv[1], part_size, write_flags, imagex_progress_func);
1989         wimlib_free(w);
1990         return ret;
1991 }
1992
1993 /* Unmounts a mounted WIM image. */
1994 static int imagex_unmount(int argc, char **argv)
1995 {
1996         int c;
1997         int unmount_flags = 0;
1998         int ret;
1999
2000         for_opt(c, unmount_options) {
2001                 switch (c) {
2002                 case 'c':
2003                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
2004                         break;
2005                 case 'C':
2006                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
2007                         break;
2008                 case 'R':
2009                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
2010                         break;
2011                 default:
2012                         usage(UNMOUNT);
2013                         return -1;
2014                 }
2015         }
2016         argc -= optind;
2017         argv += optind;
2018         if (argc != 1) {
2019                 usage(UNMOUNT);
2020                 return -1;
2021         }
2022
2023         ret = wimlib_unmount_image(argv[0], unmount_flags,
2024                                    imagex_progress_func);
2025         if (ret != 0)
2026                 imagex_error("Failed to unmount `%s'", argv[0]);
2027         return ret;
2028 }
2029
2030 struct imagex_command {
2031         const char *name;
2032         int (*func)(int , char **);
2033         int cmd;
2034 };
2035
2036
2037 #define for_imagex_command(p) for (p = &imagex_commands[0]; \
2038                 p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++)
2039
2040 static const struct imagex_command imagex_commands[] = {
2041         {"append",  imagex_capture_or_append, APPEND},
2042         {"apply",   imagex_apply,             APPLY},
2043         {"capture", imagex_capture_or_append, CAPTURE},
2044         {"delete",  imagex_delete,            DELETE},
2045         {"dir",     imagex_dir,               DIR},
2046         {"export",  imagex_export,            EXPORT},
2047         {"info",    imagex_info,              INFO},
2048         {"join",    imagex_join,              JOIN},
2049         {"mount",   imagex_mount_rw_or_ro,    MOUNT},
2050         {"mountrw", imagex_mount_rw_or_ro,    MOUNTRW},
2051         {"optimize",imagex_optimize,          OPTIMIZE},
2052         {"split",   imagex_split,             SPLIT},
2053         {"unmount", imagex_unmount,           UNMOUNT},
2054 };
2055
2056 static void version()
2057 {
2058         static const char *s =
2059         IMAGEX_PROGNAME " (" PACKAGE ") " PACKAGE_VERSION "\n"
2060         "Copyright (C) 2012, 2013 Eric Biggers\n"
2061         "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
2062         "This is free software: you are free to change and redistribute it.\n"
2063         "There is NO WARRANTY, to the extent permitted by law.\n"
2064         "\n"
2065         "Report bugs to "PACKAGE_BUGREPORT".\n";
2066         fputs(s, stdout);
2067 }
2068
2069
2070 static void help_or_version(int argc, char **argv)
2071 {
2072         int i;
2073         const char *p;
2074         const struct imagex_command *cmd;
2075
2076         for (i = 1; i < argc; i++) {
2077                 p = argv[i];
2078                 if (*p == '-')
2079                         p++;
2080                 else
2081                         continue;
2082                 if (*p == '-')
2083                         p++;
2084                 if (strcmp(p, "help") == 0) {
2085                         for_imagex_command(cmd) {
2086                                 if (strcmp(cmd->name, argv[1]) == 0) {
2087                                         usage(cmd->cmd);
2088                                         exit(0);
2089                                 }
2090                         }
2091                         usage_all();
2092                         exit(0);
2093                 }
2094                 if (strcmp(p, "version") == 0) {
2095                         version();
2096                         exit(0);
2097                 }
2098         }
2099 }
2100
2101
2102 static void usage(int cmd_type)
2103 {
2104         const struct imagex_command *cmd;
2105         printf("Usage: %s", usage_strings[cmd_type]);
2106         for_imagex_command(cmd) {
2107                 if (cmd->cmd == cmd_type)
2108                         printf("\nTry `man "IMAGEX_PROGNAME"-%s' for more details.\n",
2109                                cmd->name);
2110         }
2111 }
2112
2113 static void usage_all()
2114 {
2115         puts("IMAGEX: Usage:");
2116         for (int i = 0; i < ARRAY_LEN(usage_strings); i++)
2117                 printf("    %s", usage_strings[i]);
2118         static const char *extra =
2119 "    "IMAGEX_PROGNAME" --help\n"
2120 "    "IMAGEX_PROGNAME" --version\n"
2121 "\n"
2122 "    The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n"
2123 "\n"
2124 "    Try `man "IMAGEX_PROGNAME"' for more information.\n"
2125         ;
2126         fputs(extra, stdout);
2127 }
2128
2129 /* Entry point for wimlib's ImageX implementation */
2130 int main(int argc, char **argv)
2131 {
2132         const struct imagex_command *cmd;
2133         int ret;
2134
2135         setlocale(LC_ALL, "");
2136
2137         if (argc < 2) {
2138                 imagex_error("No command specified");
2139                 usage_all();
2140                 return 1;
2141         }
2142
2143         /* Handle --help and --version for all commands.  Note that this will
2144          * not return if either of these arguments are present. */
2145         help_or_version(argc, argv);
2146         argc--;
2147         argv++;
2148
2149         /* The user may like to see more informative error messages. */
2150         wimlib_set_print_errors(true);
2151
2152         /* Do any initializations that the library needs */
2153         ret = wimlib_global_init();
2154         if (ret)
2155                 goto out;
2156
2157         /* Search for the function to handle the ImageX subcommand. */
2158         for_imagex_command(cmd) {
2159                 if (strcmp(cmd->name, *argv) == 0) {
2160                         ret = cmd->func(argc, argv);
2161                         goto out_check_write_error;
2162                 }
2163         }
2164
2165         imagex_error("Unrecognized command: `%s'", argv[0]);
2166         usage_all();
2167         return 1;
2168 out_check_write_error:
2169         /* For 'wimlib-imagex info' and 'wimlib-imagex dir', data printed to
2170          * standard output is part of the program's actual behavior and not just
2171          * for informational purposes, so we should set a failure exit status if
2172          * there was a write error. */
2173         if (cmd == &imagex_commands[INFO] || cmd == &imagex_commands[DIR]) {
2174                 if (ferror(stdout) || fclose(stdout)) {
2175                         imagex_error_with_errno("output error");
2176                         if (ret == 0)
2177                                 ret = -1;
2178                 }
2179         }
2180 out:
2181         /* Exit status (ret):  -1 indicates an error found by 'wimlib-imagex'
2182          * outside of the wimlib library code.  0 indicates success.  > 0
2183          * indicates a wimlib error code from which an error message can be
2184          * printed. */
2185         if (ret > 0) {
2186                 imagex_error("Exiting with error code %d:\n"
2187                              "       %s.", ret,
2188                              wimlib_get_error_string(ret));
2189                 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
2190                         imagex_error_with_errno("errno");
2191         }
2192
2193         /* Make the library free any resources it's holding (not strictly
2194          * necessary because the process is ending anyway). */
2195         wimlib_global_cleanup();
2196         return ret;
2197 }