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