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