40dc9edf701a1a9285632f24504be666103055de
[wimlib] / src / unix_apply.c
1 /*
2  * unix_apply.c - Code to apply files from a WIM image on UNIX.
3  */
4
5 /*
6  * Copyright (C) 2012, 2013, 2014 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "wimlib/apply.h"
29 #include "wimlib/dentry.h"
30 #include "wimlib/error.h"
31 #include "wimlib/file_io.h"
32 #include "wimlib/reparse.h"
33 #include "wimlib/timestamp.h"
34 #include "wimlib/unix_data.h"
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <stdlib.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44
45 /* We don't require O_NOFOLLOW, but the advantage of having it is that if we
46  * need to extract a file to a location at which there exists a symbolic link,
47  * open(..., O_NOFOLLOW | ...) recognizes the symbolic link rather than
48  * following it and creating the file somewhere else.  (Equivalent to
49  * FILE_OPEN_REPARSE_POINT on Windows.)  */
50 #ifndef O_NOFOLLOW
51 #  define O_NOFOLLOW 0
52 #endif
53
54 static int
55 unix_get_supported_features(const char *target,
56                             struct wim_features *supported_features)
57 {
58         supported_features->hard_links = 1;
59         supported_features->symlink_reparse_points = 1;
60         supported_features->unix_data = 1;
61         supported_features->timestamps = 1;
62         supported_features->case_sensitive_filenames = 1;
63         return 0;
64 }
65
66 #define NUM_PATHBUFS 2  /* We need 2 when creating hard links  */
67 #define MAX_OPEN_FDS 1024 /* TODO: Add special case for when the number of
68                              identical streams exceeds this number.  */
69
70 struct unix_apply_ctx {
71         /* Extract flags, the pointer to the WIMStruct, etc.  */
72         struct apply_ctx common;
73
74         /* Buffers for building extraction paths (allocated).  */
75         char *pathbufs[NUM_PATHBUFS];
76
77         /* Index of next pathbuf to use  */
78         unsigned which_pathbuf;
79
80         /* Currently open file descriptors for extraction  */
81         struct filedes open_fds[MAX_OPEN_FDS];
82
83         /* Number of currently open file descriptors in open_fds, starting from
84          * the beginning of the array.  */
85         unsigned num_open_fds;
86
87         /* Buffer for reading reparse data streams into memory  */
88         u8 reparse_data[REPARSE_DATA_MAX_SIZE];
89
90         /* Pointer to the next byte in @reparse_data to fill  */
91         u8 *reparse_ptr;
92
93         /* Absolute path to the target directory (allocated buffer).  Only set
94          * if needed for absolute symbolic link fixups.  */
95         char *target_abspath;
96
97         /* Number of characters in target_abspath.  */
98         size_t target_abspath_nchars;
99 };
100
101 /* Returns the number of characters needed to represent the path to the
102  * specified @dentry when extracted, not including the null terminator or the
103  * path to the target directory itself.  */
104 static size_t
105 unix_dentry_path_length(const struct wim_dentry *dentry)
106 {
107         size_t len = 0;
108         const struct wim_dentry *d;
109
110         d = dentry;
111         do {
112                 len += d->d_extraction_name_nchars + 1;
113                 d = d->parent;
114         } while (!dentry_is_root(d) && will_extract_dentry(d));
115
116         return len;
117 }
118
119 /* Returns the maximum number of characters needed to represent the path to any
120  * dentry in @dentry_list when extracted, including the null terminator and the
121  * path to the target directory itself.  */
122 static size_t
123 unix_compute_path_max(const struct list_head *dentry_list,
124                       const struct unix_apply_ctx *ctx)
125 {
126         size_t max = 0;
127         size_t len;
128         const struct wim_dentry *dentry;
129
130         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
131                 len = unix_dentry_path_length(dentry);
132                 if (len > max)
133                         max = len;
134         }
135
136         /* Account for target and null terminator.  */
137         return ctx->common.target_nchars + max + 1;
138 }
139
140 /* Builds and returns the filesystem path to which to extract @dentry.
141  * This cycles through NUM_PATHBUFS different buffers.  */
142 static const char *
143 unix_build_extraction_path(const struct wim_dentry *dentry,
144                            struct unix_apply_ctx *ctx)
145 {
146         char *pathbuf;
147         char *p;
148         const struct wim_dentry *d;
149
150         pathbuf = ctx->pathbufs[ctx->which_pathbuf];
151         ctx->which_pathbuf = (ctx->which_pathbuf + 1) % NUM_PATHBUFS;
152
153         p = &pathbuf[ctx->common.target_nchars +
154                      unix_dentry_path_length(dentry)];
155         *p = '\0';
156         d = dentry;
157         do {
158                 p -= d->d_extraction_name_nchars;
159                 memcpy(p, d->d_extraction_name, d->d_extraction_name_nchars);
160                 *--p = '/';
161                 d = d->parent;
162         } while (!dentry_is_root(d) && will_extract_dentry(d));
163
164         return pathbuf;
165 }
166
167 /* This causes the next call to unix_build_extraction_path() to use the same
168  * path buffer as the previous call.  */
169 static void
170 unix_reuse_pathbuf(struct unix_apply_ctx *ctx)
171 {
172         ctx->which_pathbuf = (ctx->which_pathbuf - 1) % NUM_PATHBUFS;
173 }
174
175 /* Builds and returns the filesystem path to which to extract an unspecified
176  * alias of the @inode.  This cycles through NUM_PATHBUFS different buffers.  */
177 static const char *
178 unix_build_inode_extraction_path(const struct wim_inode *inode,
179                                  struct unix_apply_ctx *ctx)
180 {
181         return unix_build_extraction_path(inode_first_extraction_dentry(inode), ctx);
182 }
183
184 /* Sets the timestamps on a file being extracted.
185  *
186  * Either @fd or @path must be specified (not -1 and not NULL, respectively).
187  */
188 static int
189 unix_set_timestamps(int fd, const char *path, u64 atime, u64 mtime)
190 {
191         {
192                 struct timespec times[2];
193
194                 times[0] = wim_timestamp_to_timespec(atime);
195                 times[1] = wim_timestamp_to_timespec(mtime);
196
197                 errno = ENOSYS;
198 #ifdef HAVE_FUTIMENS
199                 if (fd >= 0 && !futimens(fd, times))
200                         return 0;
201 #endif
202 #ifdef HAVE_UTIMENSAT
203                 if (fd < 0 && !utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW))
204                         return 0;
205 #endif
206                 if (errno != ENOSYS)
207                         return WIMLIB_ERR_SET_TIMESTAMPS;
208         }
209         {
210                 struct timeval times[2];
211
212                 times[0] = wim_timestamp_to_timeval(atime);
213                 times[1] = wim_timestamp_to_timeval(mtime);
214
215                 if (fd >= 0 && !futimes(fd, times))
216                         return 0;
217                 if (fd < 0 && !lutimes(path, times))
218                         return 0;
219                 return WIMLIB_ERR_SET_TIMESTAMPS;
220         }
221 }
222
223 static int
224 unix_set_owner_and_group(int fd, const char *path, uid_t uid, gid_t gid)
225 {
226         if (fd >= 0 && !fchown(fd, uid, gid))
227                 return 0;
228         if (fd < 0 && !lchown(path, uid, gid))
229                 return 0;
230         return WIMLIB_ERR_SET_SECURITY;
231 }
232
233 static int
234 unix_set_mode(int fd, const char *path, mode_t mode)
235 {
236         if (fd >= 0 && !fchmod(fd, mode))
237                 return 0;
238         if (fd < 0 && !chmod(path, mode))
239                 return 0;
240         return WIMLIB_ERR_SET_SECURITY;
241 }
242
243 /*
244  * Set metadata on an extracted file.
245  *
246  * @fd is an open file descriptor to the extracted file, or -1.  @path is the
247  * path to the extracted file, or NULL.  If valid, this function uses @fd.
248  * Otherwise, if valid, it uses @path.  Otherwise, it calculates the path to one
249  * alias of the extracted file and uses it.
250  */
251 static int
252 unix_set_metadata(int fd, const struct wim_inode *inode,
253                   const char *path, struct unix_apply_ctx *ctx)
254 {
255         int ret;
256         struct wimlib_unix_data unix_data;
257
258         if (fd < 0 && !path)
259                 path = unix_build_inode_extraction_path(inode, ctx);
260
261         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA)
262             && inode_get_unix_data(inode, &unix_data))
263         {
264                 u32 uid = unix_data.uid;
265                 u32 gid = unix_data.gid;
266                 u32 mode = unix_data.mode;
267
268                 ret = unix_set_owner_and_group(fd, path, uid, gid);
269                 if (ret) {
270                         if (!path)
271                                 path = unix_build_inode_extraction_path(inode, ctx);
272                         if (ctx->common.extract_flags &
273                             WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
274                         {
275                                 ERROR_WITH_ERRNO("Can't set uid=%"PRIu32" and "
276                                                  "gid=%"PRIu32" on \"%s\"",
277                                                  uid, gid, path);
278                                 return ret;
279                         } else {
280                                 WARNING_WITH_ERRNO("Can't set uid=%"PRIu32" and "
281                                                    "gid=%"PRIu32" on \"%s\"",
282                                                    uid, gid, path);
283                         }
284                 }
285
286                 ret = 0;
287                 if (!inode_is_symlink(inode))
288                         ret = unix_set_mode(fd, path, mode);
289                 if (ret) {
290                         if (!path)
291                                 path = unix_build_inode_extraction_path(inode, ctx);
292                         if (ctx->common.extract_flags &
293                             WIMLIB_EXTRACT_FLAG_STRICT_ACLS)
294                         {
295                                 ERROR_WITH_ERRNO("Can't set mode=0%"PRIo32" "
296                                                  "on \"%s\"", mode, path);
297                                 return ret;
298                         } else {
299                                 WARNING_WITH_ERRNO("Can't set mode=0%"PRIo32" "
300                                                    "on \"%s\"", mode, path);
301                         }
302                 }
303         }
304
305         ret = unix_set_timestamps(fd, path,
306                                   inode->i_last_access_time,
307                                   inode->i_last_write_time);
308         if (ret) {
309                 if (!path)
310                         path = unix_build_inode_extraction_path(inode, ctx);
311                 if (ctx->common.extract_flags &
312                     WIMLIB_EXTRACT_FLAG_STRICT_TIMESTAMPS)
313                 {
314                         ERROR_WITH_ERRNO("Can't set timestamps on \"%s\"", path);
315                         return ret;
316                 } else {
317                         WARNING_WITH_ERRNO("Can't set timestamps on \"%s\"", path);
318                 }
319         }
320         return 0;
321 }
322
323 /* Extract all needed aliases of the @inode, where one alias, corresponding to
324  * @first_dentry, has already been extracted to @first_path.  */
325 static int
326 unix_create_hardlinks(const struct wim_inode *inode,
327                       const struct wim_dentry *first_dentry,
328                       const char *first_path, struct unix_apply_ctx *ctx)
329 {
330         const struct wim_dentry *dentry;
331         const char *newpath;
332
333         list_for_each_entry(dentry, &inode->i_extraction_aliases,
334                             d_extraction_alias_node)
335         {
336                 if (dentry == first_dentry)
337                         continue;
338
339                 newpath = unix_build_extraction_path(dentry, ctx);
340         retry_link:
341                 if (link(first_path, newpath)) {
342                         if (errno == EEXIST && !unlink(newpath))
343                                 goto retry_link;
344                         ERROR_WITH_ERRNO("Can't create hard link "
345                                          "\"%s\" => \"%s\"", newpath, first_path);
346                         return WIMLIB_ERR_LINK;
347                 }
348                 unix_reuse_pathbuf(ctx);
349         }
350         return 0;
351 }
352
353 /* If @dentry represents a directory, create it.  */
354 static int
355 unix_create_if_directory(const struct wim_dentry *dentry,
356                          struct unix_apply_ctx *ctx)
357 {
358         const char *path;
359         struct stat stbuf;
360
361         if (!dentry_is_directory(dentry))
362                 return 0;
363
364         path = unix_build_extraction_path(dentry, ctx);
365         if (mkdir(path, 0755) &&
366             /* It's okay if the path already exists, as long as it's a
367              * directory.  */
368             !(errno == EEXIST && !lstat(path, &stbuf) && S_ISDIR(stbuf.st_mode)))
369         {
370                 ERROR_WITH_ERRNO("Can't create directory \"%s\"", path);
371                 return WIMLIB_ERR_MKDIR;
372         }
373         return 0;
374 }
375
376 /* If @dentry represents an empty regular file, create it, set its metadata, and
377  * create any needed hard links.  */
378 static int
379 unix_extract_if_empty_file(const struct wim_dentry *dentry,
380                            struct unix_apply_ctx *ctx)
381 {
382         const struct wim_inode *inode;
383         const char *path;
384         int fd;
385         int ret;
386
387         inode = dentry->d_inode;
388
389         /* Extract all aliases only when the "first" comes up.  */
390         if (dentry != inode_first_extraction_dentry(inode))
391                 return 0;
392
393         /* Not an empty regular file?  */
394         if (inode_is_directory(inode) || inode_is_symlink(inode) ||
395             inode_unnamed_lte_resolved(inode))
396                 return 0;
397
398         path = unix_build_extraction_path(dentry, ctx);
399 retry_create:
400         fd = open(path, O_TRUNC | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
401         if (fd < 0) {
402                 if (errno == EEXIST && !unlink(path))
403                         goto retry_create;
404                 ERROR_WITH_ERRNO("Can't create regular file \"%s\"", path);
405                 return WIMLIB_ERR_OPEN;
406         }
407         /* On empty files, we can set timestamps immediately because we don't
408          * need to write any data to them.  */
409         ret = unix_set_metadata(fd, inode, path, ctx);
410         if (close(fd) && !ret) {
411                 ERROR_WITH_ERRNO("Error closing \"%s\"", path);
412                 ret = WIMLIB_ERR_WRITE;
413         }
414         if (ret)
415                 return ret;
416
417         return unix_create_hardlinks(inode, dentry, path, ctx);
418 }
419
420 static int
421 unix_create_dirs_and_empty_files(const struct list_head *dentry_list,
422                                  struct unix_apply_ctx *ctx)
423 {
424         const struct wim_dentry *dentry;
425         int ret;
426
427         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
428                 ret = unix_create_if_directory(dentry, ctx);
429                 if (ret)
430                         return ret;
431         }
432         list_for_each_entry(dentry, dentry_list, d_extraction_list_node) {
433                 ret = unix_extract_if_empty_file(dentry, ctx);
434                 if (ret)
435                         return ret;
436         }
437         return 0;
438 }
439
440 static int
441 unix_create_symlink(const struct wim_inode *inode, const char *path,
442                     const u8 *rpdata, u16 rpdatalen, bool rpfix,
443                     const char *apply_dir, size_t apply_dir_nchars)
444 {
445         char link_target[REPARSE_DATA_MAX_SIZE];
446         int ret;
447         struct wim_lookup_table_entry lte_override;
448
449         lte_override.resource_location = RESOURCE_IN_ATTACHED_BUFFER;
450         lte_override.attached_buffer = (void *)rpdata;
451         lte_override.size = rpdatalen;
452
453         ret = wim_inode_readlink(inode, link_target,
454                                  sizeof(link_target) - 1, &lte_override);
455         if (ret < 0) {
456                 errno = -ret;
457                 return WIMLIB_ERR_READLINK;
458         }
459
460         link_target[ret] = 0;
461
462         if (rpfix && link_target[0] == '/') {
463
464                 /* "Fix" the absolute symbolic link by prepending the absolute
465                  * path to the target directory.  */
466
467                 if (sizeof(link_target) - (ret + 1) < apply_dir_nchars) {
468                         errno = ENAMETOOLONG;
469                         return WIMLIB_ERR_REPARSE_POINT_FIXUP_FAILED;
470                 }
471                 memmove(link_target + apply_dir_nchars, link_target,
472                         ret + 1);
473                 memcpy(link_target, apply_dir, apply_dir_nchars);
474         }
475 retry_symlink:
476         if (symlink(link_target, path)) {
477                 if (errno == EEXIST && !unlink(path))
478                         goto retry_symlink;
479                 return WIMLIB_ERR_LINK;
480         }
481         return 0;
482 }
483
484 static void
485 unix_cleanup_open_fds(struct unix_apply_ctx *ctx, unsigned offset)
486 {
487         for (unsigned i = offset; i < ctx->num_open_fds; i++)
488                 filedes_close(&ctx->open_fds[i]);
489         ctx->num_open_fds = 0;
490 }
491
492 static int
493 unix_begin_extract_stream_instance(const struct wim_lookup_table_entry *stream,
494                                    const struct wim_inode *inode,
495                                    struct unix_apply_ctx *ctx)
496 {
497         const struct wim_dentry *first_dentry;
498         const char *first_path;
499         int fd;
500
501         if (inode_is_symlink(inode)) {
502                 /* On UNIX, symbolic links must be created with symlink(), which
503                  * requires that the full link target be available.  */
504                 if (stream->size > REPARSE_DATA_MAX_SIZE) {
505                         ERROR_WITH_ERRNO("Reparse data of \"%s\" has size "
506                                          "%"PRIu64" bytes (exceeds %u bytes)",
507                                          inode_first_full_path(inode),
508                                          stream->size, REPARSE_DATA_MAX_SIZE);
509                         return WIMLIB_ERR_INVALID_REPARSE_DATA;
510                 }
511                 ctx->reparse_ptr = ctx->reparse_data;
512                 return 0;
513         }
514
515         first_dentry = inode_first_extraction_dentry(inode);
516         first_path = unix_build_extraction_path(first_dentry, ctx);
517 retry_create:
518         fd = open(first_path, O_TRUNC | O_CREAT | O_WRONLY | O_NOFOLLOW, 0644);
519         if (fd < 0) {
520                 if (errno == EEXIST && !unlink(first_path))
521                         goto retry_create;
522                 ERROR_WITH_ERRNO("Can't create regular file \"%s\"", first_path);
523                 return WIMLIB_ERR_OPEN;
524         }
525         filedes_init(&ctx->open_fds[ctx->num_open_fds++], fd);
526         return unix_create_hardlinks(inode, first_dentry, first_path, ctx);
527 }
528
529 /* Called when starting to read a single-instance stream for extraction  */
530 static int
531 unix_begin_extract_stream(struct wim_lookup_table_entry *stream,
532                           u32 flags, void *_ctx)
533 {
534         struct unix_apply_ctx *ctx = _ctx;
535         const struct stream_owner *owners = stream_owners(stream);
536         int ret;
537
538         for (u32 i = 0; i < stream->out_refcnt; i++) {
539                 const struct wim_inode *inode = owners[i].inode;
540
541                 ret = unix_begin_extract_stream_instance(stream, inode, ctx);
542                 if (ret) {
543                         ctx->reparse_ptr = NULL;
544                         unix_cleanup_open_fds(ctx, 0);
545                         return ret;
546                 }
547         }
548         return 0;
549 }
550
551 /* Called when the next chunk of a single-instance stream has been read for
552  * extraction  */
553 static int
554 unix_extract_chunk(const void *chunk, size_t size, void *_ctx)
555 {
556         struct unix_apply_ctx *ctx = _ctx;
557         int ret;
558
559         for (unsigned i = 0; i < ctx->num_open_fds; i++) {
560                 ret = full_write(&ctx->open_fds[i], chunk, size);
561                 if (ret) {
562                         ERROR_WITH_ERRNO("Error writing data to filesystem");
563                         return ret;
564                 }
565         }
566         if (ctx->reparse_ptr)
567                 ctx->reparse_ptr = mempcpy(ctx->reparse_ptr, chunk, size);
568         return 0;
569 }
570
571 /* Called when a single-instance stream has been fully read for extraction  */
572 static int
573 unix_end_extract_stream(struct wim_lookup_table_entry *stream, int status,
574                         void *_ctx)
575 {
576         struct unix_apply_ctx *ctx = _ctx;
577         int ret;
578         unsigned j;
579         const struct stream_owner *owners = stream_owners(stream);
580
581         ctx->reparse_ptr = NULL;
582
583         if (status) {
584                 unix_cleanup_open_fds(ctx, 0);
585                 return status;
586         }
587
588         j = 0;
589         ret = 0;
590         for (u32 i = 0; i < stream->out_refcnt; i++) {
591                 struct wim_inode *inode = owners[i].inode;
592
593                 if (inode_is_symlink(inode)) {
594                         /* We finally have the symlink data, so we can create
595                          * the symlink.  */
596                         const char *path;
597
598                         path = unix_build_inode_extraction_path(inode, ctx);
599                         ret = unix_create_symlink(inode, path,
600                                                   ctx->reparse_data,
601                                                   stream->size,
602                                                   (ctx->common.extract_flags &
603                                                    WIMLIB_EXTRACT_FLAG_RPFIX),
604                                                   ctx->target_abspath,
605                                                   ctx->target_abspath_nchars);
606                         if (ret) {
607                                 ERROR_WITH_ERRNO("Can't create symbolic link "
608                                                  "\"%s\"", path);
609                                 break;
610                         }
611                         ret = unix_set_metadata(-1, inode, path, ctx);
612                         if (ret)
613                                 break;
614                 } else {
615                         /* Set metadata on regular file just before closing it.
616                          */
617                         struct filedes *fd = &ctx->open_fds[j];
618
619                         ret = unix_set_metadata(fd->fd, inode, NULL, ctx);
620                         if (ret)
621                                 break;
622
623                         if (filedes_close(fd)) {
624                                 ERROR_WITH_ERRNO("Error closing \"%s\"",
625                                                  unix_build_inode_extraction_path(inode, ctx));
626                                 ret = WIMLIB_ERR_WRITE;
627                                 break;
628                         }
629                         j++;
630                 }
631         }
632         unix_cleanup_open_fds(ctx, j);
633         return ret;
634 }
635
636 static int
637 unix_set_dir_metadata(struct list_head *dentry_list, struct unix_apply_ctx *ctx)
638 {
639         const struct wim_dentry *dentry;
640         int ret;
641
642         list_for_each_entry_reverse(dentry, dentry_list, d_extraction_list_node) {
643                 if (dentry_is_directory(dentry)) {
644                         ret = unix_set_metadata(-1, dentry->d_inode, NULL, ctx);
645                         if (ret)
646                                 return ret;
647                 }
648         }
649         return 0;
650 }
651
652 static int
653 unix_extract(struct list_head *dentry_list, struct apply_ctx *_ctx)
654 {
655         int ret;
656         struct unix_apply_ctx *ctx = (struct unix_apply_ctx *)_ctx;
657         size_t path_max;
658
659         /* Compute the maximum path length that will be needed, then allocate
660          * some path buffers.  */
661         path_max = unix_compute_path_max(dentry_list, ctx);
662
663         for (unsigned i = 0; i < NUM_PATHBUFS; i++) {
664                 ctx->pathbufs[i] = MALLOC(path_max);
665                 if (!ctx->pathbufs[i]) {
666                         ret = WIMLIB_ERR_NOMEM;
667                         goto out;
668                 }
669                 /* Pre-fill the target in each path buffer.  We'll just append
670                  * the rest of the paths after this.  */
671                 memcpy(ctx->pathbufs[i],
672                        ctx->common.target, ctx->common.target_nchars);
673         }
674
675         /* Extract directories and empty regular files.  Directories are needed
676          * because we can't extract any other files until their directories
677          * exist.  Empty files are needed because they don't have
678          * representatives in the stream list.  */
679         ret = unix_create_dirs_and_empty_files(dentry_list, ctx);
680         if (ret)
681                 goto out;
682
683         /* Get full path to target if needed for absolute symlink fixups.  */
684         if ((ctx->common.extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX) &&
685             ctx->common.required_features.symlink_reparse_points)
686         {
687                 ctx->target_abspath = realpath(ctx->common.target, NULL);
688                 if (!ctx->target_abspath) {
689                         ret = WIMLIB_ERR_NOMEM;
690                         goto out;
691                 }
692                 ctx->target_abspath_nchars = strlen(ctx->target_abspath);
693         }
694
695         /* Extract nonempty regular files and symbolic links.  */
696
697         struct read_stream_list_callbacks cbs = {
698                 .begin_stream      = unix_begin_extract_stream,
699                 .begin_stream_ctx  = ctx,
700                 .consume_chunk     = unix_extract_chunk,
701                 .consume_chunk_ctx = ctx,
702                 .end_stream        = unix_end_extract_stream,
703                 .end_stream_ctx    = ctx,
704         };
705         ret = extract_stream_list(&ctx->common, &cbs);
706         if (ret)
707                 goto out;
708
709         /* Set directory metadata.  We do this last so that we get the right
710          * directory timestamps.  */
711         ret = unix_set_dir_metadata(dentry_list, ctx);
712 out:
713         for (unsigned i = 0; i < NUM_PATHBUFS; i++)
714                 FREE(ctx->pathbufs[i]);
715         FREE(ctx->target_abspath);
716         return ret;
717 }
718
719 const struct apply_operations unix_apply_ops = {
720         .name                   = "UNIX",
721         .get_supported_features = unix_get_supported_features,
722         .extract                = unix_extract,
723         .context_size           = sizeof(struct unix_apply_ctx),
724 };