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