]> wimlib.net Git - wimlib/blob - programs/imagex.c
wimlib_apply_image_to_ntfs_volume() API function
[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_LZX;
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_NONE;
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                         break;
482                 case 'N':
483                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
484                         break;
485                 case 'L':
486                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
487                         break;
488                 default:
489                         usage(CAPTURE);
490                         return -1;
491                 }
492         }
493
494         argc -= optind;
495         argv += optind;
496         if (argc < 2 || argc > 4) {
497                 usage(CAPTURE);
498                 return -1;
499         }
500         dir     = argv[0];
501         wimfile = argv[1];
502         name    = (argc >= 3) ? argv[2] : dir;
503         desc    = (argc >= 4) ? argv[3] : NULL;
504
505         ret = wimlib_create_new_wim(compression_type, &w);
506         if (ret != 0)
507                 return ret;
508
509         ret = wimlib_add_image(w, dir, name, desc, flags_element, 
510                                add_image_flags);
511         if (ret != 0) {
512                 imagex_error("Failed to add the image `%s'", dir);
513                 goto done;
514         }
515
516         ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags);
517         if (ret != 0)
518                 imagex_error("Failed to write the WIM file `%s'", wimfile);
519 done:
520         wimlib_free(w);
521         return ret;
522 }
523
524 /* Remove image(s) from a WIM. */
525 static int imagex_delete(int argc, const char **argv)
526 {
527         int c;
528         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
529         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
530         const char *wimfile;
531         const char *image_num_or_name;
532         WIMStruct *w;
533         int image;
534         int ret;
535
536         for_opt(c, delete_options) {
537                 switch (c) {
538                 case 'c':
539                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
540                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
541                         break;
542                 default:
543                         usage(DELETE);
544                         return -1;
545                 }
546         }
547         argc -= optind;
548         argv += optind;
549
550         if (argc != 2) {
551                 if (argc < 1)
552                         imagex_error("Must specify a WIM file");
553                 if (argc < 2)
554                         imagex_error("Must specify an image");
555                 usage(DELETE);
556                 return -1;
557         }
558         wimfile = argv[0];
559         image_num_or_name = argv[1];
560
561         ret = wimlib_open_wim(wimfile, open_flags, &w);
562         if (ret != 0)
563                 return ret;
564
565         image = wimlib_resolve_image(w, image_num_or_name);
566
567         ret = verify_image_exists(image);
568         if (ret != 0)
569                 goto done;
570
571         ret = wimlib_delete_image(w, image);
572         if (ret != 0) {
573                 imagex_error("Failed to delete image from `%s'", wimfile);
574                 goto done;
575         }
576
577         ret = wimlib_overwrite(w, write_flags);
578         if (ret != 0) {
579                 imagex_error("Failed to write the file `%s' with image "
580                              "deleted", wimfile);
581         }
582 done:
583         wimlib_free(w);
584         return ret;
585 }
586
587 /* Print the files contained in an image(s) in a WIM file. */
588 static int imagex_dir(int argc, const char **argv)
589 {
590         const char *wimfile;
591         WIMStruct *w;
592         int image;
593         int ret;
594         int num_images;
595         int part_number;
596
597         if (argc < 2) {
598                 imagex_error("Must specify a WIM file");
599                 usage(DIR);
600                 return -1;
601         }
602         if (argc > 3) {
603                 imagex_error("Too many arguments");
604                 usage(DIR);
605                 return -1;
606         }
607
608         wimfile = argv[1];
609         ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w);
610         if (ret != 0)
611                 return ret;
612
613         part_number = wimlib_get_part_number(w, NULL);
614         if (part_number != 1) {
615                 imagex_error("`%s' is part %d of a split WIM!  Specify the "
616                              "first part to see the files",
617                              wimfile, part_number);
618                 ret = WIMLIB_ERR_SPLIT_UNSUPPORTED;
619                 goto done;
620         }
621
622         if (argc == 3) {
623                 image = wimlib_resolve_image(w, argv[2]);
624                 ret = verify_image_exists(image);
625                 if (ret != 0)
626                         goto done;
627         } else {
628                 /* Image was not specified.  If the WIM only contains one image,
629                  * choose that one; otherwise, print an error. */
630                 num_images = wimlib_get_num_images(w);
631                 if (num_images != 1) {
632                         imagex_error("The file `%s' contains %d images; Please "
633                                      "select one.", wimfile, num_images);
634                         usage(DIR);
635                         ret = -1;
636                         goto done;
637                 }
638                 image = 1;
639         }
640
641         ret = wimlib_print_files(w, image);
642 done:
643         wimlib_free(w);
644         return ret;
645 }
646
647 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
648  * WIM file. */
649 static int imagex_export(int argc, const char **argv)
650 {
651         int c;
652         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
653         int export_flags = 0;
654         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
655         int compression_type = WIM_COMPRESSION_TYPE_NONE;
656         bool compression_type_specified = false;
657         const char *src_wimfile;
658         const char *src_image_num_or_name;
659         const char *dest_wimfile;
660         const char *dest_name;
661         const char *dest_desc;
662         WIMStruct *src_w = NULL;
663         WIMStruct *dest_w = NULL;
664         int ret;
665         int image;
666         struct stat stbuf;
667         bool wim_is_new;
668
669         for_opt(c, export_options) {
670                 switch (c) {
671                 case 'b':
672                         export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
673                         break;
674                 case 'c':
675                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
676                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
677                         break;
678                 case 'x':
679                         compression_type = get_compression_type(optarg);
680                         if (compression_type == WIM_COMPRESSION_TYPE_INVALID)
681                                 return -1;
682                         compression_type_specified = true;
683                         break;
684                 default:
685                         usage(EXPORT);
686                         return -1;
687                 }
688         }
689         argc -= optind;
690         argv += optind;
691         if (argc < 3 || argc > 5) {
692                 usage(EXPORT);
693                 return -1;
694         }
695         src_wimfile           = argv[0];
696         src_image_num_or_name = argv[1];
697         dest_wimfile          = argv[2];
698         dest_name             = (argc >= 4) ? argv[3] : NULL;
699         dest_desc             = (argc >= 5) ? argv[4] : NULL;
700         ret = wimlib_open_wim(src_wimfile, open_flags, &src_w);
701         if (ret != 0)
702                 return ret;
703
704         /* Determine if the destination is an existing file or not.  
705          * If so, we try to append the exported image(s) to it; otherwise, we
706          * create a new WIM containing the exported image(s). */
707         if (stat(dest_wimfile, &stbuf) == 0) {
708                 wim_is_new = false;
709                 /* Destination file exists. */
710                 if (!S_ISREG(stbuf.st_mode)) {
711                         imagex_error("`%s' is not a regular file",
712                                         dest_wimfile);
713                         goto done;
714                 }
715                 ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w);
716                 if (ret != 0)
717                         goto done;
718
719                 if (compression_type_specified && compression_type != 
720                                 wimlib_get_compression_type(dest_w)) {
721                         imagex_error("Cannot specify a compression type that is "
722                                      "not the same as that used in the "
723                                      "destination WIM");
724                         ret = -1;
725                         goto done;
726                 }
727                 compression_type = wimlib_get_compression_type(dest_w);
728         } else {
729                 wim_is_new = true;
730                 /* dest_wimfile is not an existing file, so create a new WIM. */
731                 if (errno == ENOENT) {
732                         ret = wimlib_create_new_wim(compression_type, &dest_w);
733                         if (ret != 0)
734                                 goto done;
735                 } else {
736                         imagex_error_with_errno("Cannot stat file `%s'",
737                                                 dest_wimfile);
738                         goto done;
739                 }
740         }
741
742         image = wimlib_resolve_image(src_w, src_image_num_or_name);
743         ret = verify_image_exists(image);
744         if (ret != 0)
745                 goto done;
746
747         ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc, 
748                                   export_flags);
749         if (ret != 0)
750                 goto done;
751
752
753         if (wim_is_new)
754                 ret = wimlib_write(dest_w, dest_wimfile, WIM_ALL_IMAGES, 
755                                    write_flags);
756         else
757                 ret = wimlib_overwrite(dest_w, write_flags);
758 done:
759         wimlib_free(src_w);
760         wimlib_free(dest_w);
761         return ret;
762 }
763
764 /* Prints information about a WIM file; also can mark an image as bootable,
765  * change the name of an image, or change the description of an image. */
766 static int imagex_info(int argc, const char **argv)
767 {
768         int c;
769         bool boot         = false;
770         bool check        = false;
771         bool header       = false;
772         bool lookup_table = false;
773         bool xml          = false;
774         bool metadata     = false;
775         bool short_header = true;
776         const char *xml_out_file = NULL;
777         const char *wimfile;
778         const char *image_num_or_name = "all";
779         const char *new_name = NULL;
780         const char *new_desc = NULL;
781         WIMStruct *w;
782         FILE *fp;
783         int image;
784         int ret;
785         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | 
786                          WIMLIB_OPEN_FLAG_SPLIT_OK;
787         int part_number;
788         int total_parts;
789
790         for_opt(c, info_options) {
791                 switch (c) {
792                 case 'b':
793                         boot = true;
794                         break;
795                 case 'c':
796                         check = true;
797                         break;
798                 case 'h':
799                         header = true;
800                         short_header = false;
801                         break;
802                 case 'l':
803                         lookup_table = true;
804                         short_header = false;
805                         break;
806                 case 'x':
807                         xml = true;
808                         short_header = false;
809                         break;
810                 case 'X':
811                         xml_out_file = optarg;
812                         short_header = false;
813                         break;
814                 case 'm':
815                         metadata = true;
816                         short_header = false;
817                         break;
818                 default:
819                         usage(INFO);
820                         return -1;
821                 }
822         }
823
824         argc -= optind;
825         argv += optind;
826         if (argc == 0 || argc > 4) {
827                 usage(INFO);
828                 return -1;
829         }
830         wimfile = argv[0];
831         if (argc > 1) {
832                 image_num_or_name = argv[1];
833                 if (argc > 2) {
834                         new_name = argv[2];
835                         if (argc > 3) {
836                                 new_desc = argv[3];
837                         } 
838                 }
839         }
840
841         if (check)
842                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
843
844         ret = wimlib_open_wim(wimfile, open_flags, &w);
845         if (ret != 0)
846                 return ret;
847
848         part_number = wimlib_get_part_number(w, &total_parts);
849
850         /*if (total_parts > 1 && part_number > 1) {*/
851                 /*printf("Warning: this is part %d of a %d-part split WIM.\n"*/
852                        /*"         Select the first part if you want to see information\n"*/
853                        /*"         about images in the WIM.\n", */
854                        /*part_number, total_parts);*/
855         /*}*/
856
857         image = wimlib_resolve_image(w, image_num_or_name);
858         if (image == WIM_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) {
859                 imagex_error("The image `%s' does not exist", 
860                                                 image_num_or_name);
861                 if (boot)
862                         imagex_error("If you would like to set the boot "
863                                      "index to 0, specify image \"0\" with "
864                                      "the --boot flag.");
865                 ret = WIMLIB_ERR_INVALID_IMAGE;
866                 goto done;
867         }
868
869         if (image == WIM_ALL_IMAGES && wimlib_get_num_images(w) > 1) {
870                 if (boot) {
871                         imagex_error("Cannot specify the --boot flag "
872                                      "without specifying a specific "
873                                      "image in a multi-image WIM");
874                         ret = WIMLIB_ERR_INVALID_IMAGE;
875                         goto done;
876                 }
877                 if (new_name) {
878                         imagex_error("Cannot specify the NEW_NAME "
879                                      "without specifying a specific "
880                                      "image in a multi-image WIM");
881                         ret = WIMLIB_ERR_INVALID_IMAGE;
882                         goto done;
883                 }
884         }
885
886         /* Operations that print information are separated from operations that
887          * recreate the WIM file. */
888         if (!new_name && !boot) {
889
890                 if (image == WIM_NO_IMAGE) {
891                         imagex_error("`%s' is not a valid image",
892                                      image_num_or_name);
893                         ret = WIMLIB_ERR_INVALID_IMAGE;
894                         goto done;
895                 }
896
897                 if (image == WIM_ALL_IMAGES && short_header)
898                         wimlib_print_wim_information(w);
899
900                 if (header)
901                         wimlib_print_header(w);
902
903                 if (lookup_table) {
904                         if (total_parts != 1) {
905                                 printf("Warning: Only showing the lookup table "
906                                        "for part %d of a %d-part WIM.\n",
907                                        part_number, total_parts);
908                         }
909                         wimlib_print_lookup_table(w);
910                 }
911
912                 if (xml) {
913                         ret = wimlib_extract_xml_data(w, stdout);
914                         if (ret != 0)
915                                 goto done;
916                 }
917
918                 if (xml_out_file) {
919                         fp = fopen(xml_out_file, "wb");
920                         if (!fp) {
921                                 imagex_error_with_errno("Failed to open the "
922                                                         "file `%s' for "
923                                                         "writing ",
924                                                         xml_out_file);
925                                 goto done;
926                         }
927                         ret = wimlib_extract_xml_data(w, fp);
928                         if (fclose(fp) != 0) {
929                                 imagex_error("Failed to close the file `%s'",
930                                              xml_out_file);
931                                 goto done;
932                         }
933
934                         if (ret != 0)
935                                 goto done;
936                 }
937
938                 if (short_header)
939                         wimlib_print_available_images(w, image);
940
941                 if (metadata) {
942                         if (total_parts != 1 && part_number != 1) {
943                                 imagex_error("Select part 1 of this %d-part WIM "
944                                              "to see the image metadata",
945                                              total_parts);
946                                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
947                         }
948                         ret = wimlib_print_metadata(w, image);
949                         if (ret != 0)
950                                 goto done;
951                 }
952         } else {
953                 if (total_parts != 1) {
954                         imagex_error("Modifying a split WIM is not supported.");
955                         return -1;
956                 }
957                 if (image == WIM_ALL_IMAGES)
958                         image = 1;
959
960                 if (image == WIM_NO_IMAGE && new_name) {
961                         imagex_error("Cannot specify new_name (`%s') when "
962                                      "using image 0", new_name);
963                         return -1;
964                 }
965
966                 if (boot) {
967                         if (image == wimlib_get_boot_idx(w)) {
968                                 printf("Image %d is already marked as "
969                                        "bootable.\n", image);
970                                 boot = false;
971                         } else {
972                                 printf("Marking image %d as bootable.\n",
973                                        image);
974                                 wimlib_set_boot_idx(w, image);
975                         }
976                 }
977                 if (new_name) {
978                         if (strcmp(wimlib_get_image_name(w, image), 
979                                                 new_name) == 0) {
980                                 printf("Image %d is already named \"%s\".\n",
981                                        image, new_name);
982                                 new_name = NULL;
983                         } else {
984                                 printf("Changing the name of image %d to "
985                                        "\"%s\".\n", image, new_name);
986                                 ret = wimlib_set_image_name(w, image, new_name);
987                                 if (ret != 0)
988                                         goto done;
989                         }
990                 }
991                 if (new_desc) {
992                         const char *old_desc;
993                         old_desc = wimlib_get_image_description(w, image);
994                         if (old_desc && strcmp(old_desc, new_desc) == 0) {
995                                 printf("The description of image %d is already "
996                                        "\"%s\".\n", image, new_desc);
997                                 new_desc = NULL;
998                         } else {
999                                 printf("Changing the description of image %d "
1000                                        "to \"%s\".\n", image, new_desc);
1001                                 ret = wimlib_set_image_descripton(w, image, 
1002                                                                   new_desc);
1003                                 if (ret != 0)
1004                                         goto done;
1005                         }
1006                 }
1007
1008                 /* Only call wimlib_overwrite_xml_and_header() if something
1009                  * actually needs to be changed. */
1010                 if (boot || new_name || new_desc || 
1011                                 check != wimlib_has_integrity_table(w)) {
1012
1013                         ret = wimlib_overwrite_xml_and_header(w, check ? 
1014                                         WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | 
1015                                         WIMLIB_WRITE_FLAG_SHOW_PROGRESS : 0);
1016                 } else {
1017                         printf("The file `%s' was not modified because nothing "
1018                                         "needed to be done.\n", wimfile);
1019                         ret = 0;
1020                 }
1021         }
1022
1023 done:
1024         wimlib_free(w);
1025         return ret;
1026 }
1027
1028 /* Join split WIMs into one part WIM */
1029 static int imagex_join(int argc, const char **argv)
1030 {
1031         int c;
1032         int flags = WIMLIB_OPEN_FLAG_SPLIT_OK | WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1033         const char *output_path;
1034
1035         for_opt(c, join_options) {
1036                 switch (c) {
1037                 case 'c':
1038                         flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1039                         break;
1040                 default:
1041                         goto err;
1042                 }
1043         }
1044         argc -= optind;
1045         argv += optind;
1046
1047         if (argc < 2) {
1048                 imagex_error("Must specify at least one split WIM (.swm) parts "
1049                              "to join");
1050                 goto err;
1051         }
1052         output_path = argv[0];
1053         return wimlib_join(++argv, --argc, output_path, flags);
1054 err:
1055         usage(JOIN);
1056         return -1;
1057 }
1058
1059 /* Mounts an image using a FUSE mount. */
1060 static int imagex_mount_rw_or_ro(int argc, const char **argv)
1061 {
1062         int c;
1063         int mount_flags = 0;
1064         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1065         const char *wimfile;
1066         const char *dir;
1067         WIMStruct *w;
1068         int image;
1069         int num_images;
1070         int ret;
1071
1072         if (strcmp(argv[0], "mountrw") == 0)
1073                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
1074         for_opt(c, mount_options) {
1075                 switch (c) {
1076                 case 'c':
1077                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1078                         break;
1079                 case 'd':
1080                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
1081                         break;
1082                 case 's':
1083                         if (strcasecmp(optarg, "none") == 0)
1084                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
1085                         else if (strcasecmp(optarg, "xattr") == 0)
1086                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
1087                         else if (strcasecmp(optarg, "windows") == 0)
1088                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
1089                         else {
1090                                 imagex_error("Unknown stream interface \"%s\"", optarg);
1091                                 goto mount_usage;
1092                         }
1093                         break;
1094                 default:
1095                         goto mount_usage;
1096                 }
1097         }
1098         argc -= optind;
1099         argv += optind;
1100         if (argc != 2 && argc != 3)
1101                 goto mount_usage;
1102
1103         wimfile = argv[0];
1104
1105         ret = wimlib_open_wim(wimfile, open_flags, &w);
1106         if (ret != 0)
1107                 return ret;
1108
1109         if (argc == 2) {
1110                 image = 1;
1111                 num_images = wimlib_get_num_images(w);
1112                 if (num_images != 1) {
1113                         imagex_error("The file `%s' contains %d images; Please "
1114                                      "select one", wimfile, num_images);
1115                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)  
1116                                         ? MOUNTRW : MOUNT);
1117                         ret = WIMLIB_ERR_INVALID_IMAGE;
1118                         goto done;
1119                 }
1120                 dir = argv[1];
1121         } else {
1122                 image = wimlib_resolve_image(w, argv[1]);
1123                 dir = argv[2];
1124         }
1125
1126         ret = verify_image_exists_and_is_single(image);
1127         if (ret != 0)
1128                 goto done;
1129
1130         ret = wimlib_mount(w, image, dir, mount_flags);
1131         if (ret != 0) {
1132                 imagex_error("Failed to mount image %d from `%s' on `%s'",
1133                              image, wimfile, dir);
1134
1135         }
1136 done:
1137         wimlib_free(w);
1138         return ret;
1139 mount_usage:
1140         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)  
1141                         ? MOUNTRW : MOUNT);
1142         return -1;
1143 }
1144
1145 /* Split a WIM into a spanned set */
1146 static int imagex_split(int argc, const char **argv)
1147 {
1148         int c;
1149         int flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1150         unsigned long part_size;
1151
1152         for_opt(c, split_options) {
1153                 switch (c) {
1154                 case 'c':
1155                         flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1156                         break;
1157                 default:
1158                         usage(SPLIT);
1159                         return -1;
1160                 }
1161         }
1162         argc -= optind;
1163         argv += optind;
1164
1165         if (argc != 3) {
1166                 usage(SPLIT);
1167                 return -1;
1168         }
1169         part_size = strtoul(argv[2], NULL, 10) * (1 << 20);
1170         return wimlib_split(argv[0], argv[1], part_size, flags);
1171 }
1172
1173 /* Unmounts an image. */
1174 static int imagex_unmount(int argc, const char **argv)
1175 {
1176         int c;
1177         int unmount_flags = 0;
1178         int ret;
1179
1180         for_opt(c, unmount_options) {
1181                 switch (c) {
1182                 case 'c':
1183                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
1184                         break;
1185                 case 'C':
1186                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
1187                         break;
1188                 default:
1189                         usage(UNMOUNT);
1190                         return -1;
1191                 }
1192         }
1193         argc -= optind;
1194         argv += optind;
1195         if (argc != 1) {
1196                 usage(UNMOUNT);
1197                 return -1;
1198         }
1199
1200         ret = wimlib_unmount(argv[0], unmount_flags);
1201         if (ret != 0)
1202                 imagex_error("Failed to unmount `%s'", argv[0]);
1203         return ret;
1204 }
1205
1206 struct imagex_command {
1207         const char *name;
1208         int (*func)(int , const char **);
1209         int cmd;
1210 };
1211
1212 static struct imagex_command imagex_commands[] = {
1213         {"append",  imagex_append,         APPEND},
1214         {"apply",   imagex_apply,          APPLY},
1215         {"capture", imagex_capture,        CAPTURE},
1216         {"delete",  imagex_delete,         DELETE},
1217         {"dir",     imagex_dir,            DIR},
1218         {"export",  imagex_export,         EXPORT},
1219         {"info",    imagex_info,           INFO},
1220         {"join",    imagex_join,           JOIN},
1221         {"mount",   imagex_mount_rw_or_ro, MOUNT},
1222         {"mountrw", imagex_mount_rw_or_ro, MOUNTRW},
1223         {"split",   imagex_split,          SPLIT},
1224         {"unmount", imagex_unmount,        UNMOUNT},
1225 };
1226
1227 #define for_imagex_command(p) for (p = &imagex_commands[0]; \
1228                 p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++)
1229
1230 static void help_or_version(int argc, const char **argv)
1231 {
1232         int i;
1233         const char *p;
1234         struct imagex_command *cmd;
1235
1236         for (i = 1; i < argc; i++) {
1237                 p = argv[i];
1238                 if (*p == '-')
1239                         p++;
1240                 else
1241                         continue;
1242                 if (*p == '-')
1243                         p++;
1244                 if (strcmp(p, "help") == 0 || (*p == '?' && *(p + 1) == '\0')) {
1245                         for_imagex_command(cmd) {
1246                                 if (strcmp(cmd->name, argv[1]) == 0) {
1247                                         usage(cmd->cmd);
1248                                         exit(0);
1249                                 }
1250                         }
1251                         usage_all();
1252                         exit(0);
1253                 }
1254                 if (strcmp(p, "version") == 0) {
1255                         version();
1256                         exit(0);
1257                 }
1258         }
1259 }
1260
1261
1262 int main(int argc, const char **argv)
1263 {
1264         struct imagex_command *cmd;
1265         int ret;
1266
1267         if (argc < 2) {
1268                 imagex_error("No command specified");
1269                 usage_all();
1270                 return 1;
1271         }
1272
1273         help_or_version(argc, argv);
1274         argc--;
1275         argv++;
1276
1277         wimlib_set_print_errors(true);
1278
1279         for_imagex_command(cmd) {
1280                 if (strcmp(cmd->name, *argv) == 0) {
1281                         ret = cmd->func(argc, argv);
1282                         if (ret > 0) {
1283                                 imagex_error("Exiting with error code %d:\n"
1284                                              "       %s.", ret,
1285                                              wimlib_get_error_string(ret));
1286                                 if (ret == WIMLIB_ERR_NTFS_3G)
1287                                         imagex_error_with_errno("errno");
1288                         }
1289                         return ret;
1290                 }
1291         }
1292
1293         imagex_error("Unrecognized command: `%s'", argv[0]);
1294         usage_all();
1295         return 1;
1296 }
1297 /*#ifndef WITH_NTFS_3G*/
1298                 /*ERROR("wimlib was not compiled with support for NTFS-3g, so we cannot extract");*/
1299                 /*ERROR("a WIM to a NTFS filesystem while preserving NTFS-specific metadata.");*/
1300                 /*ERROR("Please apply the WIM to a directory rather than a block device, ");*/
1301                 /*ERROR("and without the NTFS flag; or compile in support for NTFS-3g.");*/
1302                 /*return WIMLIB_ERR_UNSUPPORTED;*/
1303 /*#endif*/