]> wimlib.net Git - wimlib/blob - src/extract_image.c
7a46fe8f839dd9b08c5eafbd28ed1cce711ad480
[wimlib] / src / extract_image.c
1 /*
2  * extract_image.c
3  *
4  * Support for extracting WIM files.
5  */
6
7 /*
8  * Copyright (C) 2012, 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #include "config.h"
27
28 #include <stdlib.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <unistd.h>
32
33 #ifdef __WIN32__
34 #  include "win32.h"
35 #endif
36
37 #include "wimlib_internal.h"
38 #include "dentry.h"
39 #include "lookup_table.h"
40 #include "xml.h"
41
42 #ifdef WITH_NTFS_3G
43 #  include <ntfs-3g/volume.h>
44 #endif
45
46 static int
47 do_apply_op(struct wim_dentry *dentry, struct apply_args *args,
48             int (*apply_dentry_func)(const tchar *, size_t,
49                                      struct wim_dentry *, struct apply_args *))
50 {
51         tchar *p;
52         const tchar *full_path;
53         size_t full_path_nchars;
54
55         wimlib_assert(dentry->_full_path != NULL);
56         full_path = dentry->_full_path + 1;
57         full_path_nchars = dentry->full_path_nbytes / sizeof(tchar) - 1;
58         tchar output_path[args->target_nchars + 1 +
59                          (full_path_nchars - args->wim_source_path_nchars) + 1];
60         p = output_path;
61
62         /*print_dentry(dentry, NULL);*/
63         /*ERROR("%"TS" %"TS, args->target, dentry->_full_path);*/
64         /*ERROR("");*/
65
66         tmemcpy(p, args->target, args->target_nchars);
67         p += args->target_nchars;
68
69         if (dentry != args->extract_root) {
70                 *p++ = T('/');
71                 tmemcpy(p, full_path + args->wim_source_path_nchars,
72                         full_path_nchars - args->wim_source_path_nchars);
73                 p += full_path_nchars - args->wim_source_path_nchars;
74         }
75         *p = T('\0');
76         return (*apply_dentry_func)(output_path, p - output_path,
77                                     dentry, args);
78 }
79
80
81 /* Extracts a file, directory, or symbolic link from the WIM archive. */
82 static int
83 apply_dentry_normal(struct wim_dentry *dentry, void *arg)
84 {
85 #ifdef __WIN32__
86         return do_apply_op(dentry, arg, win32_do_apply_dentry);
87 #else
88         return do_apply_op(dentry, arg, unix_do_apply_dentry);
89 #endif
90 }
91
92
93 /* Apply timestamps to an extracted file or directory */
94 static int
95 apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
96 {
97 #ifdef __WIN32__
98         return do_apply_op(dentry, arg, win32_do_apply_dentry_timestamps);
99 #else
100         return do_apply_op(dentry, arg, unix_do_apply_dentry_timestamps);
101 #endif
102 }
103
104 static bool
105 dentry_is_descendent(const struct wim_dentry *dentry,
106                      const struct wim_dentry *ancestor)
107 {
108         for (;;) {
109                 if (dentry == ancestor)
110                         return true;
111                 if (dentry_is_root(dentry))
112                         return false;
113                 dentry = dentry->parent;
114         }
115 }
116
117 /* Extract a dentry if it hasn't already been extracted and either
118  * WIMLIB_EXTRACT_FLAG_NO_STREAMS is not specified, or the dentry is a directory
119  * and/or has no unnamed stream. */
120 static int
121 maybe_apply_dentry(struct wim_dentry *dentry, void *arg)
122 {
123         struct apply_args *args = arg;
124         int ret;
125
126         if (dentry->is_extracted)
127                 return 0;
128
129         if (!dentry_is_descendent(dentry, args->extract_root))
130                 return 0;
131
132         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS &&
133             !dentry_is_directory(dentry) &&
134             inode_unnamed_lte_resolved(dentry->d_inode) != NULL)
135                 return 0;
136
137         if ((args->extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
138              args->progress_func) {
139                 args->progress.extract.cur_path = dentry->_full_path;
140                 args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
141                                     &args->progress);
142         }
143         ret = args->apply_dentry(dentry, args);
144         if (ret == 0)
145                 dentry->is_extracted = 1;
146         return ret;
147 }
148
149 static void
150 calculate_bytes_to_extract(struct list_head *stream_list,
151                            int extract_flags,
152                            union wimlib_progress_info *progress)
153 {
154         struct wim_lookup_table_entry *lte;
155         u64 total_bytes = 0;
156         u64 num_streams = 0;
157
158         /* For each stream to be extracted... */
159         list_for_each_entry(lte, stream_list, extraction_list) {
160                 if (extract_flags &
161                     (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
162                 {
163                         /* In the symlink or hard link extraction mode, each
164                          * stream will be extracted one time regardless of how
165                          * many dentries share the stream. */
166                         wimlib_assert(!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS));
167                         if (!lte->extracted_file) {
168                                 num_streams++;
169                                 total_bytes += wim_resource_size(lte);
170                         }
171                 } else {
172                         num_streams += lte->out_refcnt;
173                         total_bytes += lte->out_refcnt * wim_resource_size(lte);
174                 }
175         }
176         progress->extract.num_streams = num_streams;
177         progress->extract.total_bytes = total_bytes;
178         progress->extract.completed_bytes = 0;
179 }
180
181 static void
182 maybe_add_stream_for_extraction(struct wim_lookup_table_entry *lte,
183                                 struct list_head *stream_list)
184 {
185         if (++lte->out_refcnt == 1) {
186                 INIT_LIST_HEAD(&lte->inode_list);
187                 list_add_tail(&lte->extraction_list, stream_list);
188         }
189 }
190
191 static void
192 inode_find_streams_for_extraction(struct wim_inode *inode,
193                                   struct list_head *stream_list,
194                                   int extract_flags)
195 {
196         struct wim_lookup_table_entry *lte;
197         bool inode_added = false;
198
199         lte = inode_unnamed_lte_resolved(inode);
200         if (lte) {
201                 maybe_add_stream_for_extraction(lte, stream_list);
202                 list_add_tail(&inode->i_lte_inode_list, &lte->inode_list);
203                 inode_added = true;
204         }
205
206         /* Determine whether to include alternate data stream entries or not.
207          *
208          * UNIX:  Include them if extracting using NTFS-3g.
209          *
210          * Windows: Include them undconditionally, although if the filesystem is
211          * not NTFS we won't actually be able to extract them. */
212 #if defined(WITH_NTFS_3G)
213         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
214 #elif defined(__WIN32__)
215         if (1)
216 #else
217         if (0)
218 #endif
219         {
220                 for (unsigned i = 0; i < inode->i_num_ads; i++) {
221                         if (inode->i_ads_entries[i].stream_name_nbytes != 0) {
222                                 lte = inode->i_ads_entries[i].lte;
223                                 if (lte) {
224                                         maybe_add_stream_for_extraction(lte,
225                                                                         stream_list);
226                                         if (!inode_added) {
227                                                 list_add_tail(&inode->i_lte_inode_list,
228                                                               &lte->inode_list);
229                                                 inode_added = true;
230                                         }
231                                 }
232                         }
233                 }
234         }
235 }
236
237 struct find_streams_ctx {
238         struct list_head stream_list;
239         int extract_flags;
240 };
241
242 static int
243 dentry_find_streams_to_extract(struct wim_dentry *dentry, void *_ctx)
244 {
245         struct find_streams_ctx *ctx = _ctx;
246         struct wim_inode *inode = dentry->d_inode;
247
248         dentry->is_extracted = 0;
249         if (!inode->i_visited) {
250                 inode_find_streams_for_extraction(inode, &ctx->stream_list,
251                                                   ctx->extract_flags);
252                 inode->i_visited = 1;
253         }
254         return 0;
255 }
256
257 static int
258 dentry_resolve_and_zero_lte_refcnt(struct wim_dentry *dentry, void *_lookup_table)
259 {
260         struct wim_inode *inode = dentry->d_inode;
261         struct wim_lookup_table *lookup_table = _lookup_table;
262         struct wim_lookup_table_entry *lte;
263
264         inode_resolve_ltes(inode, lookup_table);
265         for (unsigned i = 0; i <= inode->i_num_ads; i++) {
266                 lte = inode_stream_lte_resolved(inode, i);
267                 if (lte)
268                         lte->out_refcnt = 0;
269         }
270         return 0;
271 }
272
273 static void
274 find_streams_for_extraction(struct wim_dentry *root,
275                             struct list_head *stream_list,
276                             struct wim_lookup_table *lookup_table,
277                             int extract_flags)
278 {
279         struct find_streams_ctx ctx;
280
281         INIT_LIST_HEAD(&ctx.stream_list);
282         ctx.extract_flags = extract_flags;
283         for_dentry_in_tree(root, dentry_resolve_and_zero_lte_refcnt, lookup_table);
284         for_dentry_in_tree(root, dentry_find_streams_to_extract, &ctx);
285         list_transfer(&ctx.stream_list, stream_list);
286 }
287
288 static int
289 dentry_mark_inode_unvisited(struct wim_dentry *dentry, void *_ignore)
290 {
291         dentry->d_inode->i_visited = 0;
292         return 0;
293 }
294
295 struct apply_operations {
296         int (*apply_dentry)(struct wim_dentry *dentry, void *arg);
297         int (*apply_dentry_timestamps)(struct wim_dentry *dentry, void *arg);
298 };
299
300 static const struct apply_operations normal_apply_operations = {
301         .apply_dentry = apply_dentry_normal,
302         .apply_dentry_timestamps = apply_dentry_timestamps_normal,
303 };
304
305 #ifdef WITH_NTFS_3G
306 static const struct apply_operations ntfs_apply_operations = {
307         .apply_dentry = apply_dentry_ntfs,
308         .apply_dentry_timestamps = apply_dentry_timestamps_ntfs,
309 };
310 #endif
311
312 static int
313 apply_stream_list(struct list_head *stream_list,
314                   struct apply_args *args,
315                   const struct apply_operations *ops,
316                   wimlib_progress_func_t progress_func)
317 {
318         uint64_t bytes_per_progress = args->progress.extract.total_bytes / 100;
319         uint64_t next_progress = bytes_per_progress;
320         struct wim_lookup_table_entry *lte;
321         struct wim_inode *inode;
322         struct wim_dentry *dentry;
323         int ret;
324
325         /* This complicated loop is essentially looping through the dentries,
326          * although dentries may be visited more than once (if a dentry contains
327          * two different nonempty streams) or not at all (if a dentry contains
328          * no non-empty streams).
329          *
330          * The outer loop is over the distinct streams to be extracted so that
331          * sequential reading of the WIM can be implemented. */
332
333         /* For each distinct stream to be extracted */
334         list_for_each_entry(lte, stream_list, extraction_list) {
335                 /* For each inode that contains the stream */
336                 list_for_each_entry(inode, &lte->inode_list, i_lte_inode_list) {
337                         /* For each dentry that points to the inode */
338                         inode_for_each_dentry(dentry, inode) {
339                                 /* Extract the dentry if it was not already
340                                  * extracted */
341                                 ret = maybe_apply_dentry(dentry, args);
342                                 if (ret)
343                                         return ret;
344                                 if (progress_func &&
345                                     args->progress.extract.completed_bytes >= next_progress)
346                                 {
347                                         progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS,
348                                                       &args->progress);
349                                         if (args->progress.extract.completed_bytes >=
350                                             args->progress.extract.total_bytes)
351                                         {
352                                                 next_progress = ~0ULL;
353                                         } else {
354                                                 next_progress =
355                                                         min (args->progress.extract.completed_bytes +
356                                                              bytes_per_progress,
357                                                              args->progress.extract.total_bytes);
358                                         }
359                                 }
360                         }
361                 }
362         }
363         return 0;
364 }
365
366 static int
367 sort_stream_list_by_wim_position(struct list_head *stream_list)
368 {
369         struct list_head *cur;
370         size_t num_streams;
371         struct wim_lookup_table_entry **array;
372         size_t i;
373         size_t array_size;
374
375         num_streams = 0;
376         list_for_each(cur, stream_list)
377                 num_streams++;
378         array_size = num_streams * sizeof(array[0]);
379         array = MALLOC(array_size);
380         if (!array) {
381                 ERROR("Failed to allocate %zu bytes to sort stream entries",
382                       array_size);
383                 return WIMLIB_ERR_NOMEM;
384         }
385         cur = stream_list->next;
386         for (i = 0; i < num_streams; i++) {
387                 array[i] = container_of(cur, struct wim_lookup_table_entry, extraction_list);
388                 cur = cur->next;
389         }
390
391         qsort(array, num_streams, sizeof(array[0]), cmp_streams_by_wim_position);
392
393         INIT_LIST_HEAD(stream_list);
394         for (i = 0; i < num_streams; i++)
395                 list_add_tail(&array[i]->extraction_list, stream_list);
396         FREE(array);
397         return 0;
398 }
399
400 /*
401  * Extract a dentry to standard output.
402  *
403  * This obviously doesn't make sense in all cases.  We return an error if the
404  * dentry does not correspond to a regular file.  Otherwise we extract the
405  * unnamed data stream only.
406  */
407 static int
408 extract_dentry_to_stdout(struct wim_dentry *dentry)
409 {
410         int ret = 0;
411         if (!dentry_is_regular_file(dentry)) {
412                 ERROR("\"%"TS"\" is not a regular file and therefore cannot be "
413                       "extracted to standard output", dentry->_full_path);
414                 ret = WIMLIB_ERR_NOT_A_REGULAR_FILE;
415         } else {
416                 struct wim_lookup_table_entry *lte;
417
418                 lte = inode_unnamed_lte_resolved(dentry->d_inode);
419                 if (lte) {
420                         ret = extract_wim_resource_to_fd(lte, STDOUT_FILENO,
421                                                          wim_resource_size(lte));
422                 }
423         }
424         return ret;
425 }
426
427 /*
428  * extract_tree - Extract a file or directory tree from the currently selected
429  *                WIM image.
430  *
431  * @wim:        WIMStruct for the WIM file, with the desired image selected
432  *              (as wim->current_image).
433  * @wim_source_path:
434  *              "Canonical" (i.e. no leading or trailing slashes, path
435  *              separators forwald slashes) path inside the WIM image to
436  *              extract.  An empty string means the full image.
437  * @target:
438  *              Filesystem path to extract the file or directory tree to.
439  *
440  * @extract_flags:
441  *              WIMLIB_EXTRACT_FLAG_*.  Also, the private flag
442  *              WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
443  *              called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
444  *              the image.
445  *
446  * @progress_func:
447  *              If non-NULL, progress function for the extraction.  The messages
448  *              we may in this function are:
449  *
450  *              WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
451  *                      WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
452  *              WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
453  *              WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
454  *              WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
455  *              WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
456  *              WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
457  *              WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
458  *                      WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
459  *
460  * Returns 0 on success; nonzero on failure.
461  */
462 static int
463 extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target,
464              int extract_flags, wimlib_progress_func_t progress_func)
465 {
466         int ret;
467         struct list_head stream_list;
468         struct apply_args args;
469         const struct apply_operations *ops;
470         struct wim_dentry *root;
471
472         memset(&args, 0, sizeof(args));
473
474         args.w                      = wim;
475         args.target                 = target;
476         args.extract_flags          = extract_flags;
477         args.progress_func          = progress_func;
478         args.target_nchars          = tstrlen(target);
479         args.wim_source_path_nchars = tstrlen(wim_source_path);
480
481         if (progress_func) {
482                 args.progress.extract.wimfile_name = wim->filename;
483                 args.progress.extract.image = wim->current_image;
484                 args.progress.extract.extract_flags = (extract_flags &
485                                                        WIMLIB_EXTRACT_MASK_PUBLIC);
486                 args.progress.extract.image_name = wimlib_get_image_name(wim,
487                                                                          wim->current_image);
488                 args.progress.extract.extract_root_wim_source_path = wim_source_path;
489                 args.progress.extract.target = target;
490         }
491
492 #ifdef WITH_NTFS_3G
493         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
494                 args.vol = ntfs_mount(target, 0);
495                 if (!args.vol) {
496                         ERROR_WITH_ERRNO("Failed to mount NTFS volume `%"TS"'",
497                                          target);
498                         ret = WIMLIB_ERR_NTFS_3G;
499                         goto out;
500                 }
501                 ops = &ntfs_apply_operations;
502         } else
503 #endif
504                 ops = &normal_apply_operations;
505
506         root = get_dentry(wim, wim_source_path);
507         if (!root) {
508                 ERROR("Path \"%"TS"\" does not exist in WIM image %d",
509                       wim_source_path, wim->current_image);
510                 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
511                 goto out_ntfs_umount;
512         }
513         args.extract_root = root;
514
515         ret = calculate_dentry_tree_full_paths(root);
516         if (ret)
517                 goto out_ntfs_umount;
518
519
520         /* Build a list of the streams that need to be extracted */
521         find_streams_for_extraction(root,
522                                     &stream_list,
523                                     wim->lookup_table, extract_flags);
524
525         /* Calculate the number of bytes of data that will be extracted */
526         calculate_bytes_to_extract(&stream_list, extract_flags,
527                                    &args.progress);
528
529         if (extract_flags & WIMLIB_EXTRACT_FLAG_TO_STDOUT) {
530                 ret = extract_dentry_to_stdout(root);
531                 goto out_mark_inodes_unvisited;
532         }
533
534         if (progress_func) {
535                 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN :
536                               WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
537                               &args.progress);
538         }
539
540         /* If a sequential extraction was specified, sort the streams to be
541          * extracted by their position in the WIM file, so that the WIM file can
542          * be read sequentially. */
543         if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
544                 ret = sort_stream_list_by_wim_position(&stream_list);
545                 if (ret != 0) {
546                         WARNING("Falling back to non-sequential extraction");
547                         extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
548                 }
549         }
550
551         if (progress_func) {
552                 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
553                               &args.progress);
554         }
555
556         /* Make the directory structure and extract empty files */
557         args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
558         args.apply_dentry = ops->apply_dentry;
559         ret = for_dentry_in_tree(root, maybe_apply_dentry, &args);
560         args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
561         if (ret)
562                 goto out_mark_inodes_unvisited;
563
564         if (progress_func) {
565                 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
566                               &args.progress);
567         }
568
569         if (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) {
570                 args.target_realpath = realpath(target, NULL);
571                 if (!args.target_realpath) {
572                         ret = WIMLIB_ERR_NOMEM;
573                         goto out_mark_inodes_unvisited;
574                 }
575                 args.target_realpath_len = tstrlen(args.target_realpath);
576         }
577
578         /* Extract non-empty files */
579         ret = apply_stream_list(&stream_list, &args, ops, progress_func);
580         if (ret)
581                 goto out_free_target_realpath;
582
583         if (progress_func) {
584                 progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
585                               &args.progress);
586         }
587
588         /* Apply timestamps */
589         ret = for_dentry_in_tree_depth(root,
590                                        ops->apply_dentry_timestamps, &args);
591         if (ret)
592                 goto out_free_target_realpath;
593
594         if (progress_func) {
595                 progress_func(*wim_source_path ? WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END :
596                               WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
597                               &args.progress);
598         }
599 out_free_target_realpath:
600         FREE(args.target_realpath);
601 out_mark_inodes_unvisited:
602         for_dentry_in_tree(root, dentry_mark_inode_unvisited, NULL);
603 out_ntfs_umount:
604 #ifdef WITH_NTFS_3G
605         /* Unmount the NTFS volume */
606         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
607                 if (ntfs_umount(args.vol, FALSE) != 0) {
608                         ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%"TS"'",
609                                          args.target);
610                         if (ret == 0)
611                                 ret = WIMLIB_ERR_NTFS_3G;
612                 }
613         }
614 #endif
615 out:
616         return ret;
617 }
618
619 /* Validates a single wimlib_extract_command, mostly checking to make sure the
620  * extract flags make sense. */
621 static int
622 check_extract_command(struct wimlib_extract_command *cmd, int wim_header_flags)
623 {
624         int extract_flags;
625         bool is_entire_image = (cmd->wim_source_path[0] == T('\0'));
626
627         /* Empty destination path? */
628         if (cmd->fs_dest_path[0] == T('\0'))
629                 return WIMLIB_ERR_INVALID_PARAM;
630
631         extract_flags = cmd->extract_flags;
632
633         /* Specified both symlink and hardlink modes? */
634         if ((extract_flags &
635              (WIMLIB_EXTRACT_FLAG_SYMLINK |
636               WIMLIB_EXTRACT_FLAG_HARDLINK)) == (WIMLIB_EXTRACT_FLAG_SYMLINK |
637                                                  WIMLIB_EXTRACT_FLAG_HARDLINK))
638                 return WIMLIB_ERR_INVALID_PARAM;
639
640 #ifdef __WIN32__
641         /* Wanted UNIX data on Win32? */
642         if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
643                 ERROR("Extracting UNIX data is not supported on Windows");
644                 return WIMLIB_ERR_INVALID_PARAM;
645         }
646         /* Wanted linked extraction on Windows?  (XXX This is possible, just not
647          * implemented yet.) */
648         if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
649                              WIMLIB_EXTRACT_FLAG_HARDLINK))
650         {
651                 ERROR("Linked extraction modes are not supported on Windows");
652                 return WIMLIB_ERR_INVALID_PARAM;
653         }
654 #endif
655
656         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
657                 /* NTFS-3g extraction mode requested */
658 #ifdef WITH_NTFS_3G
659                 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
660                                       WIMLIB_EXTRACT_FLAG_HARDLINK))) {
661                         ERROR("Cannot specify symlink or hardlink flags when applying\n"
662                               "        directly to a NTFS volume");
663                         return WIMLIB_ERR_INVALID_PARAM;
664                 }
665                 if (!is_entire_image &&
666                     (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS))
667                 {
668                         ERROR("When applying directly to a NTFS volume you can "
669                               "only extract a full image, not part of one");
670                         return WIMLIB_ERR_INVALID_PARAM;
671                 }
672                 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
673                         ERROR("Cannot restore UNIX-specific data in "
674                               "the NTFS extraction mode");
675                         return WIMLIB_ERR_INVALID_PARAM;
676                 }
677 #else
678                 ERROR("wimlib was compiled without support for NTFS-3g, so");
679                 ERROR("we cannot apply a WIM image directly to a NTFS volume");
680                 return WIMLIB_ERR_UNSUPPORTED;
681 #endif
682         }
683
684         if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
685                               WIMLIB_EXTRACT_FLAG_NORPFIX)) ==
686                 (WIMLIB_EXTRACT_FLAG_RPFIX | WIMLIB_EXTRACT_FLAG_NORPFIX))
687         {
688                 ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!");
689                 return WIMLIB_ERR_INVALID_PARAM;
690         }
691
692         if ((extract_flags & (WIMLIB_EXTRACT_FLAG_RPFIX |
693                               WIMLIB_EXTRACT_FLAG_NORPFIX)) == 0)
694         {
695                 /* Do reparse point fixups by default if the WIM header says
696                  * they are enabled and we are extracting a full image. */
697                 if ((wim_header_flags & WIM_HDR_FLAG_RP_FIX) && is_entire_image)
698                         extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
699         }
700
701         if (!is_entire_image && (extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)) {
702                 ERROR("Cannot specify --rpfix when not extracting entire image");
703                 return WIMLIB_ERR_INVALID_PARAM;
704         }
705
706         cmd->extract_flags = extract_flags;
707         return 0;
708 }
709
710
711 /* Internal function to execute extraction commands for a WIM image. */
712 static int
713 do_wimlib_extract_files(WIMStruct *wim,
714                         int image,
715                         struct wimlib_extract_command *cmds,
716                         size_t num_cmds,
717                         wimlib_progress_func_t progress_func)
718 {
719         int ret;
720         bool found_link_cmd = false;
721         bool found_nolink_cmd = false;
722
723         /* Select the image from which we are extracting files */
724         ret = select_wim_image(wim, image);
725         if (ret)
726                 return ret;
727
728         /* Make sure there are no streams in the WIM that have not been
729          * checksummed yet. */
730         ret = wim_checksum_unhashed_streams(wim);
731         if (ret)
732                 return ret;
733
734         /* Check for problems with the extraction commands */
735         for (size_t i = 0; i < num_cmds; i++) {
736                 ret = check_extract_command(&cmds[i], wim->hdr.flags);
737                 if (ret)
738                         return ret;
739                 if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
740                                              WIMLIB_EXTRACT_FLAG_HARDLINK)) {
741                         found_link_cmd = true;
742                 } else {
743                         found_nolink_cmd = true;
744                 }
745                 if (found_link_cmd && found_nolink_cmd) {
746                         ERROR("Symlink or hardlink extraction mode must "
747                               "be set on all extraction commands");
748                         return WIMLIB_ERR_INVALID_PARAM;
749                 }
750         }
751
752         /* Execute the extraction commands */
753         for (size_t i = 0; i < num_cmds; i++) {
754                 ret = extract_tree(wim,
755                                    cmds[i].wim_source_path,
756                                    cmds[i].fs_dest_path,
757                                    cmds[i].extract_flags,
758                                    progress_func);
759                 if (ret)
760                         return ret;
761         }
762         return 0;
763 }
764
765 /* Extract files or directories from a WIM image. */
766 WIMLIBAPI int
767 wimlib_extract_files(WIMStruct *wim,
768                      int image,
769                      int default_extract_flags,
770                      const struct wimlib_extract_command *cmds,
771                      size_t num_cmds,
772                      WIMStruct **additional_swms,
773                      unsigned num_additional_swms,
774                      wimlib_progress_func_t progress_func)
775 {
776         int ret;
777         struct wimlib_extract_command *cmds_copy;
778         struct wim_lookup_table *wim_tab_save, *joined_tab;
779         int all_flags = 0;
780
781         default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
782
783         ret = verify_swm_set(wim, additional_swms, num_additional_swms);
784         if (ret)
785                 goto out;
786
787         if (num_cmds == 0)
788                 goto out;
789
790         if (num_additional_swms) {
791                 ret = new_joined_lookup_table(wim, additional_swms,
792                                               num_additional_swms,
793                                               &joined_tab);
794                 if (ret)
795                         goto out;
796                 wim_tab_save = wim->lookup_table;
797                 wim->lookup_table = joined_tab;
798         }
799
800         cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
801         if (!cmds_copy) {
802                 ret = WIMLIB_ERR_NOMEM;
803                 goto out_restore_lookup_table;
804         }
805
806         for (size_t i = 0; i < num_cmds; i++) {
807                 cmds_copy[i].extract_flags = (default_extract_flags |
808                                                  cmds[i].extract_flags)
809                                                 & WIMLIB_EXTRACT_MASK_PUBLIC;
810                 all_flags |= cmds_copy[i].extract_flags;
811
812                 cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
813                 if (!cmds_copy[i].wim_source_path) {
814                         ret = WIMLIB_ERR_NOMEM;
815                         goto out_free_cmds_copy;
816                 }
817
818                 cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
819                 if (!cmds_copy[i].fs_dest_path) {
820                         ret = WIMLIB_ERR_NOMEM;
821                         goto out_free_cmds_copy;
822                 }
823
824         }
825         ret = do_wimlib_extract_files(wim, image,
826                                       cmds_copy, num_cmds,
827                                       progress_func);
828
829         if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
830                          WIMLIB_EXTRACT_FLAG_HARDLINK))
831         {
832                 for_lookup_table_entry(wim->lookup_table,
833                                        lte_free_extracted_file, NULL);
834         }
835 out_free_cmds_copy:
836         for (size_t i = 0; i < num_cmds; i++) {
837                 FREE(cmds_copy[i].wim_source_path);
838                 FREE(cmds_copy[i].fs_dest_path);
839         }
840         FREE(cmds_copy);
841 out_restore_lookup_table:
842         if (num_additional_swms) {
843                 free_lookup_table(wim->lookup_table);
844                 wim->lookup_table = wim_tab_save;
845         }
846 out:
847         return ret;
848 }
849
850 /*
851  * Extracts an image from a WIM file.
852  *
853  * @wim:                WIMStruct for the WIM file.
854  *
855  * @image:              Number of the single image to extract.
856  *
857  * @target:             Directory or NTFS volume to extract the image to.
858  *
859  * @extract_flags:      Bitwise or of WIMLIB_EXTRACT_FLAG_*.
860  *
861  * @progress_func:      If non-NULL, a progress function to be called
862  *                      periodically.
863  *
864  * Returns 0 on success; nonzero on failure.
865  */
866 static int
867 extract_single_image(WIMStruct *wim, int image,
868                      const tchar *target, int extract_flags,
869                      wimlib_progress_func_t progress_func)
870 {
871         int ret;
872         tchar *target_copy = canonicalize_fs_path(target);
873         if (!target_copy)
874                 return WIMLIB_ERR_NOMEM;
875         struct wimlib_extract_command cmd = {
876                 .wim_source_path = T(""),
877                 .fs_dest_path = target_copy,
878                 .extract_flags = extract_flags,
879         };
880         ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
881         FREE(target_copy);
882         return ret;
883 }
884
885 static const tchar * const filename_forbidden_chars =
886 T(
887 #ifdef __WIN32__
888 "<>:\"/\\|?*"
889 #else
890 "/"
891 #endif
892 );
893
894 /* This function checks if it is okay to use a WIM image's name as a directory
895  * name.  */
896 static bool
897 image_name_ok_as_dir(const tchar *image_name)
898 {
899         return image_name && *image_name &&
900                 !tstrpbrk(image_name, filename_forbidden_chars);
901 }
902
903 /* Extracts all images from the WIM to the directory @target, with the images
904  * placed in subdirectories named by their image names. */
905 static int
906 extract_all_images(WIMStruct *wim,
907                    const tchar *target,
908                    int extract_flags,
909                    wimlib_progress_func_t progress_func)
910 {
911         size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
912         size_t output_path_len = tstrlen(target);
913         tchar buf[output_path_len + 1 + image_name_max_len + 1];
914         int ret;
915         int image;
916         const tchar *image_name;
917         struct stat stbuf;
918
919         if (tstat(target, &stbuf)) {
920                 if (errno == ENOENT)
921                 {
922                         if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP |
923                                            S_IROTH | S_IXOTH))
924                         {
925                                 ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
926                                 return WIMLIB_ERR_MKDIR;
927                         }
928                 } else {
929                         ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
930                         return WIMLIB_ERR_STAT;
931                 }
932         } else if (!S_ISDIR(stbuf.st_mode)) {
933                 ERROR("\"%"TS"\" is not a directory", target);
934                 return WIMLIB_ERR_NOTDIR;
935         }
936
937         tmemcpy(buf, target, output_path_len);
938         buf[output_path_len] = T('/');
939         for (image = 1; image <= wim->hdr.image_count; image++) {
940                 image_name = wimlib_get_image_name(wim, image);
941                 if (image_name_ok_as_dir(image_name)) {
942                         tstrcpy(buf + output_path_len + 1, image_name);
943                 } else {
944                         /* Image name is empty or contains forbidden characters.
945                          * Use image number instead. */
946                         tsprintf(buf + output_path_len + 1, T("%d"), image);
947                 }
948                 ret = extract_single_image(wim, image, buf, extract_flags,
949                                            progress_func);
950                 if (ret)
951                         return ret;
952         }
953         return 0;
954 }
955
956 /* Extracts a single image or all images from a WIM file to a directory or NTFS
957  * volume. */
958 WIMLIBAPI int
959 wimlib_extract_image(WIMStruct *wim,
960                      int image,
961                      const tchar *target,
962                      int extract_flags,
963                      WIMStruct **additional_swms,
964                      unsigned num_additional_swms,
965                      wimlib_progress_func_t progress_func)
966 {
967         struct wim_lookup_table *joined_tab, *wim_tab_save;
968         int ret;
969
970         extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
971
972         ret = verify_swm_set(wim, additional_swms, num_additional_swms);
973         if (ret)
974                 return ret;
975
976         if (num_additional_swms) {
977                 ret = new_joined_lookup_table(wim, additional_swms,
978                                               num_additional_swms, &joined_tab);
979                 if (ret)
980                         return ret;
981                 wim_tab_save = wim->lookup_table;
982                 wim->lookup_table = joined_tab;
983         }
984
985         if (image == WIMLIB_ALL_IMAGES) {
986                 extract_flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE;
987                 ret = extract_all_images(wim, target, extract_flags,
988                                          progress_func);
989         } else {
990                 extract_flags &= ~WIMLIB_EXTRACT_FLAG_MULTI_IMAGE;
991                 ret = extract_single_image(wim, image, target, extract_flags,
992                                            progress_func);
993         }
994
995         if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
996                              WIMLIB_EXTRACT_FLAG_HARDLINK))
997         {
998                 for_lookup_table_entry(wim->lookup_table,
999                                        lte_free_extracted_file,
1000                                        NULL);
1001         }
1002         if (num_additional_swms) {
1003                 free_lookup_table(wim->lookup_table);
1004                 wim->lookup_table = wim_tab_save;
1005         }
1006         return ret;
1007 }