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