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