2d664084606ae10c8e2d09eb99e2e76ea820bafa
[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 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 "wimlib.h"
26 #include "config.h"
27 #include <getopt.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33
34 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
35
36 #define swap(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); \
37                                 a = __b; b = __a; })
38
39 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (char**)argv, "", \
40                                 opts, NULL)) != -1)
41
42 enum imagex_op_type {
43         APPEND,
44         APPLY,
45         CAPTURE,
46         DELETE,
47         DIR,
48         EXPORT,
49         INFO,
50         JOIN,
51         MOUNT,
52         MOUNTRW,
53         SPLIT,
54         UNMOUNT,
55 };
56
57 static const char *path_basename(const char *path)
58 {
59         const char *p = path;
60         while (*p)
61                 p++;
62         p--;
63
64         /* Trailing slashes. */
65         while ((p != path - 1) && *p == '/')
66                 p--;
67
68         while ((p != path - 1) && *p != '/')
69                 p--;
70
71         return p + 1;
72 }
73
74
75 static const char *usage_strings[] = {
76 [APPEND] = 
77 "    imagex append DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"] [--boot]\n"
78 "        [--check] [--config CONFIG_FILE] [--flags EDITIONID] [--verify]\n",
79 [APPLY] = 
80 "    imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all] DIRECTORY [--check]\n"
81 "        [--verify] [--hardlink] [--symlink] [--verbose]\n",
82 [CAPTURE] = 
83 "    imagex capture DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"]\n"
84 "        [--boot] [--check] [--compress[=TYPE]] [--config CONFIG_FILE]\n"
85 "        [--flags \"EditionID\"] [--norpfix] [--verify] [--verbose]\n",
86 [DELETE] = 
87 "    imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
88 [DIR] = 
89 "    imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | \"all\")\n",
90 [EXPORT] = 
91 "    imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n"
92 "        DEST_WIMFILE [\"DEST_IMAGE_NAME\"] [\"DEST_IMAGE_DESCRIPTION\"]\n"
93 "        [--boot] [--check] [--compress[=TYPE]]\n",
94 [INFO] = 
95 "    imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
96 "        [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
97 "        [--xml] [--extract-xml FILE] [--metadata]\n",
98 [JOIN] = 
99 "    imagex join [--check] WIMFILE SPLIT_WIM...\n",
100 [MOUNT] = 
101 "    imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n"
102 "        [--check] [--debug]\n",
103 [MOUNTRW] = 
104 "    imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
105 "        [--check] [--debug]\n",
106 [SPLIT] = 
107 "    imagex split WIMFILE SPLIT_WIMFILE PART_SIZE [--check]\n",
108 [UNMOUNT] = 
109 "    imagex unmount DIRECTORY [--commit] [--check]\n",
110 };
111
112 static const struct option common_options[] = {
113         {"help", 0, NULL, 'h'},
114         {"version", 0, NULL, 'v'},
115         {NULL, 0, NULL, 0},
116 };
117
118 static const struct option append_options[] = {
119         {"boot",   no_argument,       NULL, 'b'},
120         {"check",  no_argument,       NULL, 'c'},
121         {"config", required_argument, NULL, 'C'},
122         {"flags",    required_argument, NULL, 'f'},
123         {"verify", no_argument,       NULL, 'V'},
124         {NULL, 0, NULL, 0},
125 };
126 static const struct option apply_options[] = {
127         {"check",    no_argument,       NULL, 'c'},
128         {"verify",   no_argument,       NULL, 'V'},
129         {"hardlink", no_argument,       NULL, 'h'},
130         {"symlink",  no_argument,       NULL, 's'},
131         {"verbose",  no_argument,       NULL, 'v'},
132         {NULL, 0, NULL, 0},
133 };
134 static const struct option capture_options[] = {
135         {"boot",     no_argument,       NULL, 'b'},
136         {"check",    no_argument,       NULL, 'c'},
137         {"compress", optional_argument, NULL, 'x'},
138         {"config",   required_argument, NULL, 'C'},
139         {"flags",    required_argument, NULL, 'f'},
140         {"norpfix",  no_argument,       NULL, 'n'},
141         {"verify",   no_argument,       NULL, 'V'},
142         {"verbose",   no_argument,       NULL, 'v'},
143         {NULL, 0, NULL, 0},
144 };
145 static const struct option delete_options[] = {
146         {"check", no_argument, NULL, 'c'},
147         {NULL, 0, NULL, 0},
148 };
149
150 static const struct option export_options[] = {
151         {"boot",       no_argument, NULL, 'b'},
152         {"check",      no_argument , NULL, 'c'},
153         {"compress",   optional_argument, NULL, 'x'},
154         {NULL, 0, NULL, 0},
155 };
156
157 static const struct option info_options[] = {
158         {"boot",         no_argument, NULL, 'b'},
159         {"check",        no_argument, NULL, 'c'},
160         {"header",       no_argument, NULL, 'h'},
161         {"lookup-table", no_argument, NULL, 'l'},
162         {"xml",          no_argument, NULL, 'x'},
163         {"extract-xml",  required_argument, NULL, 'X'},
164         {"metadata",     no_argument, NULL, 'm'},
165         {NULL, 0, NULL, 0},
166 };
167
168 static const struct option join_options[] = {
169         {"check", no_argument, NULL, 'c'},
170         {NULL, 0, NULL, 0},
171 };
172
173 static const struct option mount_options[] = {
174         {"check", no_argument, NULL, 'c'},
175         {"debug", no_argument, NULL, 'd'},
176         {NULL, 0, NULL, 0},
177 };
178
179 static const struct option split_options[] = {
180         {"check", no_argument, NULL, 'c'},
181         {NULL, 0, NULL, 0},
182 };
183
184 static const struct option unmount_options[] = {
185         {"commit", no_argument, NULL, 'c'},
186         {"check", no_argument, NULL, 'C'},
187         {NULL, 0, NULL, 0},
188 };
189
190
191 /* Print formatted error message to stderr. */
192 static void imagex_error(const char *format, ...)
193 {
194         va_list va;
195         va_start(va, format);
196         fputs("ERROR: ", stderr);
197         vfprintf(stderr, format, va);
198         va_end(va);
199 }
200
201
202 static inline void version()
203 {
204         static const char *s = 
205         "imagex (" PACKAGE ") " PACKAGE_VERSION "\n"
206         "Copyright (C) 2012 Eric Biggers\n"
207         "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
208         "This is free software: you are free to change and redistribute it.\n"
209         "There is NO WARRANTY, to the extent permitted by law.\n"
210         "\n"
211         "Report bugs to "PACKAGE_BUGREPORT".\n";
212         fputs(s, stdout);
213 }
214
215 static inline void usage(int cmd)
216 {
217         puts("IMAGEX: Usage:");
218         fputs(usage_strings[cmd], stdout);
219 }
220
221 static void usage_all()
222 {
223         puts("IMAGEX: Usage:");
224         for (int i = 0; i < ARRAY_LEN(usage_strings); i++)
225                 fputs(usage_strings[i], stdout);
226         static const char *extra = 
227 "    imagex --help\n"
228 "    imagex --version\n"
229 "\n"
230 "    The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n"
231         ;
232         fputs(extra, stdout);
233 }
234
235 static int verify_image_exists(int image)
236 {
237         if (image == WIM_NO_IMAGE) {
238                 imagex_error("Not a valid image!\n");
239                 return WIMLIB_ERR_INVALID_IMAGE;
240         }
241         return 0;
242 }
243
244 static int verify_image_is_single(int image)
245 {
246         if (image == WIM_ALL_IMAGES) {
247                 imagex_error("Cannot specify all images for this action!\n");
248                 return WIMLIB_ERR_INVALID_IMAGE;
249         }
250         return 0;
251 }
252
253 static int verify_image_exists_and_is_single(int image)
254 {
255         int ret;
256         ret = verify_image_exists(image);
257         if (ret == 0)
258                 ret = verify_image_is_single(image);
259         return ret;
260 }
261
262 static int get_compression_type(const char *optarg)
263 {
264         if (!optarg)
265                 return WIM_COMPRESSION_TYPE_LZX;
266         if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0)
267                 return WIM_COMPRESSION_TYPE_LZX;
268         else if (strcasecmp(optarg, "fast") == 0 || strcasecmp(optarg, "xpress") == 0)
269                 return WIM_COMPRESSION_TYPE_XPRESS;
270         else if (strcasecmp(optarg, "none") == 0)
271                 return WIM_COMPRESSION_TYPE_NONE;
272         else {
273                 imagex_error("Invalid compression type `%s'! Must be "
274                                 "\"maximum\", \"fast\", or \"none\".\n", 
275                                 optarg);
276                 return WIM_COMPRESSION_TYPE_INVALID;
277         }
278 }
279
280 static int imagex_append(int argc, const char **argv)
281 {
282         int c;
283         const char *flags_element = NULL;
284         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
285         int add_image_flags = 0;
286         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
287         const char *dir;
288         const char *wimfile;
289         const char *name;
290         const char *desc;
291         WIMStruct *w;
292         int ret;
293
294         for_opt(c, append_options) {
295                 switch (c) {
296                 case 'b':
297                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
298                         break;
299                 case 'c':
300                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
301                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
302                         break;
303                 case 'C':
304                         /*config = optarg;*/
305                         break;
306                 case 'f':
307                         flags_element = optarg;
308                         break;
309                 case 'V':
310                         /* verify */
311                         break;
312                 default:
313                         usage(APPEND);
314                         return -1;
315                 }
316         }
317         argc -= optind;
318         argv += optind;
319         if (argc < 2 || argc > 4) {
320                 usage(APPEND);
321                 return -1;
322         }
323         dir     = argv[0];
324         wimfile = argv[1];
325         name    = (argc >= 3) ? argv[2] : path_basename(dir);
326         desc    = (argc >= 4) ? argv[3] : NULL;
327
328         ret = wimlib_open_wim(wimfile, open_flags, &w);
329         if (ret != 0)
330                 return ret;
331
332         ret = wimlib_add_image(w, dir, name, desc, 
333                                flags_element, add_image_flags);
334         if (ret != 0)
335                 goto done;
336         ret = wimlib_overwrite(w, write_flags);
337 done:
338         wimlib_free(w);
339         return ret;
340 }
341
342 /* Extract one image, or all images, from a WIM file into a directory. */
343 static int imagex_apply(int argc, const char **argv)
344 {
345         int c;
346         int link_type = WIM_LINK_TYPE_NONE;
347         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
348         bool verbose = false;
349         int image;
350         int num_images;
351         WIMStruct *w;
352         int ret;
353         const char *wimfile;
354         const char *dir;
355         const char *image_num_or_name;
356
357         for_opt(c, apply_options) {
358                 switch (c) {
359                 case 'c':
360                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
361                         break;
362                 case 'V':
363                         /* verify */
364                         break;
365                 case 'h':
366                         link_type = WIM_LINK_TYPE_HARD;
367                         break;
368                 case 's':
369                         link_type = WIM_LINK_TYPE_SYMBOLIC;
370                         break;
371                 case 'v':
372                         verbose = true;
373                         break;
374                 default:
375                         usage(APPLY);
376                         return -1;
377                 }
378         }
379         argc -= optind;
380         argv += optind;
381         if (argc != 2 && argc != 3) {
382                 usage(APPLY);
383                 return -1;
384         }
385
386         wimfile = argv[0];
387         if (argc == 2) {
388                 image_num_or_name =  "1";
389                 dir = argv[1];
390         } else {
391                 image_num_or_name = argv[1];
392                 dir = argv[2];
393         }
394
395         ret = wimlib_open_wim(wimfile, open_flags, &w);
396         if (ret != 0)
397                 return ret;
398
399         image = wimlib_resolve_image(w, image_num_or_name);
400         ret = verify_image_exists(image);
401         if (ret != 0)
402                 goto done;
403
404         num_images = wimlib_get_num_images(w);
405         if (argc == 2 && num_images != 1) {
406                 imagex_error("`%s' contains %d images; Please select one "
407                                 "(or all).\n", wimfile, num_images);
408                 usage(APPLY);
409                 ret = -1;
410                 goto done;
411         }
412         ret = wimlib_set_output_dir(w, dir);
413         if (ret != 0)
414                 goto done;
415         wimlib_set_verbose(w, verbose);
416         wimlib_set_link_type(w, link_type);
417         ret = wimlib_extract_image(w, image);
418 done:
419         wimlib_free(w);
420         return ret;
421 }
422
423
424 /* Create a WIM file from a directory. */
425 static int imagex_capture(int argc, const char **argv)
426 {
427         int c;
428         int add_image_flags = 0;
429         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
430         int compression_type = WIM_COMPRESSION_TYPE_NONE;
431         const char *flags_element    = NULL;
432         bool verbose         = false;
433         const char *dir;
434         const char *wimfile;
435         const char *name;
436         const char *desc;
437         WIMStruct *w;
438         int ret;
439
440         for_opt(c, capture_options) {
441                 switch (c) {
442                 case 'b':
443                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
444                         break;
445                 case 'c':
446                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
447                         break;
448                 case 'x':
449                         compression_type = get_compression_type(optarg);
450                         if (compression_type == WIM_COMPRESSION_TYPE_INVALID)
451                                 return -1;
452                         break;
453                 case 'C':
454                         /*config = optarg;*/
455                         break;
456                 case 'f':
457                         flags_element = optarg;
458                         break;
459                 case 'n':
460                         /*norpfix = true;*/
461                         break;
462                 case 'v':
463                         verbose = true;
464                         break;
465                 case 'V':
466                         /*verify = true;*/
467                         break;
468                 default:
469                         usage(CAPTURE);
470                         return -1;
471                 }
472         }
473
474         argc -= optind;
475         argv += optind;
476         if (argc < 2 || argc > 4) {
477                 usage(CAPTURE);
478                 return -1;
479         }
480         dir     = argv[0];
481         wimfile = argv[1];
482         name    = (argc >= 3) ? argv[2] : dir;
483         desc    = (argc >= 4) ? argv[3] : NULL;
484
485         ret = wimlib_create_new_wim(compression_type, &w);
486         if (ret != 0)
487                 return ret;
488
489         wimlib_set_verbose(w, verbose);
490
491         ret = wimlib_add_image(w, dir, name, desc, flags_element, 
492                                add_image_flags);
493         if (ret != 0) {
494                 imagex_error("Failed to add the image `%s'!\n", dir);
495                 goto done;
496         }
497
498         ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags);
499         if (ret != 0)
500                 imagex_error("Failed to write the WIM file `%s'!\n", wimfile);
501 done:
502         wimlib_free(w);
503         return ret;
504 }
505
506 /* Remove image(s) from a WIM. */
507 static int imagex_delete(int argc, const char **argv)
508 {
509         int c;
510         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
511         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
512         const char *wimfile;
513         const char *image_num_or_name;
514         WIMStruct *w;
515         int image;
516         int ret;
517
518         for_opt(c, delete_options) {
519                 switch (c) {
520                 case 'c':
521                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
522                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
523                         break;
524                 default:
525                         usage(DELETE);
526                         return -1;
527                 }
528         }
529         argc -= optind;
530         argv += optind;
531
532         if (argc != 2) {
533                 if (argc < 1)
534                         imagex_error("Must specify a WIM file!\n");
535                 if (argc < 2)
536                         imagex_error("Must specify an image!\n");
537                 usage(DELETE);
538                 return -1;
539         }
540         wimfile = argv[0];
541         image_num_or_name = argv[1];
542
543         ret = wimlib_open_wim(wimfile, open_flags, &w);
544         if (ret != 0)
545                 return ret;
546
547         image = wimlib_resolve_image(w, image_num_or_name);
548
549         ret = verify_image_exists(image);
550         if (ret != 0)
551                 goto done;
552
553         ret = wimlib_delete_image(w, image);
554         if (ret != 0) {
555                 imagex_error("Failed to delete image from `%s'!\n",
556                                                 wimfile);
557                 goto done;
558         }
559
560         ret = wimlib_overwrite(w, write_flags);
561         if (ret != 0) {
562                 imagex_error("Failed to write the file `%s' with image "
563                                 "deleted!\n", wimfile);
564         }
565 done:
566         wimlib_free(w);
567         return ret;
568 }
569
570 /* Print the files contained in an image(s) in a WIM file. */
571 static int imagex_dir(int argc, const char **argv)
572 {
573         const char *wimfile;
574         WIMStruct *w;
575         int image;
576         int ret;
577         int num_images;
578         int part_number;
579
580         if (argc < 2) {
581                 imagex_error("Must specify a WIM file!\n");
582                 usage(DIR);
583                 return -1;
584         }
585         if (argc > 3) {
586                 imagex_error("Too many arguments!\n");
587                 usage(DIR);
588                 return -1;
589         }
590
591         wimfile = argv[1];
592         ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w);
593         if (ret != 0)
594                 return ret;
595
596         part_number = wimlib_get_part_number(w, NULL);
597         if (part_number != 1) {
598                 imagex_error("`%s' is part %d of a split WIM!  Specify the first part "
599                                 "to see the files.\n",
600                                 wimfile, part_number);
601                 ret = WIMLIB_ERR_SPLIT_UNSUPPORTED;
602                 goto done;
603         }
604
605         if (argc == 3) {
606                 image = wimlib_resolve_image(w, argv[2]);
607                 ret = verify_image_exists(image);
608                 if (ret != 0)
609                         goto done;
610         } else {
611                 /* Image was not specified.  If the WIM only contains one image,
612                  * choose that one; otherwise, print an error. */
613                 num_images = wimlib_get_num_images(w);
614                 if (num_images != 1) {
615                         imagex_error("The file `%s' contains %d images; Please "
616                                         "select one.\n", wimfile, num_images);
617                         usage(DIR);
618                         ret = -1;
619                         goto done;
620                 }
621                 image = 1;
622         }
623
624         ret = wimlib_print_files(w, image);
625 done:
626         wimlib_free(w);
627         return ret;
628 }
629
630 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
631  * WIM file. */
632 static int imagex_export(int argc, const char **argv)
633 {
634         int c;
635         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
636         int export_flags = 0;
637         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
638         int compression_type = WIM_COMPRESSION_TYPE_NONE;
639         bool compression_type_specified = false;
640         const char *src_wimfile;
641         const char *src_image_num_or_name;
642         const char *dest_wimfile;
643         const char *dest_name;
644         const char *dest_desc;
645         WIMStruct *src_w = NULL;
646         WIMStruct *dest_w = NULL;
647         int ret;
648         int image;
649         struct stat stbuf;
650         bool wim_is_new;
651
652         for_opt(c, export_options) {
653                 switch (c) {
654                 case 'b':
655                         export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
656                         break;
657                 case 'c':
658                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
659                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
660                         break;
661                 case 'x':
662                         compression_type = get_compression_type(optarg);
663                         if (compression_type == WIM_COMPRESSION_TYPE_INVALID)
664                                 return -1;
665                         compression_type_specified = true;
666                         break;
667                 default:
668                         usage(EXPORT);
669                         return -1;
670                 }
671         }
672         argc -= optind;
673         argv += optind;
674         if (argc < 3 || argc > 5) {
675                 usage(EXPORT);
676                 return -1;
677         }
678         src_wimfile           = argv[0];
679         src_image_num_or_name = argv[1];
680         dest_wimfile          = argv[2];
681         dest_name             = (argc >= 4) ? argv[3] : NULL;
682         dest_desc             = (argc >= 5) ? argv[4] : NULL;
683         ret = wimlib_open_wim(src_wimfile, open_flags, &src_w);
684         if (ret != 0)
685                 return ret;
686
687         /* Determine if the destination is an existing file or not.  
688          * If so, we try to append the exported image(s) to it; otherwise, we
689          * create a new WIM containing the exported image(s). */
690         if (stat(dest_wimfile, &stbuf) == 0) {
691                 wim_is_new = false;
692                 /* Destination file exists. */
693                 if (!S_ISREG(stbuf.st_mode)) {
694                         imagex_error("`%s' is not a regular file!\n",
695                                         dest_wimfile);
696                         goto done;
697                 }
698                 ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w);
699                 if (ret != 0)
700                         goto done;
701
702                 if (compression_type_specified && compression_type != 
703                                 wimlib_get_compression_type(dest_w)) {
704                         imagex_error("Cannot specify a compression type that is "
705                                         "not the same as that used in the "
706                                         "destination WIM!\n");
707                         goto done;
708                 }
709                 compression_type = wimlib_get_compression_type(dest_w);
710         } else {
711                 wim_is_new = true;
712                 /* dest_wimfile is not an existing file, so create a new WIM. */
713                 if (errno == ENOENT) {
714                         ret = wimlib_create_new_wim(compression_type, &dest_w);
715                         if (ret != 0)
716                                 goto done;
717                 } else {
718                         imagex_error("Cannot stat file `%s': %m\n",
719                                                 dest_wimfile);
720                         goto done;
721                 }
722         }
723
724         image = wimlib_resolve_image(src_w, src_image_num_or_name);
725         ret = verify_image_exists(image);
726         if (ret != 0)
727                 goto done;
728
729         ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc, 
730                                   export_flags);
731         if (ret != 0)
732                 goto done;
733
734
735         if (wim_is_new)
736                 ret = wimlib_write(dest_w, dest_wimfile, WIM_ALL_IMAGES, 
737                                    write_flags);
738         else
739                 ret = wimlib_overwrite(dest_w, write_flags);
740 done:
741         wimlib_free(src_w);
742         wimlib_free(dest_w);
743         return ret;
744 }
745
746 /* Prints information about a WIM file; also can mark an image as bootable,
747  * change the name of an image, or change the description of an image. */
748 static int imagex_info(int argc, const char **argv)
749 {
750         int c;
751         bool boot         = false;
752         bool check        = false;
753         bool header       = false;
754         bool lookup_table = false;
755         bool xml          = false;
756         bool metadata     = false;
757         bool short_header = true;
758         const char *xml_out_file = NULL;
759         const char *wimfile;
760         const char *image_num_or_name = "all";
761         const char *new_name = NULL;
762         const char *new_desc = NULL;
763         WIMStruct *w;
764         FILE *fp;
765         int image;
766         int ret;
767         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | 
768                          WIMLIB_OPEN_FLAG_SPLIT_OK;
769         int part_number;
770         int total_parts;
771
772         for_opt(c, info_options) {
773                 switch (c) {
774                 case 'b':
775                         boot = true;
776                         break;
777                 case 'c':
778                         check = true;
779                         break;
780                 case 'h':
781                         header = true;
782                         short_header = false;
783                         break;
784                 case 'l':
785                         lookup_table = true;
786                         short_header = false;
787                         break;
788                 case 'x':
789                         xml = true;
790                         short_header = false;
791                         break;
792                 case 'X':
793                         xml_out_file = optarg;
794                         short_header = false;
795                         break;
796                 case 'm':
797                         metadata = true;
798                         short_header = false;
799                         break;
800                 default:
801                         usage(INFO);
802                         return -1;
803                 }
804         }
805
806         argc -= optind;
807         argv += optind;
808         if (argc == 0 || argc > 4) {
809                 usage(INFO);
810                 return -1;
811         }
812         wimfile = argv[0];
813         if (argc > 1) {
814                 image_num_or_name = argv[1];
815                 if (argc > 2) {
816                         new_name = argv[2];
817                         if (argc > 3) {
818                                 new_desc = argv[3];
819                         } 
820                 }
821         }
822
823         if (check)
824                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
825
826         ret = wimlib_open_wim(wimfile, open_flags, &w);
827         if (ret != 0)
828                 return ret;
829
830         part_number = wimlib_get_part_number(w, &total_parts);
831
832         /*if (total_parts > 1 && part_number > 1) {*/
833                 /*printf("Warning: this is part %d of a %d-part split WIM.\n"*/
834                        /*"         Select the first part if you want to see information\n"*/
835                        /*"         about images in the WIM.\n", */
836                        /*part_number, total_parts);*/
837         /*}*/
838
839         image = wimlib_resolve_image(w, image_num_or_name);
840         if (image == WIM_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) {
841                 imagex_error("The image `%s' does not exist!\n", 
842                                                 image_num_or_name);
843                 if (boot)
844                         imagex_error("If you would like to set the boot "
845                                         "index to 0, specify image \"0\" with "
846                                         "the --boot flag.\n");
847                 ret = WIMLIB_ERR_INVALID_IMAGE;
848                 goto done;
849         }
850
851         if (image == WIM_ALL_IMAGES && wimlib_get_num_images(w) > 1) {
852                 if (boot) {
853                         imagex_error("Cannot specify the --boot flag "
854                                         "without specifying a specific "
855                                         "image in a multi-image WIM!\n");
856                         ret = WIMLIB_ERR_INVALID_IMAGE;
857                         goto done;
858                 }
859                 if (new_name) {
860                         imagex_error("Cannot specify the NEW_NAME "
861                                         "without specifying a specific "
862                                         "image in a multi-image WIM!\n");
863                         ret = WIMLIB_ERR_INVALID_IMAGE;
864                         goto done;
865                 }
866         }
867
868         /* Operations that print information are separated from operations that
869          * recreate the WIM file. */
870         if (!new_name && !boot) {
871
872                 if (image == WIM_NO_IMAGE) {
873                         imagex_error("`%s' is not a valid image!\n", 
874                                         image_num_or_name);
875                         ret = WIMLIB_ERR_INVALID_IMAGE;
876                         goto done;
877                 }
878
879                 if (image == WIM_ALL_IMAGES && short_header)
880                         wimlib_print_wim_information(w);
881
882                 if (header)
883                         wimlib_print_header(w);
884
885                 if (lookup_table) {
886                         if (total_parts != 1) {
887                                 printf("Warning: Only showing the lookup table "
888                                                 "for part %d of a %d-part WIM.\n",
889                                                 part_number, total_parts);
890                         }
891                         wimlib_print_lookup_table(w);
892                 }
893
894                 if (xml) {
895                         ret = wimlib_extract_xml_data(w, stdout);
896                         if (ret != 0)
897                                 goto done;
898                 }
899
900                 if (xml_out_file) {
901                         fp = fopen(xml_out_file, "wb");
902                         if (!fp) {
903                                 imagex_error("Failed to open the file `%s' for "
904                                                 "writing: %m\n", xml_out_file);
905                                 goto done;
906                         }
907                         ret = wimlib_extract_xml_data(w, fp);
908                         if (fclose(fp) != 0) {
909                                 imagex_error("Failed to close the "
910                                                 "file `%s': %m\n",
911                                                 xml_out_file);
912                                 goto done;
913                         }
914
915                         if (ret != 0)
916                                 goto done;
917                 }
918
919                 if (short_header)
920                         wimlib_print_available_images(w, image);
921
922                 if (metadata) {
923                         if (total_parts != 1 && part_number != 1) {
924                                 imagex_error("Select part 1 of this %d-part WIM "
925                                                 "to see the image metadata.\n",
926                                                 total_parts);
927                                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
928                         }
929                         ret = wimlib_print_metadata(w, image);
930                         if (ret != 0)
931                                 goto done;
932                 }
933         } else {
934                 if (total_parts != 1) {
935                         imagex_error("Modifying a split WIM is not supported.\n");
936                         return -1;
937                 }
938                 if (image == WIM_ALL_IMAGES)
939                         image = 1;
940
941                 if (image == WIM_NO_IMAGE && new_name) {
942                         imagex_error("Cannot specify new_name (`%s') when "
943                                         "using image 0!\n");
944                         return -1;
945                 }
946
947                 if (boot) {
948                         if (image == wimlib_get_boot_idx(w)) {
949                                 printf("Image %d is already marked as "
950                                                 "bootable.\n", image);
951                                 boot = false;
952                         } else {
953                                 printf("Marking image %d as bootable.\n", 
954                                                                 image);
955                                 wimlib_set_boot_idx(w, image);
956                         }
957                 }
958                 if (new_name) {
959                         if (strcmp(wimlib_get_image_name(w, image), 
960                                                 new_name) == 0) {
961                                 printf("Image %d is already named \"%s\".\n",
962                                                 image, new_name);
963                                 new_name = NULL;
964                         } else {
965                                 printf("Changing the name of image %d to \"%s\".\n",
966                                                 image, new_name);
967                                 ret = wimlib_set_image_name(w, image, new_name);
968                                 if (ret != 0)
969                                         goto done;
970                         }
971                 }
972                 if (new_desc) {
973                         const char *old_desc;
974                         old_desc = wimlib_get_image_description(w, image);
975                         if (old_desc && strcmp(old_desc, new_desc) == 0) {
976                                 printf("The description of image %d is already "
977                                                 "\"%s\".\n", image, new_desc);
978                                 new_desc = NULL;
979                         } else {
980                                 printf("Changing the description of image %d "
981                                                 "to \"%s\".\n", image, new_desc);
982                                 ret = wimlib_set_image_descripton(w, image, 
983                                                                   new_desc);
984                                 if (ret != 0)
985                                         goto done;
986                         }
987                 }
988
989                 /* Only call wimlib_overwrite_xml_and_header() if something
990                  * actually needs to be changed. */
991                 if (boot || new_name || new_desc || 
992                                 check != wimlib_has_integrity_table(w)) {
993
994                         ret = wimlib_overwrite_xml_and_header(w, check ? 
995                                         WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | 
996                                         WIMLIB_WRITE_FLAG_SHOW_PROGRESS : 0);
997                 } else {
998                         printf("The file `%s' was not modified because nothing "
999                                         "needed to be done.\n", wimfile);
1000                         ret = 0;
1001                 }
1002         }
1003
1004 done:
1005         wimlib_free(w);
1006         return ret;
1007 }
1008
1009 /* Join split WIMs into one part WIM */
1010 static int imagex_join(int argc, const char **argv)
1011 {
1012         int c;
1013         int flags = WIMLIB_OPEN_FLAG_SPLIT_OK | WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1014         const char *output_path;
1015
1016         for_opt(c, join_options) {
1017                 switch (c) {
1018                 case 'c':
1019                         flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1020                         break;
1021                 default:
1022                         goto err;
1023                 }
1024         }
1025         argc -= optind;
1026         argv += optind;
1027
1028         if (argc < 2) {
1029                 imagex_error("Must specify at least one split WIM "
1030                                 "(.swm) parts to join!\n");
1031                 goto err;
1032         }
1033         output_path = argv[0];
1034         return wimlib_join(++argv, --argc, output_path, flags);
1035 err:
1036         usage(JOIN);
1037         return -1;
1038 }
1039
1040 /* Mounts an image using a FUSE mount. */
1041 static int imagex_mount_rw_or_ro(int argc, const char **argv)
1042 {
1043         int c;
1044         int mount_flags = 0;
1045         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1046         const char *wimfile;
1047         const char *dir;
1048         WIMStruct *w;
1049         int image;
1050         int num_images;
1051         int ret;
1052
1053         if (strcmp(argv[0], "mountrw") == 0)
1054                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
1055         for_opt(c, mount_options) {
1056                 switch (c) {
1057                 case 'c':
1058                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1059                         break;
1060                 case 'd':
1061                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
1062                         break;
1063                 default:
1064                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)  
1065                                         ? MOUNTRW : MOUNT);
1066                         return -1;
1067                 }
1068         }
1069         argc -= optind;
1070         argv += optind;
1071         if (argc != 2 && argc != 3) {
1072                 usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)  
1073                                 ? MOUNTRW : MOUNT);
1074                 return -1;
1075         }
1076
1077         wimfile = argv[0];
1078
1079         ret = wimlib_open_wim(wimfile, open_flags, &w);
1080         if (ret != 0)
1081                 return ret;
1082
1083         if (argc == 2) {
1084                 image = 1;
1085                 num_images = wimlib_get_num_images(w);
1086                 if (num_images != 1) {
1087                         imagex_error("The file `%s' contains %d images; Please "
1088                                         "select one.\n", wimfile, num_images);
1089                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)  
1090                                         ? MOUNTRW : MOUNT);
1091                         ret = WIMLIB_ERR_INVALID_IMAGE;
1092                         goto done;
1093                 }
1094                 dir = argv[1];
1095         } else {
1096                 image = wimlib_resolve_image(w, argv[1]);
1097                 dir = argv[2];
1098         }
1099
1100         ret = verify_image_exists_and_is_single(image);
1101         if (ret != 0)
1102                 goto done;
1103
1104         ret = wimlib_mount(w, image, dir, mount_flags);
1105         if (ret != 0) {
1106                 imagex_error("Failed to mount image %d from `%s' on `%s'!\n",
1107                                 image, wimfile, dir);
1108
1109         }
1110 done:
1111         wimlib_free(w);
1112         return ret;
1113 }
1114
1115 /* Split a WIM into a spanned set */
1116 static int imagex_split(int argc, const char **argv)
1117 {
1118         int c;
1119         int flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1120         unsigned long part_size;
1121
1122         for_opt(c, split_options) {
1123                 switch (c) {
1124                 case 'c':
1125                         flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1126                         break;
1127                 default:
1128                         usage(SPLIT);
1129                         return -1;
1130                 }
1131         }
1132         argc -= optind;
1133         argv += optind;
1134
1135         if (argc != 3) {
1136                 usage(SPLIT);
1137                 return -1;
1138         }
1139         part_size = strtoul(argv[2], NULL, 10) * (1 << 20);
1140         return wimlib_split(argv[0], argv[1], part_size, flags);
1141 }
1142
1143 /* Unmounts an image. */
1144 static int imagex_unmount(int argc, const char **argv)
1145 {
1146         int c;
1147         int unmount_flags = 0;
1148         int ret;
1149
1150         for_opt(c, unmount_options) {
1151                 switch (c) {
1152                 case 'c':
1153                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
1154                         break;
1155                 case 'C':
1156                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
1157                         break;
1158                 default:
1159                         usage(UNMOUNT);
1160                         return -1;
1161                 }
1162         }
1163         argc -= optind;
1164         argv += optind;
1165         if (argc != 1) {
1166                 usage(UNMOUNT);
1167                 return -1;
1168         }
1169
1170         ret = wimlib_unmount(argv[0], unmount_flags);
1171         if (ret != 0)
1172                 imagex_error("Failed to unmount `%s'!\n", argv[0]);
1173         return ret;
1174 }
1175
1176 struct imagex_command {
1177         const char *name;
1178         int (*func)(int , const char **);
1179         int cmd;
1180 };
1181
1182 static struct imagex_command imagex_commands[] = {
1183         {"append",  imagex_append,         APPEND},
1184         {"apply",   imagex_apply,          APPLY},
1185         {"capture", imagex_capture,        CAPTURE},
1186         {"delete",  imagex_delete,         DELETE},
1187         {"dir",     imagex_dir,            DIR},
1188         {"export",  imagex_export,         EXPORT},
1189         {"info",    imagex_info,           INFO},
1190         {"join",    imagex_join,           JOIN},
1191         {"mount",   imagex_mount_rw_or_ro, MOUNT},
1192         {"mountrw", imagex_mount_rw_or_ro, MOUNTRW},
1193         {"split",   imagex_split,          SPLIT},
1194         {"unmount", imagex_unmount,        UNMOUNT},
1195 };
1196
1197 #define for_imagex_command(p) for (p = &imagex_commands[0]; \
1198                 p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++)
1199
1200 static void help_or_version(int argc, const char **argv)
1201 {
1202         int i;
1203         const char *p;
1204         struct imagex_command *cmd;
1205
1206         for (i = 1; i < argc; i++) {
1207                 p = argv[i];
1208                 if (*p == '-')
1209                         p++;
1210                 else
1211                         continue;
1212                 if (*p == '-')
1213                         p++;
1214                 if (strcmp(p, "help") == 0 || (*p == '?' && *(p + 1) == '\0')) {
1215                         for_imagex_command(cmd) {
1216                                 if (strcmp(cmd->name, argv[1]) == 0) {
1217                                         usage(cmd->cmd);
1218                                         exit(0);
1219                                 }
1220                         }
1221                         usage_all();
1222                         exit(0);
1223                 }
1224                 if (strcmp(p, "version") == 0) {
1225                         version();
1226                         exit(0);
1227                 }
1228         }
1229 }
1230
1231
1232 int main(int argc, const char **argv)
1233 {
1234         struct imagex_command *cmd;
1235         int ret;
1236
1237         if (argc < 2) {
1238                 imagex_error("No command specified!\n");
1239                 usage_all();
1240                 return 1;
1241         }
1242
1243         help_or_version(argc, argv);
1244         argc--;
1245         argv++;
1246
1247         wimlib_set_print_errors(true);
1248
1249         for_imagex_command(cmd) {
1250                 if (strcmp(cmd->name, *argv) == 0) {
1251                         ret = cmd->func(argc, argv);
1252                         if (ret > 0) {
1253                                 imagex_error("Exiting with error code %d:\n"
1254                                                 "       %s.\n", ret, 
1255                                                 wimlib_get_error_string(ret));
1256                         }
1257                         return ret;
1258                 }
1259         }
1260
1261         imagex_error("Unrecognized command: `%s'\n", argv[0]);
1262         usage_all();
1263         return 1;
1264 }