dentry_find_streams_to_extract(): Handle hard links correctly
[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         struct wim_lookup_table *wim_tab_save, *joined_tab;
750         int all_flags = 0;
751
752         default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
753
754         ret = verify_swm_set(wim, additional_swms, num_additional_swms);
755         if (ret)
756                 goto out;
757
758         if (num_cmds == 0)
759                 goto out;
760
761         if (num_additional_swms) {
762                 ret = new_joined_lookup_table(wim, additional_swms,
763                                               num_additional_swms,
764                                               &joined_tab);
765                 if (ret)
766                         goto out;
767                 wim_tab_save = wim->lookup_table;
768                 wim->lookup_table = joined_tab;
769         }
770
771         cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
772         if (!cmds_copy) {
773                 ret = WIMLIB_ERR_NOMEM;
774                 goto out_restore_lookup_table;
775         }
776
777         for (size_t i = 0; i < num_cmds; i++) {
778                 cmds_copy[i].extract_flags = (default_extract_flags |
779                                                  cmds[i].extract_flags)
780                                                 & WIMLIB_EXTRACT_MASK_PUBLIC;
781                 all_flags |= cmds_copy[i].extract_flags;
782
783                 cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
784                 if (!cmds_copy[i].wim_source_path) {
785                         ret = WIMLIB_ERR_NOMEM;
786                         goto out_free_cmds_copy;
787                 }
788
789                 cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
790                 if (!cmds_copy[i].fs_dest_path) {
791                         ret = WIMLIB_ERR_NOMEM;
792                         goto out_free_cmds_copy;
793                 }
794
795         }
796         ret = do_wimlib_extract_files(wim, image,
797                                       cmds_copy, num_cmds,
798                                       progress_func);
799
800         if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
801                          WIMLIB_EXTRACT_FLAG_HARDLINK))
802         {
803                 for_lookup_table_entry(wim->lookup_table,
804                                        lte_free_extracted_file, NULL);
805         }
806 out_free_cmds_copy:
807         for (size_t i = 0; i < num_cmds; i++) {
808                 FREE(cmds_copy[i].wim_source_path);
809                 FREE(cmds_copy[i].fs_dest_path);
810         }
811         FREE(cmds_copy);
812 out_restore_lookup_table:
813         if (num_additional_swms) {
814                 free_lookup_table(wim->lookup_table);
815                 wim->lookup_table = wim_tab_save;
816         }
817 out:
818         return ret;
819 }
820
821 /*
822  * Extracts an image from a WIM file.
823  *
824  * @wim:                WIMStruct for the WIM file.
825  *
826  * @image:              Number of the single image to extract.
827  *
828  * @target:             Directory or NTFS volume to extract the image to.
829  *
830  * @extract_flags:      Bitwise or of WIMLIB_EXTRACT_FLAG_*.
831  *
832  * @progress_func:      If non-NULL, a progress function to be called
833  *                      periodically.
834  *
835  * Returns 0 on success; nonzero on failure.
836  */
837 static int
838 extract_single_image(WIMStruct *wim, int image,
839                      const tchar *target, int extract_flags,
840                      wimlib_progress_func_t progress_func)
841 {
842         int ret;
843         tchar *target_copy = canonicalize_fs_path(target);
844         if (!target_copy)
845                 return WIMLIB_ERR_NOMEM;
846         struct wimlib_extract_command cmd = {
847                 .wim_source_path = T(""),
848                 .fs_dest_path = target_copy,
849                 .extract_flags = extract_flags,
850         };
851         ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
852         FREE(target_copy);
853         return ret;
854 }
855
856 static const tchar * const filename_forbidden_chars =
857 T(
858 #ifdef __WIN32__
859 "<>:\"/\\|?*"
860 #else
861 "/"
862 #endif
863 );
864
865 /* This function checks if it is okay to use a WIM image's name as a directory
866  * name.  */
867 static bool
868 image_name_ok_as_dir(const tchar *image_name)
869 {
870         return image_name && *image_name &&
871                 !tstrpbrk(image_name, filename_forbidden_chars) &&
872                 tstrcmp(image_name, T(".")) &&
873                 tstrcmp(image_name, T(".."));
874 }
875
876 /* Extracts all images from the WIM to the directory @target, with the images
877  * placed in subdirectories named by their image names. */
878 static int
879 extract_all_images(WIMStruct *wim,
880                    const tchar *target,
881                    int extract_flags,
882                    wimlib_progress_func_t progress_func)
883 {
884         size_t image_name_max_len = max(xml_get_max_image_name_len(wim), 20);
885         size_t output_path_len = tstrlen(target);
886         tchar buf[output_path_len + 1 + image_name_max_len + 1];
887         int ret;
888         int image;
889         const tchar *image_name;
890         struct stat stbuf;
891
892         if (tstat(target, &stbuf)) {
893                 if (errno == ENOENT)
894                 {
895                         if (tmkdir(target, S_IRWXU | S_IRGRP | S_IXGRP |
896                                            S_IROTH | S_IXOTH))
897                         {
898                                 ERROR_WITH_ERRNO("Failed to create directory \"%"TS"\"", target);
899                                 return WIMLIB_ERR_MKDIR;
900                         }
901                 } else {
902                         ERROR_WITH_ERRNO("Failed to stat \"%"TS"\"", target);
903                         return WIMLIB_ERR_STAT;
904                 }
905         } else if (!S_ISDIR(stbuf.st_mode)) {
906                 ERROR("\"%"TS"\" is not a directory", target);
907                 return WIMLIB_ERR_NOTDIR;
908         }
909
910         tmemcpy(buf, target, output_path_len);
911         buf[output_path_len] = T('/');
912         for (image = 1; image <= wim->hdr.image_count; image++) {
913                 image_name = wimlib_get_image_name(wim, image);
914                 if (image_name_ok_as_dir(image_name)) {
915                         tstrcpy(buf + output_path_len + 1, image_name);
916                 } else {
917                         /* Image name is empty or contains forbidden characters.
918                          * Use image number instead. */
919                         tsprintf(buf + output_path_len + 1, T("%d"), image);
920                 }
921                 ret = extract_single_image(wim, image, buf, extract_flags,
922                                            progress_func);
923                 if (ret)
924                         return ret;
925         }
926         return 0;
927 }
928
929 /* Extracts a single image or all images from a WIM file to a directory or NTFS
930  * volume. */
931 WIMLIBAPI int
932 wimlib_extract_image(WIMStruct *wim,
933                      int image,
934                      const tchar *target,
935                      int extract_flags,
936                      WIMStruct **additional_swms,
937                      unsigned num_additional_swms,
938                      wimlib_progress_func_t progress_func)
939 {
940         struct wim_lookup_table *joined_tab, *wim_tab_save;
941         int ret;
942
943         extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
944
945         ret = verify_swm_set(wim, additional_swms, num_additional_swms);
946         if (ret)
947                 return ret;
948
949         if (num_additional_swms) {
950                 ret = new_joined_lookup_table(wim, additional_swms,
951                                               num_additional_swms, &joined_tab);
952                 if (ret)
953                         return ret;
954                 wim_tab_save = wim->lookup_table;
955                 wim->lookup_table = joined_tab;
956         }
957
958         if (image == WIMLIB_ALL_IMAGES) {
959                 ret = extract_all_images(wim, target,
960                                          extract_flags | WIMLIB_EXTRACT_FLAG_MULTI_IMAGE,
961                                          progress_func);
962         } else {
963                 ret = extract_single_image(wim, image, target, extract_flags,
964                                            progress_func);
965         }
966
967         if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
968                              WIMLIB_EXTRACT_FLAG_HARDLINK))
969         {
970                 for_lookup_table_entry(wim->lookup_table,
971                                        lte_free_extracted_file,
972                                        NULL);
973         }
974         if (num_additional_swms) {
975                 free_lookup_table(wim->lookup_table);
976                 wim->lookup_table = wim_tab_save;
977         }
978         return ret;
979 }