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