]> wimlib.net Git - wimlib/blob - src/unix_capture.c
5ae8209bf9ecab5b2ab31fd276195706df93fc11
[wimlib] / src / unix_capture.c
1 /*
2  * unix_capture.c:  Capture a directory tree on UNIX.
3  */
4
5 /*
6  * Copyright (C) 2012-2017 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21
22 #ifndef __WIN32__
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include <dirent.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_XATTR_H
34 #  include <sys/xattr.h>
35 #endif
36 #include <unistd.h>
37
38 #include "wimlib/blob_table.h"
39 #include "wimlib/dentry.h"
40 #include "wimlib/error.h"
41 #include "wimlib/reparse.h"
42 #include "wimlib/scan.h"
43 #include "wimlib/timestamp.h"
44 #include "wimlib/unix_data.h"
45 #include "wimlib/xattr.h"
46
47 #ifdef HAVE_FDOPENDIR
48 #  define my_fdopendir(dirfd_p) fdopendir(*(dirfd_p))
49 #else
50 static DIR *
51 my_fdopendir(int *dirfd_p)
52 {
53         DIR *dir = NULL;
54         int old_pwd;
55
56         old_pwd = open(".", O_RDONLY);
57         if (old_pwd >= 0) {
58                 if (!fchdir(*dirfd_p)) {
59                         dir = opendir(".");
60                         if (dir) {
61                                 close(*dirfd_p);
62                                 *dirfd_p = dirfd(dir);
63                         }
64                         fchdir(old_pwd);
65                 }
66                 close(old_pwd);
67         }
68         return dir;
69 }
70 #endif
71
72 #ifdef HAVE_OPENAT
73 #  define my_openat(full_path, dirfd, relpath, flags) \
74                 openat((dirfd), (relpath), (flags))
75 #else
76 #  define my_openat(full_path, dirfd, relpath, flags) \
77                 open((full_path), (flags))
78 #endif
79
80 #ifdef HAVE_READLINKAT
81 #  define my_readlinkat(full_path, dirfd, relpath, buf, bufsize) \
82                 readlinkat((dirfd), (relpath), (buf), (bufsize))
83 #else
84 #  define my_readlinkat(full_path, dirfd, relpath, buf, bufsize) \
85                 readlink((full_path), (buf), (bufsize))
86 #endif
87
88 #ifdef HAVE_FSTATAT
89 #  define my_fstatat(full_path, dirfd, relpath, stbuf, flags)   \
90         fstatat((dirfd), (relpath), (stbuf), (flags))
91 #else
92 #  define my_fstatat(full_path, dirfd, relpath, stbuf, flags)   \
93         ((flags) & AT_SYMLINK_NOFOLLOW) ? \
94                 lstat((full_path), (stbuf)) : \
95                 stat((full_path), (stbuf))
96 #endif
97
98 #ifndef AT_FDCWD
99 #  define AT_FDCWD      -100
100 #endif
101
102 #ifndef AT_SYMLINK_NOFOLLOW
103 #  define AT_SYMLINK_NOFOLLOW   0x100
104 #endif
105
106 #ifdef HAVE_XATTR_SUPPORT
107 /*
108  * Retrieves the values of the xattrs named by the null-terminated @names of the
109  * file at @path and serializes the xattr names and values into @entries.  If
110  * successful, returns the number of bytes used in @entries.  If unsuccessful,
111  * returns -1 and sets errno (ERANGE if @entries was too small).
112  */
113 static ssize_t
114 gather_xattr_entries(const char *path, const char *names, size_t names_size,
115                      void *entries, size_t entries_size)
116 {
117         const char * const names_end = names + names_size;
118         void * const entries_end = entries + entries_size;
119         const char *name = names;
120         struct wimlib_xattr_entry *entry = entries;
121
122         wimlib_assert((uintptr_t)entries % 4 == 0 &&
123                       entries_size % 4 == 0 && names_size != 0);
124         do {
125                 size_t name_len = strnlen(name, names_end - name);
126                 void *value;
127                 ssize_t value_len;
128
129                 if (name_len == 0 || name_len >= names_end - name ||
130                     (u16)name_len != name_len) {
131                         ERROR("\"%s\": malformed extended attribute names list",
132                               path);
133                         errno = EINVAL;
134                         return -1;
135                 }
136
137                 /*
138                  * Note: we take care to always call lgetxattr() with a nonzero
139                  * size, since zero size means to return the value length only.
140                  */
141                 if (entries_end - (void *)entry <= sizeof(*entry) + name_len) {
142                         errno = ERANGE;
143                         return -1;
144                 }
145
146                 entry->name_len = cpu_to_le16(name_len);
147                 entry->reserved = 0;
148                 value = mempcpy(entry->name, name, name_len);
149
150                 value_len = lgetxattr(path, name, value, entries_end - value);
151                 if (value_len < 0) {
152                         if (errno != ERANGE) {
153                                 ERROR_WITH_ERRNO("\"%s\": unable to read extended attribute \"%s\"",
154                                                  path, name);
155                         }
156                         return -1;
157                 }
158                 if ((u32)value_len != value_len) {
159                         ERROR("\"%s\": value of extended attribute \"%s\" is too large",
160                               path, name);
161                         errno = EINVAL;
162                         return -1;
163                 }
164                 entry->value_len = cpu_to_le32(value_len);
165
166                 /*
167                  * Zero-pad the entry to the next 4-byte boundary.
168                  * Note: because we've guaranteed that @entries_size is a
169                  * multiple of 4, this cannot overflow the @entries buffer.
170                  */
171                 value += value_len;
172                 while ((uintptr_t)value & 3) {
173                         *(u8 *)value = 0;
174                         value++;
175                 }
176
177                 entry = value;
178                 name += name_len + 1;
179         } while (name < names_end);
180
181         return (void *)entry - entries;
182 }
183
184 static int
185 create_xattr_item(const char *path, struct wim_inode *inode,
186                   const char *names, size_t names_size)
187 {
188         char _entries[1024] _aligned_attribute(4);
189         char *entries = _entries;
190         size_t entries_avail = ARRAY_LEN(_entries);
191         ssize_t entries_size;
192         int ret;
193
194 retry:
195         /* Serialize the xattrs into @entries */
196         entries_size = gather_xattr_entries(path, names, names_size,
197                                             entries, entries_avail);
198         if (entries_size < 0) {
199                 ret = WIMLIB_ERR_STAT;
200                 if (errno != ERANGE)
201                         goto out;
202                 /* Not enough space in @entries.  Reallocate it. */
203                 if (entries != _entries)
204                         FREE(entries);
205                 ret = WIMLIB_ERR_NOMEM;
206                 entries_avail *= 2;
207                 entries = MALLOC(entries_avail);
208                 if (!entries)
209                         goto out;
210                 goto retry;
211         }
212
213         /* Copy @entries into an xattr item associated with @inode */
214         if ((u32)entries_size != entries_size) {
215                 ERROR("\"%s\": too much xattr data!", path);
216                 ret = WIMLIB_ERR_STAT;
217                 goto out;
218         }
219         ret = WIMLIB_ERR_NOMEM;
220         if (!inode_set_linux_xattrs(inode, entries, entries_size))
221                 goto out;
222
223         ret = 0;
224 out:
225         if (entries != _entries)
226                 FREE(entries);
227         return ret;
228 }
229
230 /*
231  * If the file at @path has Linux-style extended attributes, read them into
232  * memory and add them to @inode as a tagged item.
233  */
234 static noinline_for_stack int
235 scan_linux_xattrs(const char *path, struct wim_inode *inode)
236 {
237         char _names[256];
238         char *names = _names;
239         ssize_t names_size = ARRAY_LEN(_names);
240         int ret = 0;
241
242 retry:
243         /* Gather the names of the xattrs of the file at @path */
244         names_size = llistxattr(path, names, names_size);
245         if (names_size == 0) /* No xattrs? */
246                 goto out;
247         if (names_size < 0) {
248                 /* xattrs unsupported or disabled? */
249                 if (errno == ENOTSUP || errno == ENOSYS)
250                         goto out;
251                 if (errno == ERANGE) {
252                         /*
253                          * Not enough space in @names.  Ask for how much space
254                          * we need, then try again.
255                          */
256                         names_size = llistxattr(path, NULL, 0);
257                         if (names_size == 0)
258                                 goto out;
259                         if (names_size > 0) {
260                                 if (names != _names)
261                                         FREE(names);
262                                 names = MALLOC(names_size);
263                                 if (!names) {
264                                         ret = WIMLIB_ERR_NOMEM;
265                                         goto out;
266                                 }
267                                 goto retry;
268                         }
269                 }
270                 /* Some other error occurred. */
271                 ERROR_WITH_ERRNO("\"%s\": unable to list extended attributes",
272                                  path);
273                 ret = WIMLIB_ERR_STAT;
274                 goto out;
275         }
276
277         /*
278          * We have a nonempty list of xattr names.  Gather the xattr values and
279          * add them as a tagged item.
280          */
281         ret = create_xattr_item(path, inode, names, names_size);
282 out:
283         if (names != _names)
284                 FREE(names);
285         return ret;
286 }
287 #endif /* HAVE_XATTR_SUPPORT */
288
289 static int
290 unix_scan_regular_file(const char *path, u64 blocks, u64 size,
291                        struct wim_inode *inode,
292                        struct list_head *unhashed_blobs)
293 {
294         struct blob_descriptor *blob = NULL;
295         struct wim_inode_stream *strm;
296
297         /*
298          * Set FILE_ATTRIBUTE_SPARSE_FILE if the file uses less disk space than
299          * expected given its size.
300          */
301         if (blocks < DIV_ROUND_UP(size, 512))
302                 inode->i_attributes = FILE_ATTRIBUTE_SPARSE_FILE;
303         else
304                 inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
305
306         if (size) {
307                 blob = new_blob_descriptor();
308                 if (unlikely(!blob))
309                         goto err_nomem;
310                 blob->file_on_disk = STRDUP(path);
311                 if (unlikely(!blob->file_on_disk))
312                         goto err_nomem;
313                 blob->blob_location = BLOB_IN_FILE_ON_DISK;
314                 blob->size = size;
315                 blob->file_inode = inode;
316         }
317
318         strm = inode_add_stream(inode, STREAM_TYPE_DATA, NO_STREAM_NAME, blob);
319         if (unlikely(!strm))
320                 goto err_nomem;
321
322         prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
323         return 0;
324
325 err_nomem:
326         free_blob_descriptor(blob);
327         return WIMLIB_ERR_NOMEM;
328 }
329
330 static int
331 unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret,
332                                  int dirfd, const char *relpath,
333                                  struct scan_params *params);
334
335 static int
336 unix_scan_directory(struct wim_dentry *dir_dentry,
337                     int parent_dirfd, const char *dir_relpath,
338                     struct scan_params *params)
339 {
340
341         int dirfd;
342         DIR *dir;
343         int ret;
344
345         dirfd = my_openat(params->cur_path, parent_dirfd, dir_relpath, O_RDONLY);
346         if (dirfd < 0) {
347                 ERROR_WITH_ERRNO("\"%s\": Can't open directory",
348                                  params->cur_path);
349                 return WIMLIB_ERR_OPENDIR;
350         }
351
352         dir_dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY;
353         dir = my_fdopendir(&dirfd);
354         if (!dir) {
355                 ERROR_WITH_ERRNO("\"%s\": Can't open directory",
356                                  params->cur_path);
357                 close(dirfd);
358                 return WIMLIB_ERR_OPENDIR;
359         }
360
361         ret = 0;
362         for (;;) {
363                 struct dirent *entry;
364                 struct wim_dentry *child;
365                 size_t name_len;
366                 size_t orig_path_len;
367
368                 errno = 0;
369                 entry = readdir(dir);
370                 if (!entry) {
371                         if (errno) {
372                                 ret = WIMLIB_ERR_READ;
373                                 ERROR_WITH_ERRNO("\"%s\": Error reading directory",
374                                                  params->cur_path);
375                         }
376                         break;
377                 }
378
379                 name_len = strlen(entry->d_name);
380
381                 if (should_ignore_filename(entry->d_name, name_len))
382                         continue;
383
384                 ret = WIMLIB_ERR_NOMEM;
385                 if (!pathbuf_append_name(params, entry->d_name, name_len,
386                                          &orig_path_len))
387                         break;
388                 ret = unix_build_dentry_tree_recursive(&child, dirfd,
389                                                        entry->d_name, params);
390                 pathbuf_truncate(params, orig_path_len);
391                 if (ret)
392                         break;
393                 attach_scanned_tree(dir_dentry, child, params->blob_table);
394         }
395         closedir(dir);
396         return ret;
397 }
398
399 /*
400  * Given an absolute symbolic link target (UNIX-style, beginning with '/'),
401  * determine whether it points into the directory identified by @ino and @dev.
402  * If yes, return the suffix of @target which is relative to this directory, but
403  * retaining leading slashes.  If no, return @target.
404  *
405  * Here are some examples, assuming that the @ino/@dev directory is "/home/e":
406  *
407  *      Original target         New target
408  *      ---------------         ----------
409  *      /home/e/test            /test
410  *      /home/e/test/           /test/
411  *      //home//e//test//       //test//
412  *      /home/e                                         (empty string)
413  *      /home/e/                /
414  *      /usr/lib                /usr/lib                (external link)
415  *
416  * Because of the possibility of other links into the @ino/@dev directory and/or
417  * multiple path separators, we can't simply do a string comparison; instead we
418  * need to stat() each ancestor directory.
419  *
420  * If the link points directly to the @ino/@dev directory with no trailing
421  * slashes, then the new target will be an empty string.  This is not a valid
422  * UNIX symlink target, but we store this in the archive anyway since the target
423  * is intended to be de-relativized when the link is extracted.
424  */
425 static char *
426 unix_relativize_link_target(char *target, u64 ino, u64 dev)
427 {
428         char *p = target;
429
430         do {
431                 char save;
432                 struct stat stbuf;
433                 int ret;
434
435                 /* Skip slashes (guaranteed to be at least one here)  */
436                 do {
437                         p++;
438                 } while (*p == '/');
439
440                 /* End of string?  */
441                 if (!*p)
442                         break;
443
444                 /* Skip non-slashes (guaranteed to be at least one here)  */
445                 do {
446                         p++;
447                 } while (*p && *p != '/');
448
449                 /* Get the inode and device numbers for this prefix.  */
450                 save = *p;
451                 *p = '\0';
452                 ret = stat(target, &stbuf);
453                 *p = save;
454
455                 if (ret) {
456                         /* stat() failed.  Assume the link points outside the
457                          * directory tree being captured.  */
458                         break;
459                 }
460
461                 if (stbuf.st_ino == ino && stbuf.st_dev == dev) {
462                         /* Link points inside directory tree being captured.
463                          * Return abbreviated path.  */
464                         return p;
465                 }
466         } while (*p);
467
468         /* Link does not point inside directory tree being captured.  */
469         return target;
470 }
471
472 static noinline_for_stack int
473 unix_scan_symlink(int dirfd, const char *relpath,
474                   struct wim_inode *inode, struct scan_params *params)
475 {
476         char orig_target[REPARSE_POINT_MAX_SIZE];
477         char *target = orig_target;
478         int ret;
479
480         /* Read the UNIX symbolic link target.  */
481         ret = my_readlinkat(params->cur_path, dirfd, relpath, target,
482                             sizeof(orig_target));
483         if (unlikely(ret < 0)) {
484                 ERROR_WITH_ERRNO("\"%s\": Can't read target of symbolic link",
485                                  params->cur_path);
486                 return WIMLIB_ERR_READLINK;
487         }
488         if (unlikely(ret >= sizeof(orig_target))) {
489                 ERROR("\"%s\": target of symbolic link is too long",
490                       params->cur_path);
491                 return WIMLIB_ERR_READLINK;
492         }
493         target[ret] = '\0';
494
495         /* If the link is absolute and reparse point fixups are enabled, then
496          * change it to be "absolute" relative to the tree being captured.  */
497         if (target[0] == '/' && (params->add_flags & WIMLIB_ADD_FLAG_RPFIX)) {
498                 int status = WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK;
499
500                 params->progress.scan.symlink_target = target;
501
502                 target = unix_relativize_link_target(target,
503                                                      params->capture_root_ino,
504                                                      params->capture_root_dev);
505                 if (target != orig_target) {
506                         /* Link target was fixed.  */
507                         inode->i_rp_flags &= ~WIM_RP_FLAG_NOT_FIXED;
508                         status = WIMLIB_SCAN_DENTRY_FIXED_SYMLINK;
509                 }
510                 ret = do_scan_progress(params, status, NULL);
511                 if (ret)
512                         return ret;
513         }
514
515         /* Translate the UNIX symlink target into a Windows reparse point.  */
516         ret = wim_inode_set_symlink(inode, target, params->blob_table);
517         if (unlikely(ret)) {
518                 if (ret == WIMLIB_ERR_INVALID_UTF8_STRING) {
519                         ERROR("\"%s\": target of symbolic link is not valid "
520                               "UTF-8.  This is not supported.",
521                               params->cur_path);
522                 }
523                 return ret;
524         }
525
526         /* On Windows, a reparse point can be set on both directory and
527          * non-directory files.  Usually, a link that is intended to point to a
528          * (non-)directory is stored as a reparse point on a (non-)directory
529          * file.  Replicate this behavior by examining the target file.  */
530         struct stat stbuf;
531         if (my_fstatat(params->cur_path, dirfd, relpath, &stbuf, 0) == 0 &&
532             S_ISDIR(stbuf.st_mode))
533                 inode->i_attributes |= FILE_ATTRIBUTE_DIRECTORY;
534         return 0;
535 }
536
537 static int
538 unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret,
539                                  int dirfd, const char *relpath,
540                                  struct scan_params *params)
541 {
542         struct wim_dentry *tree = NULL;
543         struct wim_inode *inode = NULL;
544         int ret;
545         struct stat stbuf;
546         int stat_flags;
547
548         ret = try_exclude(params);
549         if (unlikely(ret < 0)) /* Excluded? */
550                 goto out_progress;
551         if (unlikely(ret > 0)) /* Error? */
552                 goto out;
553
554         if (params->add_flags & (WIMLIB_ADD_FLAG_DEREFERENCE |
555                                  WIMLIB_ADD_FLAG_ROOT))
556                 stat_flags = 0;
557         else
558                 stat_flags = AT_SYMLINK_NOFOLLOW;
559
560         ret = my_fstatat(params->cur_path, dirfd, relpath, &stbuf, stat_flags);
561
562         if (ret) {
563                 ERROR_WITH_ERRNO("\"%s\": Can't read metadata",
564                                  params->cur_path);
565                 ret = WIMLIB_ERR_STAT;
566                 goto out;
567         }
568
569         if (!(params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA)) {
570                 if (unlikely(!S_ISREG(stbuf.st_mode) &&
571                              !S_ISDIR(stbuf.st_mode) &&
572                              !S_ISLNK(stbuf.st_mode)))
573                 {
574                         if (params->add_flags &
575                             WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE)
576                         {
577                                 ERROR("\"%s\": File type is unsupported",
578                                       params->cur_path);
579                                 ret = WIMLIB_ERR_UNSUPPORTED_FILE;
580                                 goto out;
581                         }
582                         ret = do_scan_progress(params,
583                                                WIMLIB_SCAN_DENTRY_UNSUPPORTED,
584                                                NULL);
585                         goto out;
586                 }
587         }
588
589         ret = inode_table_new_dentry(params->inode_table, relpath,
590                                      stbuf.st_ino, stbuf.st_dev, false, &tree);
591         if (unlikely(ret)) {
592                 if (ret == WIMLIB_ERR_INVALID_UTF8_STRING) {
593                         ERROR("\"%s\": filename is not valid UTF-8.  "
594                               "This is not supported.", params->cur_path);
595                 }
596                 goto out;
597         }
598
599         inode = tree->d_inode;
600
601         /* Already seen this inode?  */
602         if (inode->i_nlink > 1)
603                 goto out_progress;
604
605 #ifdef HAVE_STAT_NANOSECOND_PRECISION
606         inode->i_creation_time = timespec_to_wim_timestamp(&stbuf.st_mtim);
607         inode->i_last_write_time = timespec_to_wim_timestamp(&stbuf.st_mtim);
608         inode->i_last_access_time = timespec_to_wim_timestamp(&stbuf.st_atim);
609 #else
610         inode->i_creation_time = time_t_to_wim_timestamp(stbuf.st_mtime);
611         inode->i_last_write_time = time_t_to_wim_timestamp(stbuf.st_mtime);
612         inode->i_last_access_time = time_t_to_wim_timestamp(stbuf.st_atime);
613 #endif
614         if (params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) {
615                 struct wimlib_unix_data unix_data;
616
617                 unix_data.uid = stbuf.st_uid;
618                 unix_data.gid = stbuf.st_gid;
619                 unix_data.mode = stbuf.st_mode;
620                 unix_data.rdev = stbuf.st_rdev;
621                 if (!inode_set_unix_data(inode, &unix_data, UNIX_DATA_ALL)) {
622                         ret = WIMLIB_ERR_NOMEM;
623                         goto out;
624                 }
625 #ifdef HAVE_XATTR_SUPPORT
626                 ret = scan_linux_xattrs(params->cur_path, inode);
627                 if (ret)
628                         goto out;
629 #endif
630         }
631
632         if (params->add_flags & WIMLIB_ADD_FLAG_ROOT) {
633                 params->capture_root_ino = stbuf.st_ino;
634                 params->capture_root_dev = stbuf.st_dev;
635                 params->add_flags &= ~WIMLIB_ADD_FLAG_ROOT;
636         }
637
638         if (S_ISREG(stbuf.st_mode)) {
639                 ret = unix_scan_regular_file(params->cur_path, stbuf.st_blocks,
640                                              stbuf.st_size, inode,
641                                              params->unhashed_blobs);
642         } else if (S_ISDIR(stbuf.st_mode)) {
643                 ret = unix_scan_directory(tree, dirfd, relpath, params);
644         } else if (S_ISLNK(stbuf.st_mode)) {
645                 ret = unix_scan_symlink(dirfd, relpath, inode, params);
646         }
647
648         if (ret)
649                 goto out;
650
651 out_progress:
652         if (likely(tree))
653                 ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_OK, inode);
654         else
655                 ret = do_scan_progress(params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
656 out:
657         if (unlikely(ret)) {
658                 free_dentry_tree(tree, params->blob_table);
659                 tree = NULL;
660                 ret = report_scan_error(params, ret);
661         }
662         *tree_ret = tree;
663         return ret;
664 }
665
666 /*
667  * unix_build_dentry_tree():
668  *      Builds a tree of WIM dentries from an on-disk directory tree (UNIX
669  *      version; no NTFS-specific data is captured).
670  *
671  * @root_ret:   Place to return a pointer to the root of the dentry tree.  Set
672  *              to NULL if the file or directory was excluded from capture.
673  *
674  * @root_disk_path:  The path to the root of the directory tree on disk.
675  *
676  * @params:     See doc for `struct scan_params'.
677  *
678  * @return:     0 on success, nonzero on failure.  It is a failure if any of
679  *              the files cannot be `stat'ed, or if any of the needed
680  *              directories cannot be opened or read.  Failure to add the files
681  *              to the WIM may still occur later when trying to actually read
682  *              the on-disk files during a call to wimlib_write() or
683  *              wimlib_overwrite().
684  */
685 int
686 unix_build_dentry_tree(struct wim_dentry **root_ret,
687                        const char *root_disk_path, struct scan_params *params)
688 {
689         int ret;
690
691         ret = pathbuf_init(params, root_disk_path);
692         if (ret)
693                 return ret;
694
695         return unix_build_dentry_tree_recursive(root_ret, AT_FDCWD,
696                                                 root_disk_path, params);
697 }
698
699 #endif /* !__WIN32__ */