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