]> wimlib.net Git - wimlib/blob - src/extract_image.c
Win32 apply
[wimlib] / src / extract_image.c
1 /*
2  * extract_image.c
3  *
4  * Support for extracting WIM files.
5  *
6  * This code does NOT contain any filesystem-specific features.  In particular,
7  * security information (i.e. file permissions) and alternate data streams are
8  * ignored, except possibly to read an alternate data stream that contains
9  * symbolic link data.
10  */
11
12 /*
13  * Copyright (C) 2012, 2013 Eric Biggers
14  *
15  * This file is part of wimlib, a library for working with WIM files.
16  *
17  * wimlib is free software; you can redistribute it and/or modify it under the
18  * terms of the GNU General Public License as published by the Free
19  * Software Foundation; either version 3 of the License, or (at your option)
20  * any later version.
21  *
22  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
23  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
24  * A PARTICULAR PURPOSE. See the GNU General Public License for more
25  * details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with wimlib; if not, see http://www.gnu.org/licenses/.
29  */
30
31 #include "config.h"
32
33 #if defined(__CYGWIN__) || defined(__WIN32__)
34 #include <windows.h>
35 #       ifdef ERROR
36 #               undef ERROR
37 #       endif
38 #include <wchar.h>
39 #endif
40
41 #include <dirent.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <string.h>
45 #include <sys/stat.h>
46 #include <stdlib.h>
47 #include <sys/time.h>
48
49 #ifdef HAVE_UTIME_H
50 #include <utime.h>
51 #endif
52
53 #include <unistd.h>
54
55 #include "dentry.h"
56 #include "lookup_table.h"
57 #include "timestamp.h"
58 #include "wimlib_internal.h"
59 #include "xml.h"
60
61 #ifdef WITH_NTFS_3G
62 #include <ntfs-3g/volume.h>
63 #endif
64
65 #ifdef HAVE_ALLOCA_H
66 #include <alloca.h>
67 #else
68 #include <stdlib.h>
69 #endif
70
71 #if defined(__CYGWIN__) || defined(__WIN32__)
72
73 static int win32_set_reparse_data(HANDLE h,
74                                   u32 reparse_tag,
75                                   const struct wim_lookup_table_entry *lte,
76                                   const wchar_t *path,
77                                   const char *path_utf8)
78 {
79         int ret;
80         u8 *buf;
81         size_t len;
82         
83         if (!lte) {
84                 WARNING("\"%s\" is marked as a reparse point but had no reparse data",
85                         path_utf8);
86                 return 0;
87         }
88         len = wim_resource_size(lte);
89         if (len > 16 * 1024 - 8) {
90                 WARNING("\"%s\": reparse data too long!", path_utf8);
91                 return 0;
92         }
93         buf = alloca(len + 8);
94         ret = read_full_wim_resource(lte, buf + 8, 0);
95         if (ret)
96                 return ret;
97         *(u32*)(buf + 0) = reparse_tag;
98         *(u16*)(buf + 4) = len;
99         if (!DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, buf, len + 8,
100                              NULL, 0, NULL, NULL))
101         {
102                 DWORD err = GetLastError();
103                 ERROR("Failed to set reparse data on \"%s\"",
104                       path_utf8);
105                 win32_error(err);
106                 ret = WIMLIB_ERR_WRITE;
107         } else
108                 ret = 0;
109         return ret;
110 }
111
112
113 static int win32_extract_chunk(const u8 *buf, size_t len, u64 offset, void *arg)
114 {
115         HANDLE hStream = arg;
116
117         DWORD nbytes_written;
118         wimlib_assert(len <= 0xffffffff);
119
120         if (!WriteFile(hStream, buf, len, &nbytes_written, NULL) ||
121             nbytes_written != len)
122         {
123                 DWORD err = GetLastError();
124                 ERROR("WriteFile(): write error");
125                 win32_error(err);
126                 return WIMLIB_ERR_WRITE;
127         }
128         return 0;
129 }
130
131 static int do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte)
132 {
133         return extract_wim_resource(lte, wim_resource_size(lte),
134                                     win32_extract_chunk, hStream);
135 }
136
137 static int win32_extract_stream(const struct wim_inode *inode,
138                                 const wchar_t *path,
139                                 const wchar_t *stream_name_utf16,
140                                 struct wim_lookup_table_entry *lte,
141                                 const char *path_utf8)
142 {
143         wchar_t *stream_path;
144         HANDLE h;
145         int ret;
146         DWORD err;
147         DWORD creationDisposition = CREATE_ALWAYS;
148
149         if (stream_name_utf16) {
150                 size_t stream_path_nchars;
151                 size_t path_nchars = wcslen(path);
152                 size_t stream_name_nchars = wcslen(stream_name_utf16);
153
154                 stream_path_nchars = path_nchars + 1 + stream_name_nchars;
155
156                 stream_path = alloca((stream_path_nchars + 1) * sizeof(wchar_t));
157
158                 memcpy(stream_path, path, path_nchars * sizeof(wchar_t));
159                 stream_path[path_nchars] = L':';
160                 memcpy(&stream_path[path_nchars + 1], stream_name_utf16,
161                        stream_name_nchars * sizeof(wchar_t));
162                 stream_path[stream_path_nchars] = L'\0';
163
164                 /*wsprintf(stream_path, stream_path_nchars, L"%ls:%ls",*/
165                          /*path, stream_name_utf16);*/
166         } else {
167                 stream_path = (wchar_t*)path;
168                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
169                         if (!CreateDirectoryW(stream_path, NULL)) {
170                                 err = GetLastError();
171                                 if (err == ERROR_FILE_EXISTS &&
172                                     dentry_is_root(inode_first_dentry(inode)))
173                                 {
174                                         /* Already exists */
175                                         return 0;
176                                 }
177                                 ERROR("Failed to create directory \"%s\"", path_utf8);
178                                 win32_error(err);
179                                 ret = WIMLIB_ERR_MKDIR;
180                                 goto fail;
181                         }
182                         creationDisposition = OPEN_EXISTING;
183                 }
184         }
185
186         h = CreateFileW(stream_path,
187                         GENERIC_WRITE | WRITE_OWNER | WRITE_DAC,
188                         0,
189                         NULL,
190                         creationDisposition,
191                         FILE_FLAG_OPEN_REPARSE_POINT |
192                             FILE_FLAG_BACKUP_SEMANTICS |
193                             inode->i_attributes,
194                         NULL);
195         if (h == INVALID_HANDLE_VALUE) {
196                 err = GetLastError();
197                 if (err == ERROR_FILE_EXISTS &&
198                     dentry_is_root(inode_first_dentry(inode)))
199                 {
200                         /* Already exists */
201                         return 0;
202                 }
203                 ERROR("Failed to create \"%s\"", path_utf8);
204                 win32_error(err);
205                 ret = WIMLIB_ERR_OPEN;
206                 goto fail;
207         }
208
209         if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
210                 DEBUG("Setting reparse data on \"%s\"", path_utf8);
211                 ret = win32_set_reparse_data(h, inode->i_reparse_tag,
212                                              lte, path, path_utf8);
213                 if (ret)
214                         goto fail_close_handle;
215         } else {
216                 if (lte) {
217                         DEBUG("Extracting stream for \"%s\" (len = %zu)",
218                               path_utf8, wim_resource_size(lte));
219                         ret = do_win32_extract_stream(h, lte);
220                         if (ret)
221                                 goto fail_close_handle;
222                 }
223         }
224
225         if (!CloseHandle(h)) {
226                 err = GetLastError();
227                 ERROR("Failed to close \"%s\"", path_utf8);
228                 win32_error(err);
229                 ret = WIMLIB_ERR_WRITE;
230                 goto fail;
231         }
232         ret = 0;
233         goto out;
234 fail_close_handle:
235         CloseHandle(h);
236 fail:
237         ERROR("Error extracting %s", path_utf8);
238 out:
239         return ret;
240 }
241
242 static int win32_extract_streams(struct wim_inode *inode,
243                                  const wchar_t *path,
244                                  const char *path_utf8)
245 {
246         struct wim_lookup_table_entry *unnamed_lte;
247         int ret;
248
249         ret = win32_extract_stream(inode, path, NULL, unnamed_lte, path_utf8);
250         if (ret)
251                 goto out;
252
253         for (u16 i = 0; i < inode->i_num_ads; i++) {
254                 const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i];
255                 if (ads_entry->stream_name_len != 0) {
256                         ret = win32_extract_stream(inode,
257                                                    path,
258                                                    (const wchar_t*)ads_entry->stream_name,
259                                                    ads_entry->lte,
260                                                    path_utf8);
261                         if (ret)
262                                 goto out;
263                 }
264         }
265         ret = 0;
266 out:
267         return ret;
268 }
269
270 static int win32_set_security_data(const struct wim_inode *inode,
271                                    const wchar_t *path,
272                                    const struct wim_security_data *sd,
273                                    const char *path_utf8)
274 {
275         SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION |
276                                                    SACL_SECURITY_INFORMATION |
277                                                    OWNER_SECURITY_INFORMATION |
278                                                    GROUP_SECURITY_INFORMATION;
279         if (!SetFileSecurityW(path, securityInformation,
280                               (PSECURITY_DESCRIPTOR)sd->descriptors[inode->i_security_id]))
281         {
282                 DWORD err = GetLastError();
283                 ERROR("Can't set security descriptor on \"%s\"", path_utf8);
284                 win32_error(err);
285                 return WIMLIB_ERR_WRITE;
286         }
287         return 0;
288 }
289
290 #else
291 static int extract_regular_file_linked(struct wim_dentry *dentry,
292                                        const char *output_path,
293                                        struct apply_args *args,
294                                        struct wim_lookup_table_entry *lte)
295 {
296         /* This mode overrides the normal hard-link extraction and
297          * instead either symlinks or hardlinks *all* identical files in
298          * the WIM, even if they are in a different image (in the case
299          * of a multi-image extraction) */
300
301         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_HARDLINK) {
302                 if (link(lte->extracted_file, output_path) != 0) {
303                         ERROR_WITH_ERRNO("Failed to hard link "
304                                          "`%s' to `%s'",
305                                          output_path, lte->extracted_file);
306                         return WIMLIB_ERR_LINK;
307                 }
308         } else {
309                 int num_path_components;
310                 int num_output_dir_path_components;
311                 size_t extracted_file_len;
312                 char *p;
313                 const char *p2;
314                 size_t i;
315
316                 num_path_components =
317                         get_num_path_components(dentry->full_path_utf8) - 1;
318                 num_output_dir_path_components =
319                         get_num_path_components(args->target);
320
321                 if (args->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE) {
322                         num_path_components++;
323                         num_output_dir_path_components--;
324                 }
325                 extracted_file_len = strlen(lte->extracted_file);
326
327                 char buf[extracted_file_len + 3 * num_path_components + 1];
328                 p = &buf[0];
329
330                 for (i = 0; i < num_path_components; i++) {
331                         *p++ = '.';
332                         *p++ = '.';
333                         *p++ = '/';
334                 }
335                 p2 = lte->extracted_file;
336                 while (*p2 == '/')
337                         p2++;
338                 while (num_output_dir_path_components--)
339                         p2 = path_next_part(p2, NULL);
340                 strcpy(p, p2);
341                 if (symlink(buf, output_path) != 0) {
342                         ERROR_WITH_ERRNO("Failed to symlink `%s' to "
343                                          "`%s'",
344                                          buf, lte->extracted_file);
345                         return WIMLIB_ERR_LINK;
346                 }
347         }
348         return 0;
349 }
350
351 static int symlink_apply_unix_data(const char *link,
352                                    const struct wimlib_unix_data *unix_data)
353 {
354         if (lchown(link, unix_data->uid, unix_data->gid)) {
355                 if (errno == EPERM) {
356                         /* Ignore */
357                         WARNING_WITH_ERRNO("failed to set symlink UNIX owner/group");
358                 } else {
359                         ERROR_WITH_ERRNO("failed to set symlink UNIX owner/group");
360                         return WIMLIB_ERR_INVALID_DENTRY;
361                 }
362         }
363         return 0;
364 }
365
366 static int fd_apply_unix_data(int fd, const struct wimlib_unix_data *unix_data)
367 {
368         if (fchown(fd, unix_data->uid, unix_data->gid)) {
369                 if (errno == EPERM) {
370                         WARNING_WITH_ERRNO("failed to set file UNIX owner/group");
371                         /* Ignore? */
372                 } else {
373                         ERROR_WITH_ERRNO("failed to set file UNIX owner/group");
374                         return WIMLIB_ERR_INVALID_DENTRY;
375                 }
376         }
377
378         if (fchmod(fd, unix_data->mode)) {
379                 if (errno == EPERM) {
380                         WARNING_WITH_ERRNO("failed to set UNIX file mode");
381                         /* Ignore? */
382                 } else {
383                         ERROR_WITH_ERRNO("failed to set UNIX file mode");
384                         return WIMLIB_ERR_INVALID_DENTRY;
385                 }
386         }
387         return 0;
388 }
389
390 static int dir_apply_unix_data(const char *dir,
391                                const struct wimlib_unix_data *unix_data)
392 {
393         int dfd = open(dir, O_RDONLY);
394         int ret;
395         if (dfd >= 0) {
396                 ret = fd_apply_unix_data(dfd, unix_data);
397                 if (close(dfd)) {
398                         ERROR_WITH_ERRNO("can't close directory `%s'", dir);
399                         ret = WIMLIB_ERR_MKDIR;
400                 }
401         } else {
402                 ERROR_WITH_ERRNO("can't open directory `%s'", dir);
403                 ret = WIMLIB_ERR_MKDIR;
404         }
405         return ret;
406 }
407
408 static int extract_regular_file_unlinked(struct wim_dentry *dentry,
409                                          struct apply_args *args,
410                                          const char *output_path,
411                                          struct wim_lookup_table_entry *lte)
412 {
413         /* Normal mode of extraction.  Regular files and hard links are
414          * extracted in the way that they appear in the WIM. */
415
416         int out_fd;
417         int ret;
418         struct wim_inode *inode = dentry->d_inode;
419
420         if (!((args->extract_flags & WIMLIB_EXTRACT_FLAG_MULTI_IMAGE)
421                 && (args->extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
422                                      WIMLIB_EXTRACT_FLAG_HARDLINK))))
423         {
424                 /* If the dentry is part of a hard link set of at least 2
425                  * dentries and one of the other dentries has already been
426                  * extracted, make a hard link to the file corresponding to this
427                  * already-extracted directory.  Otherwise, extract the file and
428                  * set the inode->i_extracted_file field so that other dentries
429                  * in the hard link group can link to it. */
430                 if (inode->i_nlink > 1) {
431                         if (inode->i_extracted_file) {
432                                 DEBUG("Extracting hard link `%s' => `%s'",
433                                       output_path, inode->i_extracted_file);
434                                 if (link(inode->i_extracted_file, output_path) != 0) {
435                                         ERROR_WITH_ERRNO("Failed to hard link "
436                                                          "`%s' to `%s'",
437                                                          output_path,
438                                                          inode->i_extracted_file);
439                                         return WIMLIB_ERR_LINK;
440                                 }
441                                 return 0;
442                         }
443                         FREE(inode->i_extracted_file);
444                         inode->i_extracted_file = STRDUP(output_path);
445                         if (!inode->i_extracted_file) {
446                                 ERROR("Failed to allocate memory for filename");
447                                 return WIMLIB_ERR_NOMEM;
448                         }
449                 }
450         }
451
452         /* Extract the contents of the file to @output_path. */
453
454         out_fd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
455         if (out_fd == -1) {
456                 ERROR_WITH_ERRNO("Failed to open the file `%s' for writing",
457                                  output_path);
458                 return WIMLIB_ERR_OPEN;
459         }
460
461         if (!lte) {
462                 /* Empty file with no lookup table entry */
463                 DEBUG("Empty file `%s'.", output_path);
464                 ret = 0;
465                 goto out_extract_unix_data;
466         }
467
468         ret = extract_wim_resource_to_fd(lte, out_fd, wim_resource_size(lte));
469         if (ret != 0) {
470                 ERROR("Failed to extract resource to `%s'", output_path);
471                 goto out;
472         }
473
474 out_extract_unix_data:
475         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
476                 struct wimlib_unix_data unix_data;
477                 ret = inode_get_unix_data(inode, &unix_data, NULL);
478                 if (ret > 0)
479                         ;
480                 else if (ret < 0)
481                         ret = 0;
482                 else
483                         ret = fd_apply_unix_data(out_fd, &unix_data);
484                 if (ret != 0)
485                         goto out;
486         }
487         if (lte)
488                 args->progress.extract.completed_bytes += wim_resource_size(lte);
489 out:
490         if (close(out_fd) != 0) {
491                 ERROR_WITH_ERRNO("Failed to close file `%s'", output_path);
492                 if (ret == 0)
493                         ret = WIMLIB_ERR_WRITE;
494         }
495         return ret;
496 }
497
498 static int extract_regular_file(struct wim_dentry *dentry,
499                                 struct apply_args *args,
500                                 const char *output_path)
501 {
502         struct wim_lookup_table_entry *lte;
503         const struct wim_inode *inode = dentry->d_inode;
504
505         lte = inode_unnamed_lte_resolved(inode);
506
507         if (lte && (args->extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
508                                            WIMLIB_EXTRACT_FLAG_HARDLINK)))
509         {
510                 if (lte->extracted_file) {
511                         return extract_regular_file_linked(dentry, output_path, args, lte);
512                 } else {
513                         lte->extracted_file = STRDUP(output_path);
514                         if (!lte->extracted_file)
515                                 return WIMLIB_ERR_NOMEM;
516                 }
517         }
518         return extract_regular_file_unlinked(dentry, args, output_path, lte);
519 }
520
521 static int extract_symlink(struct wim_dentry *dentry,
522                            struct apply_args *args,
523                            const char *output_path)
524 {
525         char target[4096];
526         ssize_t ret = inode_readlink(dentry->d_inode, target,
527                                      sizeof(target), args->w, 0);
528         struct wim_lookup_table_entry *lte;
529
530         if (ret <= 0) {
531                 ERROR("Could not read the symbolic link from dentry `%s'",
532                       dentry->full_path_utf8);
533                 return WIMLIB_ERR_INVALID_DENTRY;
534         }
535         ret = symlink(target, output_path);
536         if (ret != 0) {
537                 ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'",
538                                  output_path, target);
539                 return WIMLIB_ERR_LINK;
540         }
541         lte = inode_unnamed_lte_resolved(dentry->d_inode);
542         wimlib_assert(lte != NULL);
543         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
544                 struct wimlib_unix_data unix_data;
545                 ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL);
546                 if (ret > 0)
547                         ;
548                 else if (ret < 0)
549                         ret = 0;
550                 else
551                         ret = symlink_apply_unix_data(output_path, &unix_data);
552                 if (ret != 0)
553                         return ret;
554         }
555         args->progress.extract.completed_bytes += wim_resource_size(lte);
556         return 0;
557 }
558
559 #endif /* !__CYGWIN__ && !__WIN32__ */
560
561 static int extract_directory(struct wim_dentry *dentry,
562                              const char *output_path, bool is_root)
563 {
564         int ret;
565         struct stat stbuf;
566
567         ret = stat(output_path, &stbuf);
568         if (ret == 0) {
569                 if (S_ISDIR(stbuf.st_mode)) {
570                         /*if (!is_root)*/
571                                 /*WARNING("`%s' already exists", output_path);*/
572                         goto dir_exists;
573                 } else {
574                         ERROR("`%s' is not a directory", output_path);
575                         return WIMLIB_ERR_MKDIR;
576                 }
577         } else {
578                 if (errno != ENOENT) {
579                         ERROR_WITH_ERRNO("Failed to stat `%s'", output_path);
580                         return WIMLIB_ERR_STAT;
581                 }
582         }
583         if (mkdir(output_path, S_IRWXU | S_IRGRP | S_IXGRP |
584                                S_IROTH | S_IXOTH) != 0) {
585                 ERROR_WITH_ERRNO("Cannot create directory `%s'",
586                                  output_path);
587                 return WIMLIB_ERR_MKDIR;
588         }
589 dir_exists:
590         ret = 0;
591 #if !defined(__CYGWIN__) && !defined(__WIN32__)
592         if (dentry) {
593                 struct wimlib_unix_data unix_data;
594                 ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL);
595                 if (ret > 0)
596                         ;
597                 else if (ret < 0)
598                         ret = 0;
599                 else
600                         ret = dir_apply_unix_data(output_path, &unix_data);
601         }
602 #endif
603         return ret;
604 }
605
606 /* Extracts a file, directory, or symbolic link from the WIM archive. */
607 static int apply_dentry_normal(struct wim_dentry *dentry, void *arg)
608 {
609         struct apply_args *args = arg;
610         struct wim_inode *inode = dentry->d_inode;
611         size_t len;
612         char *output_path;
613
614         len = strlen(args->target);
615         if (dentry_is_root(dentry)) {
616                 output_path = (char*)args->target;
617         } else {
618                 output_path = alloca(len + dentry->full_path_utf8_len + 1);
619                 memcpy(output_path, args->target, len);
620                 memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len);
621                 output_path[len + dentry->full_path_utf8_len] = '\0';
622                 len += dentry->full_path_utf8_len;
623         }
624
625 #if defined(__CYGWIN__) || defined(__WIN32__)
626         char *utf16_path;
627         size_t utf16_path_len;
628         DWORD err;
629         int ret;
630         ret = utf8_to_utf16(output_path, len, &utf16_path, &utf16_path_len);
631         if (ret)
632                 return ret;
633
634         if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) {
635                 /* Linked file, with another name already extracted */
636                 if (!CreateHardLinkW((const wchar_t*)inode->i_extracted_file,
637                                      (const wchar_t*)utf16_path,
638                                      NULL))
639                 {
640                         err = GetLastError();
641                         ERROR("Can't create hard link \"%s\"", output_path);
642                         ret = WIMLIB_ERR_LINK;
643                         win32_error(err);
644                         goto out_free_utf16_path;
645                 }
646                 ret = 0;
647                 goto out_free_utf16_path;
648         }
649         ret = win32_extract_streams(inode, (const wchar_t*)utf16_path,
650                                     output_path);
651         if (ret)
652                 goto out_free_utf16_path;
653
654         if (inode->i_security_id != -1) {
655                 DEBUG("Setting security descriptor %d on %s",
656                       inode->i_security_id, output_path);
657                 ret = win32_set_security_data(inode,
658                                               (const wchar_t*)utf16_path,
659                                               wim_const_security_data(args->w),
660                                               output_path);
661                 if (ret)
662                         goto out_free_utf16_path;
663         }
664
665         /*if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) {*/
666                 /*ret = win32_set_reparse_data(inode, path, output_path);*/
667                 /*if (ret)*/
668                         /*goto out_free_utf16_path;*/
669         /*}*/
670
671         if (inode->i_nlink > 1) {
672                 inode->i_extracted_file = utf16_path;
673                 utf16_path = NULL;
674         }
675         ret = 0;
676 out_free_utf16_path:
677         FREE(utf16_path);
678         return ret;
679 #else
680         if (inode_is_symlink(inode))
681                 return extract_symlink(dentry, args, output_path);
682         else if (inode_is_directory(inode))
683                 return extract_directory((args->extract_flags &
684                                            WIMLIB_EXTRACT_FLAG_UNIX_DATA) ? dentry : NULL,
685                                          output_path, false);
686         else
687                 return extract_regular_file(dentry, args, output_path);
688 #endif
689 }
690
691 /* Apply timestamps to an extracted file or directory */
692 static int apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
693 {
694         struct apply_args *args = arg;
695         size_t len = strlen(args->target);
696         char output_path[len + dentry->full_path_utf8_len + 1];
697         const struct wim_inode *inode = dentry->d_inode;
698         int ret;
699
700         memcpy(output_path, args->target, len);
701         memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len);
702         output_path[len + dentry->full_path_utf8_len] = '\0';
703
704 #if defined(__CYGWIN__) || defined(__WIN32__)
705         /* Win32 */
706         char *utf16_path;
707         size_t utf16_path_len;
708         DWORD err;
709         HANDLE h;
710         BOOL bret1, bret2;
711
712         ret = utf8_to_utf16(output_path, len + dentry->full_path_utf8_len,
713                             &utf16_path, &utf16_path_len);
714         if (ret)
715                 return ret;
716         h = CreateFile(utf16_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
717                        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
718                        NULL);
719
720         if (h == INVALID_HANDLE_VALUE)
721                 err = GetLastError();
722         FREE(utf16_path);
723         if (h == INVALID_HANDLE_VALUE)
724                 goto fail;
725
726         FILETIME creationTime = {.dwLowDateTime = dentry->d_inode->i_creation_time & 0xffffffff,
727                                  .dwHighDateTime = dentry->d_inode->i_creation_time >> 32};
728         FILETIME lastAccessTime = {.dwLowDateTime = dentry->d_inode->i_last_access_time & 0xffffffff,
729                                   .dwHighDateTime = dentry->d_inode->i_last_access_time >> 32};
730         FILETIME lastWriteTime = {.dwLowDateTime = dentry->d_inode->i_last_write_time & 0xffffffff,
731                                   .dwHighDateTime = dentry->d_inode->i_last_write_time >> 32};
732
733         if (!SetFileTime(h, &creationTime, &lastAccessTime, &lastWriteTime)) {
734                 err = GetLastError();
735                 CloseHandle(h);
736                 goto fail;
737         }
738         if (!CloseHandle(h)) {
739                 err = GetLastError();
740                 goto fail;
741         }
742         return 0;
743 fail:
744         err = GetLastError();
745         ERROR("Can't set timestamps on \"%s\"", output_path);
746         win32_error(err);
747         return WIMLIB_ERR_WRITE;
748 #else
749         /* UNIX */
750
751         /* Convert the WIM timestamps, which are accurate to 100 nanoseconds,
752          * into struct timeval's. */
753         struct timeval tv[2];
754         wim_timestamp_to_timeval(inode->i_last_access_time, &tv[0]);
755         wim_timestamp_to_timeval(inode->i_last_write_time, &tv[1]);
756         #ifdef HAVE_LUTIMES
757         ret = lutimes(output_path, tv);
758         #else
759         ret = -1;
760         errno = ENOSYS;
761         #endif
762         if (ret != 0) {
763                 #ifdef HAVE_UTIME
764                 if (errno == ENOSYS) {
765                         struct utimbuf buf;
766                         buf.actime = wim_timestamp_to_unix(inode->i_last_access_time);
767                         buf.modtime = wim_timestamp_to_unix(inode->i_last_write_time);
768                         if (utime(output_path, &buf) == 0)
769                                 return 0;
770                 }
771                 #endif
772                 if (errno != ENOSYS || args->num_lutimes_warnings < 10) {
773                         /*WARNING_WITH_ERRNO("Failed to set timestamp on file `%s',*/
774                                             /*output_path");*/
775                         args->num_lutimes_warnings++;
776                 }
777         }
778         return 0;
779 #endif
780 }
781
782 /* Extract a dentry if it hasn't already been extracted, and either the dentry
783  * has no streams or WIMLIB_EXTRACT_FLAG_NO_STREAMS is not specified. */
784 static int maybe_apply_dentry(struct wim_dentry *dentry, void *arg)
785 {
786         struct apply_args *args = arg;
787         int ret;
788
789         if (dentry->is_extracted)
790                 return 0;
791
792         if (args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_STREAMS)
793                 if (inode_unnamed_lte_resolved(dentry->d_inode))
794                         return 0;
795
796         if ((args->extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE) &&
797              args->progress_func) {
798                 args->progress.extract.cur_path = dentry->full_path_utf8;
799                 args->progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY,
800                                     &args->progress);
801         }
802         ret = args->apply_dentry(dentry, args);
803         if (ret == 0)
804                 dentry->is_extracted = 1;
805         return ret;
806 }
807
808 static int cmp_streams_by_wim_position(const void *p1, const void *p2)
809 {
810         const struct wim_lookup_table_entry *lte1, *lte2;
811         lte1 = *(const struct wim_lookup_table_entry**)p1;
812         lte2 = *(const struct wim_lookup_table_entry**)p2;
813         if (lte1->resource_entry.offset < lte2->resource_entry.offset)
814                 return -1;
815         else if (lte1->resource_entry.offset > lte2->resource_entry.offset)
816                 return 1;
817         else
818                 return 0;
819 }
820
821 static int sort_stream_list_by_wim_position(struct list_head *stream_list)
822 {
823         struct list_head *cur;
824         size_t num_streams;
825         struct wim_lookup_table_entry **array;
826         size_t i;
827         size_t array_size;
828
829         num_streams = 0;
830         list_for_each(cur, stream_list)
831                 num_streams++;
832         array_size = num_streams * sizeof(array[0]);
833         array = MALLOC(array_size);
834         if (!array) {
835                 ERROR("Failed to allocate %zu bytes to sort stream entries",
836                       array_size);
837                 return WIMLIB_ERR_NOMEM;
838         }
839         cur = stream_list->next;
840         for (i = 0; i < num_streams; i++) {
841                 array[i] = container_of(cur, struct wim_lookup_table_entry, staging_list);
842                 cur = cur->next;
843         }
844
845         qsort(array, num_streams, sizeof(array[0]), cmp_streams_by_wim_position);
846
847         INIT_LIST_HEAD(stream_list);
848         for (i = 0; i < num_streams; i++)
849                 list_add_tail(&array[i]->staging_list, stream_list);
850         FREE(array);
851         return 0;
852 }
853
854 static void calculate_bytes_to_extract(struct list_head *stream_list,
855                                        int extract_flags,
856                                        union wimlib_progress_info *progress)
857 {
858         struct wim_lookup_table_entry *lte;
859         u64 total_bytes = 0;
860         u64 num_streams = 0;
861
862         /* For each stream to be extracted... */
863         list_for_each_entry(lte, stream_list, staging_list) {
864                 if (extract_flags &
865                     (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
866                 {
867                         /* In the symlink or hard link extraction mode, each
868                          * stream will be extracted one time regardless of how
869                          * many dentries share the stream. */
870                         wimlib_assert(!(extract_flags & WIMLIB_EXTRACT_FLAG_NTFS));
871                         if (!lte->extracted_file) {
872                                 num_streams++;
873                                 total_bytes += wim_resource_size(lte);
874                         }
875                 } else {
876                         num_streams += lte->out_refcnt;
877                         total_bytes += lte->out_refcnt * wim_resource_size(lte);
878                 }
879         }
880         progress->extract.num_streams = num_streams;
881         progress->extract.total_bytes = total_bytes;
882         progress->extract.completed_bytes = 0;
883 }
884
885 static void maybe_add_stream_for_extraction(struct wim_lookup_table_entry *lte,
886                                             struct list_head *stream_list)
887 {
888         if (++lte->out_refcnt == 1) {
889                 INIT_LIST_HEAD(&lte->inode_list);
890                 list_add_tail(&lte->staging_list, stream_list);
891         }
892 }
893
894 static void inode_find_streams_for_extraction(struct wim_inode *inode,
895                                               struct list_head *stream_list,
896                                               int extract_flags)
897 {
898         struct wim_lookup_table_entry *lte;
899         bool inode_added = false;
900
901         lte = inode_unnamed_lte_resolved(inode);
902         if (lte) {
903                 maybe_add_stream_for_extraction(lte, stream_list);
904                 list_add_tail(&inode->i_lte_inode_list, &lte->inode_list);
905                 inode_added = true;
906         }
907 #ifdef WITH_NTFS_3G
908         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
909                 for (unsigned i = 0; i < inode->i_num_ads; i++) {
910                         if (inode->i_ads_entries[i].stream_name_len != 0) {
911                                 lte = inode->i_ads_entries[i].lte;
912                                 if (lte) {
913                                         maybe_add_stream_for_extraction(lte,
914                                                                         stream_list);
915                                         if (!inode_added) {
916                                                 list_add_tail(&inode->i_lte_inode_list,
917                                                               &lte->inode_list);
918                                                 inode_added = true;
919                                         }
920                                 }
921                         }
922                 }
923         }
924 #endif
925 }
926
927 static void find_streams_for_extraction(struct hlist_head *inode_list,
928                                         struct list_head *stream_list,
929                                         struct wim_lookup_table *lookup_table,
930                                         int extract_flags)
931 {
932         struct wim_inode *inode;
933         struct hlist_node *cur;
934         struct wim_dentry *dentry;
935
936         for_lookup_table_entry(lookup_table, lte_zero_out_refcnt, NULL);
937         INIT_LIST_HEAD(stream_list);
938         hlist_for_each_entry(inode, cur, inode_list, i_hlist) {
939                 if (!inode->i_resolved)
940                         inode_resolve_ltes(inode, lookup_table);
941                 inode_for_each_dentry(dentry, inode)
942                         dentry->is_extracted = 0;
943                 inode_find_streams_for_extraction(inode, stream_list,
944                                                   extract_flags);
945         }
946 }
947
948 struct apply_operations {
949         int (*apply_dentry)(struct wim_dentry *dentry, void *arg);
950         int (*apply_dentry_timestamps)(struct wim_dentry *dentry, void *arg);
951 };
952
953 static const struct apply_operations normal_apply_operations = {
954         .apply_dentry = apply_dentry_normal,
955         .apply_dentry_timestamps = apply_dentry_timestamps_normal,
956 };
957
958 #ifdef WITH_NTFS_3G
959 static const struct apply_operations ntfs_apply_operations = {
960         .apply_dentry = apply_dentry_ntfs,
961         .apply_dentry_timestamps = apply_dentry_timestamps_ntfs,
962 };
963 #endif
964
965 static int apply_stream_list(struct list_head *stream_list,
966                              struct apply_args *args,
967                              const struct apply_operations *ops,
968                              wimlib_progress_func_t progress_func)
969 {
970         uint64_t bytes_per_progress = args->progress.extract.total_bytes / 100;
971         uint64_t next_progress = bytes_per_progress;
972         struct wim_lookup_table_entry *lte;
973         struct wim_inode *inode;
974         struct wim_dentry *dentry;
975         int ret;
976
977         /* This complicated loop is essentially looping through the dentries,
978          * although dentries may be visited more than once (if a dentry contains
979          * two different nonempty streams) or not at all (if a dentry contains
980          * no non-empty streams).
981          *
982          * The outer loop is over the distinct streams to be extracted so that
983          * sequential reading of the WIM can be implemented. */
984
985         /* For each distinct stream to be extracted */
986         list_for_each_entry(lte, stream_list, staging_list) {
987                 /* For each inode that contains the stream */
988                 list_for_each_entry(inode, &lte->inode_list, i_lte_inode_list) {
989                         /* For each dentry that points to the inode */
990                         inode_for_each_dentry(dentry, inode) {
991                                 /* Extract the dentry if it was not already
992                                  * extracted */
993                                 ret = maybe_apply_dentry(dentry, args);
994                                 if (ret != 0)
995                                         return ret;
996                                 if (progress_func &&
997                                     args->progress.extract.completed_bytes >= next_progress)
998                                 {
999                                         progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS,
1000                                                       &args->progress);
1001                                         if (args->progress.extract.completed_bytes >=
1002                                             args->progress.extract.total_bytes)
1003                                         {
1004                                                 next_progress = ~0ULL;
1005                                         } else {
1006                                                 next_progress =
1007                                                         min (args->progress.extract.completed_bytes +
1008                                                              bytes_per_progress,
1009                                                              args->progress.extract.total_bytes);
1010                                         }
1011                                 }
1012                         }
1013                 }
1014         }
1015         return 0;
1016 }
1017
1018 /* Extracts the image @image from the WIM @w to the directory or NTFS volume
1019  * @target. */
1020 static int extract_single_image(WIMStruct *w, int image,
1021                                 const char *target, int extract_flags,
1022                                 wimlib_progress_func_t progress_func)
1023 {
1024         int ret;
1025         struct list_head stream_list;
1026         struct hlist_head *inode_list;
1027
1028         struct apply_args args;
1029         const struct apply_operations *ops;
1030
1031         args.w                    = w;
1032         args.target               = target;
1033         args.extract_flags        = extract_flags;
1034         args.num_lutimes_warnings = 0;
1035         args.stream_list          = &stream_list;
1036         args.progress_func        = progress_func;
1037
1038         if (progress_func) {
1039                 args.progress.extract.wimfile_name = w->filename;
1040                 args.progress.extract.image = image;
1041                 args.progress.extract.extract_flags = (extract_flags &
1042                                                        WIMLIB_EXTRACT_MASK_PUBLIC);
1043                 args.progress.extract.image_name = wimlib_get_image_name(w, image);
1044                 args.progress.extract.target = target;
1045         }
1046
1047 #ifdef WITH_NTFS_3G
1048         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
1049                 args.vol = ntfs_mount(target, 0);
1050                 if (!args.vol) {
1051                         ERROR_WITH_ERRNO("Failed to mount NTFS volume `%s'", target);
1052                         return WIMLIB_ERR_NTFS_3G;
1053                 }
1054                 ops = &ntfs_apply_operations;
1055         } else
1056 #endif
1057                 ops = &normal_apply_operations;
1058
1059         ret = select_wim_image(w, image);
1060         if (ret != 0)
1061                 goto out;
1062
1063         inode_list = &w->image_metadata[image - 1].inode_list;
1064
1065         /* Build a list of the streams that need to be extracted */
1066         find_streams_for_extraction(inode_list, &stream_list,
1067                                     w->lookup_table, extract_flags);
1068
1069         /* Calculate the number of bytes of data that will be extracted */
1070         calculate_bytes_to_extract(&stream_list, extract_flags,
1071                                    &args.progress);
1072
1073         if (progress_func) {
1074                 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN,
1075                               &args.progress);
1076         }
1077
1078         /* If a sequential extraction was specified, sort the streams to be
1079          * extracted by their position in the WIM file, so that the WIM file can
1080          * be read sequentially. */
1081         if (extract_flags & WIMLIB_EXTRACT_FLAG_SEQUENTIAL) {
1082                 ret = sort_stream_list_by_wim_position(&stream_list);
1083                 if (ret != 0) {
1084                         WARNING("Falling back to non-sequential extraction");
1085                         extract_flags &= ~WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
1086                 }
1087         }
1088
1089         if (progress_func) {
1090                 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN,
1091                               &args.progress);
1092         }
1093
1094         /* Make the directory structure and extract empty files */
1095         args.extract_flags |= WIMLIB_EXTRACT_FLAG_NO_STREAMS;
1096         args.apply_dentry = ops->apply_dentry;
1097         ret = for_dentry_in_tree(wim_root_dentry(w), maybe_apply_dentry, &args);
1098         args.extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_STREAMS;
1099         if (ret != 0)
1100                 goto out;
1101
1102         if (progress_func) {
1103                 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END,
1104                               &args.progress);
1105         }
1106
1107         /* Extract non-empty files */
1108         ret = apply_stream_list(&stream_list, &args, ops, progress_func);
1109         if (ret != 0)
1110                 goto out;
1111
1112         if (progress_func) {
1113                 progress_func(WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS,
1114                               &args.progress);
1115         }
1116
1117         /* Apply timestamps */
1118         ret = for_dentry_in_tree_depth(wim_root_dentry(w),
1119                                        ops->apply_dentry_timestamps, &args);
1120         if (ret != 0)
1121                 goto out;
1122
1123         if (progress_func) {
1124                 progress_func(WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END,
1125                               &args.progress);
1126         }
1127 out:
1128 #ifdef WITH_NTFS_3G
1129         /* Unmount the NTFS volume */
1130         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
1131                 if (ntfs_umount(args.vol, FALSE) != 0) {
1132                         ERROR_WITH_ERRNO("Failed to unmount NTFS volume `%s'", args.target);
1133                         if (ret == 0)
1134                                 ret = WIMLIB_ERR_NTFS_3G;
1135                 }
1136         }
1137 #endif
1138         return ret;
1139 }
1140
1141
1142 /* Extracts all images from the WIM to the directory @target, with the images
1143  * placed in subdirectories named by their image names. */
1144 static int extract_all_images(WIMStruct *w, const char *target,
1145                               int extract_flags,
1146                               wimlib_progress_func_t progress_func)
1147 {
1148         size_t image_name_max_len = max(xml_get_max_image_name_len(w), 20);
1149         size_t output_path_len = strlen(target);
1150         char buf[output_path_len + 1 + image_name_max_len + 1];
1151         int ret;
1152         int image;
1153         const char *image_name;
1154
1155         ret = extract_directory(NULL, target, true);
1156         if (ret != 0)
1157                 return ret;
1158
1159         memcpy(buf, target, output_path_len);
1160         buf[output_path_len] = '/';
1161         for (image = 1; image <= w->hdr.image_count; image++) {
1162                 image_name = wimlib_get_image_name(w, image);
1163                 if (image_name && *image_name) {
1164                         strcpy(buf + output_path_len + 1, image_name);
1165                 } else {
1166                         /* Image name is empty. Use image number instead */
1167                         sprintf(buf + output_path_len + 1, "%d", image);
1168                 }
1169                 ret = extract_single_image(w, image, buf, extract_flags,
1170                                            progress_func);
1171                 if (ret != 0)
1172                         return ret;
1173         }
1174         return 0;
1175 }
1176
1177 /* Extracts a single image or all images from a WIM file to a directory or NTFS
1178  * volume. */
1179 WIMLIBAPI int wimlib_extract_image(WIMStruct *w,
1180                                    int image,
1181                                    const char *target,
1182                                    int extract_flags,
1183                                    WIMStruct **additional_swms,
1184                                    unsigned num_additional_swms,
1185                                    wimlib_progress_func_t progress_func)
1186 {
1187         struct wim_lookup_table *joined_tab, *w_tab_save;
1188         int ret;
1189
1190         if (!target)
1191                 return WIMLIB_ERR_INVALID_PARAM;
1192
1193         extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
1194
1195         if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
1196                         == (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))
1197                 return WIMLIB_ERR_INVALID_PARAM;
1198
1199 #if defined(__CYGWIN__) || defined(__WIN32__)
1200         if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
1201                 ERROR("Extracting UNIX data is not supported on Windows");
1202                 return WIMLIB_ERR_INVALID_PARAM;
1203         }
1204         if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK)) {
1205                 ERROR("Linked extraction modes are not supported on Windows");
1206                 return WIMLIB_ERR_INVALID_PARAM;
1207         }
1208 #endif
1209
1210         if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
1211 #ifdef WITH_NTFS_3G
1212                 if ((extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK | WIMLIB_EXTRACT_FLAG_HARDLINK))) {
1213                         ERROR("Cannot specify symlink or hardlink flags when applying\n"
1214                               "        directly to a NTFS volume");
1215                         return WIMLIB_ERR_INVALID_PARAM;
1216                 }
1217                 if (image == WIMLIB_ALL_IMAGES) {
1218                         ERROR("Can only apply a single image when applying "
1219                               "directly to a NTFS volume");
1220                         return WIMLIB_ERR_INVALID_PARAM;
1221                 }
1222                 if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
1223                         ERROR("Cannot restore UNIX-specific data in the NTFS extraction mode");
1224                         return WIMLIB_ERR_INVALID_PARAM;
1225                 }
1226 #else
1227                 ERROR("wimlib was compiled without support for NTFS-3g, so");
1228                 ERROR("we cannot apply a WIM image directly to a NTFS volume");
1229                 return WIMLIB_ERR_UNSUPPORTED;
1230 #endif
1231         }
1232
1233         ret = verify_swm_set(w, additional_swms, num_additional_swms);
1234         if (ret != 0)
1235                 return ret;
1236
1237         if (num_additional_swms) {
1238                 ret = new_joined_lookup_table(w, additional_swms,
1239                                               num_additional_swms, &joined_tab);
1240                 if (ret != 0)
1241                         return ret;
1242                 w_tab_save = w->lookup_table;
1243                 w->lookup_table = joined_tab;
1244         }
1245
1246         if (image == WIMLIB_ALL_IMAGES) {
1247                 extract_flags |= WIMLIB_EXTRACT_FLAG_MULTI_IMAGE;
1248                 ret = extract_all_images(w, target, extract_flags,
1249                                          progress_func);
1250         } else {
1251                 extract_flags &= ~WIMLIB_EXTRACT_FLAG_MULTI_IMAGE;
1252                 ret = extract_single_image(w, image, target, extract_flags,
1253                                            progress_func);
1254         }
1255
1256         if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
1257                              WIMLIB_EXTRACT_FLAG_HARDLINK))
1258         {
1259                 for_lookup_table_entry(w->lookup_table,
1260                                        lte_free_extracted_file,
1261                                        NULL);
1262         }
1263
1264         if (num_additional_swms) {
1265                 free_lookup_table(w->lookup_table);
1266                 w->lookup_table = w_tab_save;
1267         }
1268         return ret;
1269 }