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