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