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