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