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