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