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